Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b156b2bb4 | |||
| 09fc79bd6f | |||
| 4dd176eaab | |||
| e3740f272b | |||
| 6fa9375c01 | |||
| 7524b613c5 | |||
| 73e00cf056 | |||
| 2e27d28fec | |||
| ca03bdaf6e | |||
| b8313cc998 | |||
| 4f57352937 | |||
| b46dfdb81c | |||
| e35395a45a | |||
| 089df50687 | |||
| 010a016ac9 | |||
| 43c1e622cd | |||
| 54640de156 | |||
| 969012e534 | |||
| 226744d11b | |||
| eabb67fb61 | |||
| 7d90417733 | |||
| 1451778a88 | |||
| a5aed4b10b | |||
| d83e082524 | |||
| 0ff2217035 | |||
| f12228fa23 | |||
| f5f0e43a6d | |||
| a64d1fbc23 | |||
| f48d4ef8bc | |||
| 0c2e5cfda5 | |||
| 5859636fb1 | |||
| a485ad191f | |||
| 63331b1af3 | |||
| 754ee285fa | |||
| 59533aa5e0 | |||
| 60172a5a93 | |||
| 85ef630fde | |||
| 57498ae9cf | |||
| 13aa88d956 | |||
| 6a8700b97b | |||
| a02860511d | |||
| 59c435a015 | |||
| 0019881e08 | |||
| 6903bf6d49 | |||
| 86b40dfd33 | |||
| b7673ede37 | |||
| e9e1074d27 | |||
| 334d163e41 | |||
| 5b54259873 | |||
| ab2eca0965 | |||
| 93bbfbe539 | |||
| c644980c4d | |||
| ea2844f153 | |||
| df94966c8b | |||
| 2fc2f92ed6 | |||
| 9e61104c68 | |||
| 344f763f72 | |||
| 2912da0283 | |||
| a66632d83a | |||
| 77b4f528fa | |||
| cf201488a5 | |||
| 33447e7793 | |||
| 4da30f8888 | |||
| 2df9478f5f | |||
| d76dba7eb5 | |||
| 9d70954094 | |||
| 172861be76 | |||
| d080e3d7ac | |||
| 35270d2604 | |||
| 94e32acb56 | |||
| d67a97705c | |||
| efe1697191 | |||
| 466004d5a4 |
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.20.1.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.21.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.20.1'
|
||||
PACKAGE_STRING='sqlite 3.20.1'
|
||||
PACKAGE_VERSION='3.21.0'
|
||||
PACKAGE_STRING='sqlite 3.21.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1463,7 +1463,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.20.1 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.21.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1528,7 +1528,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.20.1:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.21.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1652,7 +1652,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.20.1
|
||||
sqlite configure 3.21.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2071,7 +2071,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.20.1, which was
|
||||
It was created by sqlite $as_me 3.21.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -12151,7 +12151,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.20.1, which was
|
||||
This file was extended by sqlite $as_me 3.21.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12217,7 +12217,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.20.1
|
||||
sqlite config.status 3.21.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+493
-246
@@ -12,6 +12,42 @@
|
||||
**
|
||||
** This file implements a simple virtual table wrapper around the LSM
|
||||
** storage engine from SQLite4.
|
||||
**
|
||||
** USAGE
|
||||
**
|
||||
** CREATE VIRTUAL TABLE demo USING lsm1(filename,key,keytype,value1,...);
|
||||
**
|
||||
** The keytype must be one of: UINT, TEXT, BLOB. All keys must be of that
|
||||
** one type. "UINT" means unsigned integer. The values may be any
|
||||
** SQLite datatype.
|
||||
**
|
||||
** INTERNALS
|
||||
**
|
||||
** The key encoding for BLOB and TEXT is just a copy of the blob or text.
|
||||
** UTF-8 is used for text. The key encoding for UINT is the variable-length
|
||||
** integer format at https://sqlite.org/src4/doc/trunk/www/varint.wiki.
|
||||
**
|
||||
** The values are encoded as a single blob (since that is what lsm stores as
|
||||
** its content). There is a "type integer" followed by "content" for each
|
||||
** value, alternating back and forth. The content might be empty.
|
||||
**
|
||||
** TYPE1 CONTENT1 TYPE2 CONTENT2 TYPE3 CONTENT3 ....
|
||||
**
|
||||
** Each "type integer" is encoded as a variable-length integer in the
|
||||
** format of the link above. Let the type integer be T. The actual
|
||||
** datatype is an integer 0-5 equal to T%6. Values 1 through 5 correspond
|
||||
** to SQLITE_INTEGER through SQLITE_NULL. The size of the content in bytes
|
||||
** is T/6. Type value 0 means that the value is an integer whose actual
|
||||
** values is T/6 and there is no content. The type-value-0 integer format
|
||||
** only works for integers in the range of 0 through 40.
|
||||
**
|
||||
** There is no content for NULL or type-0 integers. For BLOB and TEXT
|
||||
** values, the content is the blob data or the UTF-8 text data. For
|
||||
** non-negative integers X, the content is a variable-length integer X*2.
|
||||
** For negative integers Y, the content is varaible-length integer (1-Y)*2+1.
|
||||
** For FLOAT values, the content is the IEEE754 floating point value in
|
||||
** native byte-order. This means that FLOAT values will be corrupted when
|
||||
** database file is moved between big-endian and little-endian machines.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
@@ -22,14 +58,19 @@ SQLITE_EXTENSION_INIT1
|
||||
/* Forward declaration of subclasses of virtual table objects */
|
||||
typedef struct lsm1_vtab lsm1_vtab;
|
||||
typedef struct lsm1_cursor lsm1_cursor;
|
||||
typedef struct lsm1_vblob lsm1_vblob;
|
||||
|
||||
/* Primitive types */
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
typedef sqlite3_uint64 u64;
|
||||
|
||||
/* An open connection to an LSM table */
|
||||
struct lsm1_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
lsm_db *pDb; /* Open connection to the LSM table */
|
||||
u8 keyType; /* SQLITE_BLOB, _TEXT, or _INTEGER */
|
||||
u32 nVal; /* Number of value columns */
|
||||
};
|
||||
|
||||
|
||||
@@ -43,8 +84,82 @@ struct lsm1_cursor {
|
||||
u8 isDesc; /* 0: scan forward. 1: scan reverse */
|
||||
u8 atEof; /* True if the scan is complete */
|
||||
u8 bUnique; /* True if no more than one row of output */
|
||||
u8 *zData; /* Content of the current row */
|
||||
u32 nData; /* Number of bytes in the current row */
|
||||
u8 *aeType; /* Types for all column values */
|
||||
u32 *aiOfst; /* Offsets to the various fields */
|
||||
u32 *aiLen; /* Length of each field */
|
||||
u8 *pKey2; /* Loop termination key, or NULL */
|
||||
u32 nKey2; /* Length of the loop termination key */
|
||||
};
|
||||
|
||||
/* An extensible buffer object.
|
||||
**
|
||||
** Content can be appended. Space to hold new content is automatically
|
||||
** allocated.
|
||||
*/
|
||||
struct lsm1_vblob {
|
||||
u8 *a; /* Space to hold content, from sqlite3_malloc64() */
|
||||
u64 n; /* Bytes of space used */
|
||||
u64 nAlloc; /* Bytes of space allocated */
|
||||
u8 errNoMem; /* True if a memory allocation error has been seen */
|
||||
};
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define LSM1_NOINLINE __attribute__((noinline))
|
||||
#elif defined(_MSC_VER) && _MSC_VER>=1310
|
||||
# define LSM1_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
# define LSM1_NOINLINE
|
||||
#endif
|
||||
|
||||
|
||||
/* Increase the available space in the vblob object so that it can hold
|
||||
** at least N more bytes. Return the number of errors.
|
||||
*/
|
||||
static int lsm1VblobEnlarge(lsm1_vblob *p, u32 N){
|
||||
if( p->n+N>p->nAlloc ){
|
||||
if( p->errNoMem ) return 1;
|
||||
p->nAlloc += N + (p->nAlloc ? p->nAlloc : N);
|
||||
p->a = sqlite3_realloc64(p->a, p->nAlloc);
|
||||
if( p->a==0 ){
|
||||
p->n = 0;
|
||||
p->nAlloc = 0;
|
||||
p->errNoMem = 1;
|
||||
return 1;
|
||||
}
|
||||
p->nAlloc = sqlite3_msize(p->a);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Append N bytes to a vblob after first enlarging it */
|
||||
static LSM1_NOINLINE void lsm1VblobEnlargeAndAppend(
|
||||
lsm1_vblob *p,
|
||||
const u8 *pData,
|
||||
u32 N
|
||||
){
|
||||
if( p->n+N>p->nAlloc && lsm1VblobEnlarge(p, N) ) return;
|
||||
memcpy(p->a+p->n, pData, N);
|
||||
p->n += N;
|
||||
}
|
||||
|
||||
/* Append N bytes to a vblob */
|
||||
static void lsm1VblobAppend(lsm1_vblob *p, const u8 *pData, u32 N){
|
||||
sqlite3_int64 n = p->n;
|
||||
if( n+N>p->nAlloc ){
|
||||
lsm1VblobEnlargeAndAppend(p, pData, N);
|
||||
}else{
|
||||
p->n += N;
|
||||
memcpy(p->a+n, pData, N);
|
||||
}
|
||||
}
|
||||
|
||||
/* append text to a vblob */
|
||||
static void lsm1VblobAppendText(lsm1_vblob *p, const char *z){
|
||||
lsm1VblobAppend(p, (u8*)z, (u32)strlen(z));
|
||||
}
|
||||
|
||||
/* Dequote the string */
|
||||
static void lsm1Dequote(char *z){
|
||||
int j;
|
||||
@@ -76,9 +191,28 @@ static int lsm1Connect(
|
||||
lsm1_vtab *pNew;
|
||||
int rc;
|
||||
char *zFilename;
|
||||
u8 keyType = 0;
|
||||
int i;
|
||||
lsm1_vblob sql;
|
||||
static const char *azTypes[] = { "UINT", "TEXT", "BLOB" };
|
||||
static const u8 aeTypes[] = { SQLITE_INTEGER, SQLITE_TEXT, SQLITE_BLOB };
|
||||
static const char *azArgName[] = {"filename", "key", "key type", "value1" };
|
||||
|
||||
if( argc!=4 || argv[3]==0 || argv[3][0]==0 ){
|
||||
*pzErr = sqlite3_mprintf("filename argument missing");
|
||||
for(i=0; i<sizeof(azArgName)/sizeof(azArgName[0]); i++){
|
||||
if( argc<i+4 || argv[i+3]==0 || argv[i+3][0]==0 ){
|
||||
*pzErr = sqlite3_mprintf("%s (%r) argument missing",
|
||||
azArgName[i], i+1);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
for(i=0; i<sizeof(azTypes)/sizeof(azTypes[0]); i++){
|
||||
if( sqlite3_stricmp(azTypes[i],argv[5])==0 ){
|
||||
keyType = aeTypes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( keyType==0 ){
|
||||
*pzErr = sqlite3_mprintf("key type should be INT, TEXT, or BLOB");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
*ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
@@ -87,6 +221,7 @@ static int lsm1Connect(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->keyType = keyType;
|
||||
rc = lsm_new(0, &pNew->pDb);
|
||||
if( rc ){
|
||||
*pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc);
|
||||
@@ -103,22 +238,26 @@ static int lsm1Connect(
|
||||
goto connect_failed;
|
||||
}
|
||||
|
||||
/* Column numbers */
|
||||
#define LSM1_COLUMN_KEY 0
|
||||
#define LSM1_COLUMN_BLOBKEY 1
|
||||
#define LSM1_COLUMN_VALUE 2
|
||||
#define LSM1_COLUMN_BLOBVALUE 3
|
||||
#define LSM1_COLUMN_COMMAND 4
|
||||
memset(&sql, 0, sizeof(sql));
|
||||
lsm1VblobAppendText(&sql, "CREATE TABLE x(");
|
||||
lsm1VblobAppendText(&sql, argv[4]);
|
||||
lsm1VblobAppendText(&sql, " ");
|
||||
lsm1VblobAppendText(&sql, argv[5]);
|
||||
for(i=6; i<argc; i++){
|
||||
lsm1VblobAppendText(&sql, ", ");
|
||||
lsm1VblobAppendText(&sql, argv[i]);
|
||||
pNew->nVal++;
|
||||
}
|
||||
lsm1VblobAppendText(&sql,
|
||||
", lsm1_command HIDDEN, lsm1_key HIDDEN, lsm1_value HIDDEN)");
|
||||
lsm1VblobAppend(&sql, (u8*)"", 1);
|
||||
if( sql.errNoMem ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto connect_failed;
|
||||
}
|
||||
rc = sqlite3_declare_vtab(db, (const char*)sql.a);
|
||||
sqlite3_free(sql.a);
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x("
|
||||
" key," /* The primary key. Any non-NULL */
|
||||
" blobkey," /* Pure BLOB primary key */
|
||||
" value," /* The value associated with key. Any non-NULL */
|
||||
" blobvalue," /* Pure BLOB value */
|
||||
" command hidden" /* Insert here for control operations */
|
||||
");"
|
||||
);
|
||||
connect_failed:
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( pNew ){
|
||||
@@ -147,9 +286,13 @@ static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
|
||||
lsm1_vtab *p = (lsm1_vtab*)pVtab;
|
||||
lsm1_cursor *pCur;
|
||||
int rc;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur)
|
||||
+ p->nVal*(sizeof(pCur->aiOfst)+sizeof(pCur->aiLen)+1) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->aiOfst = (u32*)&pCur[1];
|
||||
pCur->aiLen = &pCur->aiOfst[p->nVal];
|
||||
pCur->aeType = (u8*)&pCur->aiLen[p->nVal];
|
||||
*ppCursor = &pCur->base;
|
||||
rc = lsm_csr_open(p->pDb, &pCur->pLsmCur);
|
||||
if( rc==LSM_OK ){
|
||||
@@ -167,6 +310,7 @@ static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
|
||||
*/
|
||||
static int lsm1Close(sqlite3_vtab_cursor *cur){
|
||||
lsm1_cursor *pCur = (lsm1_cursor*)cur;
|
||||
sqlite3_free(pCur->pKey2);
|
||||
lsm_csr_close(pCur->pLsmCur);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
@@ -190,6 +334,21 @@ static int lsm1Next(sqlite3_vtab_cursor *cur){
|
||||
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)==0 ){
|
||||
pCur->atEof = 1;
|
||||
}
|
||||
if( pCur->pKey2 && pCur->atEof==0 ){
|
||||
const u8 *pVal;
|
||||
u32 nVal;
|
||||
assert( pCur->isDesc==0 );
|
||||
rc = lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, (int*)&nVal);
|
||||
if( rc==LSM_OK ){
|
||||
u32 len = pCur->nKey2;
|
||||
int c;
|
||||
if( len>nVal ) len = nVal;
|
||||
c = memcmp(pVal, pCur->pKey2, len);
|
||||
if( c==0 ) c = nVal - pCur->nKey2;
|
||||
if( c>0 ) pCur->atEof = 1;
|
||||
}
|
||||
}
|
||||
pCur->zData = 0;
|
||||
}
|
||||
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
|
||||
}
|
||||
@@ -295,6 +454,14 @@ static int lsm1PutVarint64(unsigned char *z, sqlite3_uint64 x){
|
||||
return 9;
|
||||
}
|
||||
|
||||
/* Append non-negative integer x as a variable-length integer.
|
||||
*/
|
||||
static void lsm1VblobAppendVarint(lsm1_vblob *p, sqlite3_uint64 x){
|
||||
sqlite3_int64 n = p->n;
|
||||
if( n+9>p->nAlloc && lsm1VblobEnlarge(p, 9) ) return;
|
||||
p->n += lsm1PutVarint64(p->a+p->n, x);
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode the varint in the first n bytes z[]. Write the integer value
|
||||
** into *pResult and return the number of bytes in the varint.
|
||||
@@ -349,69 +516,74 @@ static int lsm1GetVarint64(
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate a key encoding for pValue such that all keys compare in
|
||||
** lexicographical order. Return an SQLite error code or SQLITE_OK.
|
||||
/* Encoded a signed integer as a varint. Numbers close to zero uses fewer
|
||||
** bytes than numbers far away from zero. However, the result is not in
|
||||
** lexicographical order.
|
||||
**
|
||||
** The key encoding is *pnKey bytes in length written into *ppKey.
|
||||
** Space to hold the key is taken from pSpace if sufficient, or else
|
||||
** from sqlite3_malloc(). The caller is responsible for freeing malloced
|
||||
** space.
|
||||
** Encoding: Non-negative integer X is encoding as an unsigned
|
||||
** varint X*2. Negative integer Y is encoding as an unsigned
|
||||
** varint (1-Y)*2 + 1.
|
||||
*/
|
||||
static int lsm1EncodeKey(
|
||||
sqlite3_value *pValue, /* Value to be encoded */
|
||||
unsigned char **ppKey, /* Write the encoding here */
|
||||
int *pnKey, /* Write the size of the encoding here */
|
||||
unsigned char *pSpace, /* Use this space if it is large enough */
|
||||
int nSpace /* Size of pSpace[] */
|
||||
){
|
||||
int eType = sqlite3_value_type(pValue);
|
||||
*ppKey = 0;
|
||||
*pnKey = 0;
|
||||
assert( nSpace>=32 );
|
||||
switch( eType ){
|
||||
default: {
|
||||
return SQLITE_ERROR; /* We cannot handle NULL keys */
|
||||
}
|
||||
case SQLITE_BLOB:
|
||||
case SQLITE_TEXT: {
|
||||
int nVal = sqlite3_value_bytes(pValue);
|
||||
const void *pVal;
|
||||
if( eType==SQLITE_BLOB ){
|
||||
eType = LSM1_TYPE_BLOB;
|
||||
pVal = sqlite3_value_blob(pValue);
|
||||
}else{
|
||||
eType = LSM1_TYPE_TEXT;
|
||||
pVal = (const void*)sqlite3_value_text(pValue);
|
||||
if( pVal==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
if( nVal+1>nSpace ){
|
||||
pSpace = sqlite3_malloc( nVal+1 );
|
||||
if( pSpace==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
pSpace[0] = (unsigned char)eType;
|
||||
memcpy(&pSpace[1], pVal, nVal);
|
||||
*ppKey = pSpace;
|
||||
*pnKey = nVal+1;
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_int64 iVal = sqlite3_value_int64(pValue);
|
||||
sqlite3_uint64 uVal;
|
||||
if( iVal<0 ){
|
||||
if( iVal==0xffffffffffffffffLL ) return SQLITE_ERROR;
|
||||
uVal = *(sqlite3_uint64*)&iVal;
|
||||
eType = LSM1_TYPE_NEGATIVE;
|
||||
}else{
|
||||
uVal = iVal;
|
||||
eType = LSM1_TYPE_POSITIVE;
|
||||
}
|
||||
pSpace[0] = (unsigned char)eType;
|
||||
*ppKey = pSpace;
|
||||
*pnKey = 1 + lsm1PutVarint64(&pSpace[1], uVal);
|
||||
}
|
||||
static int lsm1PutSignedVarint64(u8 *z, sqlite3_int64 v){
|
||||
sqlite3_uint64 u;
|
||||
if( v>=0 ){
|
||||
u = (sqlite3_uint64)v;
|
||||
return lsm1PutVarint64(z, u*2);
|
||||
}else{
|
||||
u = (sqlite3_uint64)(-1-v);
|
||||
return lsm1PutVarint64(z, u*2+1);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Decoded a signed varint. */
|
||||
static int lsm1GetSignedVarint64(
|
||||
const unsigned char *z,
|
||||
int n,
|
||||
sqlite3_int64 *pResult
|
||||
){
|
||||
sqlite3_uint64 u = 0;
|
||||
n = lsm1GetVarint64(z, n, &u);
|
||||
if( u&1 ){
|
||||
*pResult = -1 - (sqlite3_int64)(u>>1);
|
||||
}else{
|
||||
*pResult = (sqlite3_int64)(u>>1);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read the value part of the key-value pair and decode it into columns.
|
||||
*/
|
||||
static int lsm1DecodeValues(lsm1_cursor *pCur){
|
||||
lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab);
|
||||
int i, n;
|
||||
int rc;
|
||||
u8 eType;
|
||||
sqlite3_uint64 v;
|
||||
|
||||
if( pCur->zData ) return 1;
|
||||
rc = lsm_csr_value(pCur->pLsmCur, (const void**)&pCur->zData,
|
||||
(int*)&pCur->nData);
|
||||
if( rc ) return 0;
|
||||
for(i=n=0; i<pTab->nVal; i++){
|
||||
v = 0;
|
||||
n += lsm1GetVarint64(pCur->zData+n, pCur->nData-n, &v);
|
||||
pCur->aeType[i] = eType = (u8)(v%6);
|
||||
if( eType==0 ){
|
||||
pCur->aiOfst[i] = (u32)(v/6);
|
||||
pCur->aiLen[i] = 0;
|
||||
}else{
|
||||
pCur->aiOfst[i] = n;
|
||||
n += (pCur->aiLen[i] = (u32)(v/6));
|
||||
}
|
||||
if( n>pCur->nData ) break;
|
||||
}
|
||||
if( i<pTab->nVal ){
|
||||
pCur->zData = 0;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -424,83 +596,74 @@ static int lsm1Column(
|
||||
int i /* Which column to return */
|
||||
){
|
||||
lsm1_cursor *pCur = (lsm1_cursor*)cur;
|
||||
switch( i ){
|
||||
case LSM1_COLUMN_BLOBKEY: {
|
||||
lsm1_vtab *pTab = (lsm1_vtab*)(cur->pVtab);
|
||||
if( i==0 ){
|
||||
/* The key column */
|
||||
const void *pVal;
|
||||
int nVal;
|
||||
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
|
||||
if( pTab->keyType==SQLITE_BLOB ){
|
||||
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
|
||||
}else if( pTab->keyType==SQLITE_TEXT ){
|
||||
sqlite3_result_text(ctx,(const char*)pVal, nVal, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
const unsigned char *z = (const unsigned char*)pVal;
|
||||
sqlite3_uint64 v1;
|
||||
lsm1GetVarint64(z, nVal, &v1);
|
||||
sqlite3_result_int64(ctx, (sqlite3_int64)v1);
|
||||
}
|
||||
}
|
||||
}else if( i>pTab->nVal ){
|
||||
if( i==pTab->nVal+2 ){ /* lsm1_key */
|
||||
const void *pVal;
|
||||
int nVal;
|
||||
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
|
||||
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LSM1_COLUMN_KEY: {
|
||||
const unsigned char *pVal;
|
||||
int nVal;
|
||||
if( lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK
|
||||
&& nVal>=1
|
||||
){
|
||||
if( pVal[0]==LSM1_TYPE_BLOB ){
|
||||
sqlite3_result_blob(ctx, (const void*)&pVal[1],nVal-1,
|
||||
SQLITE_TRANSIENT);
|
||||
}else if( pVal[0]==LSM1_TYPE_TEXT ){
|
||||
sqlite3_result_text(ctx, (const char*)&pVal[1],nVal-1,
|
||||
SQLITE_TRANSIENT);
|
||||
}else if( nVal>=2 && nVal<=10 &&
|
||||
(pVal[0]==LSM1_TYPE_POSITIVE || pVal[0]==LSM1_TYPE_NEGATIVE)
|
||||
){
|
||||
sqlite3_int64 iVal;
|
||||
lsm1GetVarint64(pVal+1, nVal-1, (sqlite3_uint64*)&iVal);
|
||||
sqlite3_result_int64(ctx, iVal);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LSM1_COLUMN_BLOBVALUE: {
|
||||
}else if( i==pTab->nVal+3 ){ /* lsm1_value */
|
||||
const void *pVal;
|
||||
int nVal;
|
||||
if( lsm_csr_value(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK ){
|
||||
if( lsm_csr_value(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
|
||||
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LSM1_COLUMN_VALUE: {
|
||||
const unsigned char *aVal;
|
||||
int nVal;
|
||||
if( lsm_csr_value(pCur->pLsmCur, (const void**)&aVal, &nVal)==LSM_OK
|
||||
&& nVal>=1
|
||||
){
|
||||
switch( aVal[0] ){
|
||||
case SQLITE_FLOAT:
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_uint64 x = 0;
|
||||
int j;
|
||||
for(j=1; j<nVal; j++){
|
||||
x = (x<<8) | aVal[j];
|
||||
}
|
||||
if( aVal[0]==SQLITE_INTEGER ){
|
||||
sqlite3_result_int64(ctx, *(sqlite3_int64*)&x);
|
||||
}else{
|
||||
double r;
|
||||
assert( sizeof(r)==sizeof(x) );
|
||||
memcpy(&r, &x, sizeof(r));
|
||||
sqlite3_result_double(ctx, r);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
sqlite3_result_text(ctx, (char*)&aVal[1], nVal-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
sqlite3_result_blob(ctx, &aVal[1], nVal-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if( lsm1DecodeValues(pCur) ){
|
||||
/* The i-th value column (where leftmost is 1) */
|
||||
const u8 *zData;
|
||||
u32 nData;
|
||||
i--;
|
||||
zData = pCur->zData + pCur->aiOfst[i];
|
||||
nData = pCur->aiLen[i];
|
||||
switch( pCur->aeType[i] ){
|
||||
case 0: { /* in-line integer */
|
||||
sqlite3_result_int(ctx, pCur->aiOfst[i]);
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_int64 v;
|
||||
lsm1GetSignedVarint64(zData, nData, &v);
|
||||
sqlite3_result_int64(ctx, v);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
double v;
|
||||
if( nData==sizeof(v) ){
|
||||
memcpy(&v, zData, sizeof(v));
|
||||
sqlite3_result_double(ctx, v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* A NULL. Do nothing */
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@@ -514,21 +677,85 @@ static int lsm1Filter(
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor;
|
||||
lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab);
|
||||
int rc = LSM_OK;
|
||||
int seekType = -1;
|
||||
const void *pVal = 0;
|
||||
int nVal;
|
||||
u8 keyType = pTab->keyType;
|
||||
u8 aKey1[16];
|
||||
|
||||
pCur->atEof = 1;
|
||||
if( idxNum==1 ){
|
||||
assert( argc==1 );
|
||||
pCur->isDesc = 0;
|
||||
pCur->bUnique = 1;
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
||||
const void *pVal = sqlite3_value_blob(argv[0]);
|
||||
int nVal = sqlite3_value_bytes(argv[0]);
|
||||
rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, LSM_SEEK_EQ);
|
||||
sqlite3_free(pCur->pKey2);
|
||||
pCur->pKey2 = 0;
|
||||
if( idxNum<99 ){
|
||||
if( keyType==SQLITE_BLOB ){
|
||||
pVal = sqlite3_value_blob(argv[0]);
|
||||
nVal = sqlite3_value_bytes(argv[0]);
|
||||
}else if( keyType==SQLITE_TEXT ){
|
||||
pVal = sqlite3_value_text(argv[0]);
|
||||
nVal = sqlite3_value_bytes(argv[0]);
|
||||
}else{
|
||||
sqlite3_int64 v = sqlite3_value_int64(argv[0]);
|
||||
if( v<0 ) v = 0;
|
||||
nVal = lsm1PutVarint64(aKey1, v);
|
||||
pVal = aKey1;
|
||||
}
|
||||
}
|
||||
switch( idxNum ){
|
||||
case 0: { /* key==argv[0] */
|
||||
assert( argc==1 );
|
||||
seekType = LSM_SEEK_EQ;
|
||||
pCur->isDesc = 0;
|
||||
pCur->bUnique = 1;
|
||||
break;
|
||||
}
|
||||
case 1: { /* key>=argv[0] AND key<=argv[1] */
|
||||
u8 aKey[12];
|
||||
seekType = LSM_SEEK_GE;
|
||||
pCur->isDesc = 0;
|
||||
pCur->bUnique = 0;
|
||||
if( keyType==SQLITE_INTEGER ){
|
||||
sqlite3_int64 v = sqlite3_value_int64(argv[1]);
|
||||
if( v<0 ) v = 0;
|
||||
pCur->nKey2 = lsm1PutVarint64(aKey, (sqlite3_uint64)v);
|
||||
pCur->pKey2 = sqlite3_malloc( pCur->nKey2 );
|
||||
if( pCur->pKey2==0 ) return SQLITE_NOMEM;
|
||||
memcpy(pCur->pKey2, aKey, pCur->nKey2);
|
||||
}else{
|
||||
pCur->nKey2 = sqlite3_value_bytes(argv[1]);
|
||||
pCur->pKey2 = sqlite3_malloc( pCur->nKey2 );
|
||||
if( pCur->pKey2==0 ) return SQLITE_NOMEM;
|
||||
if( keyType==SQLITE_BLOB ){
|
||||
memcpy(pCur->pKey2, sqlite3_value_blob(argv[1]), pCur->nKey2);
|
||||
}else{
|
||||
memcpy(pCur->pKey2, sqlite3_value_text(argv[1]), pCur->nKey2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: { /* key>=argv[0] */
|
||||
seekType = LSM_SEEK_GE;
|
||||
pCur->isDesc = 0;
|
||||
pCur->bUnique = 0;
|
||||
break;
|
||||
}
|
||||
case 3: { /* key<=argv[0] */
|
||||
seekType = LSM_SEEK_LE;
|
||||
pCur->isDesc = 1;
|
||||
pCur->bUnique = 0;
|
||||
break;
|
||||
}
|
||||
default: { /* full table scan */
|
||||
pCur->isDesc = 0;
|
||||
pCur->bUnique = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pVal ){
|
||||
rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, seekType);
|
||||
}else{
|
||||
rc = lsm_csr_first(pCur->pLsmCur);
|
||||
pCur->isDesc = 0;
|
||||
pCur->bUnique = 0;
|
||||
}
|
||||
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){
|
||||
pCur->atEof = 0;
|
||||
@@ -540,59 +767,87 @@ static int lsm1Filter(
|
||||
** Only comparisons against the key are allowed. The idxNum defines
|
||||
** which comparisons are available:
|
||||
**
|
||||
** 0 Full table scan only
|
||||
** bit 1 key==?1 single argument for ?1
|
||||
** bit 2 key>?1
|
||||
** bit 3 key>=?1
|
||||
** bit 4 key<?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set)
|
||||
** bit 5 key<=?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set)
|
||||
** bit 6 Use blobkey instead of key
|
||||
**
|
||||
** To put it another way:
|
||||
**
|
||||
** 0 Full table scan.
|
||||
** 1 key==?1
|
||||
** 2 key>?1
|
||||
** 4 key>=?1
|
||||
** 8 key<?1
|
||||
** 10 key>?1 AND key<?2
|
||||
** 12 key>=?1 AND key<?2
|
||||
** 16 key<=?1
|
||||
** 18 key>?1 AND key<=?2
|
||||
** 20 key>=?1 AND key<=?2
|
||||
** 33..52 Use blobkey in place of key...
|
||||
** 0 key==?1
|
||||
** 1 key>=?1 AND key<=?2
|
||||
** 2 key>?1 or key>=?1
|
||||
** 3 key<?1 or key<=?1
|
||||
** 99 Full table scan only
|
||||
*/
|
||||
static int lsm1BestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i; /* Loop over constraints */
|
||||
int idxNum = 0; /* The query plan bitmask */
|
||||
int idxNum = 99; /* The query plan */
|
||||
int nArg = 0; /* Number of arguments to xFilter */
|
||||
int eqIdx = -1; /* Index of the key== constraint, or -1 if none */
|
||||
int argIdx = -1; /* Index of the key== constraint, or -1 if none */
|
||||
int iIdx2 = -1; /* The index of the second key */
|
||||
int omit1 = 0;
|
||||
int omit2 = 0;
|
||||
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint && idxNum<16; i++, pConstraint++){
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( pConstraint->iColumn!=LSM1_COLUMN_KEY ) continue;
|
||||
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
if( pConstraint->iColumn!=0 ) continue;
|
||||
switch( pConstraint->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ: {
|
||||
eqIdx = i;
|
||||
idxNum = 1;
|
||||
if( idxNum>0 ){
|
||||
argIdx = i;
|
||||
iIdx2 = -1;
|
||||
idxNum = 0;
|
||||
omit1 = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_INDEX_CONSTRAINT_GE:
|
||||
case SQLITE_INDEX_CONSTRAINT_GT: {
|
||||
if( idxNum==99 ){
|
||||
argIdx = i;
|
||||
idxNum = 2;
|
||||
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE;
|
||||
}else if( idxNum==3 ){
|
||||
iIdx2 = idxNum;
|
||||
omit2 = omit1;
|
||||
argIdx = i;
|
||||
idxNum = 1;
|
||||
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_INDEX_CONSTRAINT_LE:
|
||||
case SQLITE_INDEX_CONSTRAINT_LT: {
|
||||
if( idxNum==99 ){
|
||||
argIdx = i;
|
||||
idxNum = 3;
|
||||
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE;
|
||||
}else if( idxNum==2 ){
|
||||
iIdx2 = i;
|
||||
idxNum = 1;
|
||||
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( eqIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[eqIdx].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[eqIdx].omit = 1;
|
||||
if( argIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[argIdx].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[argIdx].omit = omit1;
|
||||
}
|
||||
if( idxNum==1 ){
|
||||
if( iIdx2>=0 ){
|
||||
pIdxInfo->aConstraintUsage[iIdx2].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[iIdx2].omit = omit2;
|
||||
}
|
||||
if( idxNum==0 ){
|
||||
pIdxInfo->estimatedCost = (double)1;
|
||||
pIdxInfo->estimatedRows = 1;
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}else if( idxNum==1 ){
|
||||
pIdxInfo->estimatedCost = (double)100;
|
||||
pIdxInfo->estimatedRows = 100;
|
||||
}else if( idxNum<99 ){
|
||||
pIdxInfo->estimatedCost = (double)5000;
|
||||
pIdxInfo->estimatedRows = 5000;
|
||||
}else{
|
||||
/* Full table scan */
|
||||
pIdxInfo->estimatedCost = (double)2147483647;
|
||||
@@ -615,16 +870,13 @@ int lsm1Update(
|
||||
sqlite_int64 *pRowid
|
||||
){
|
||||
lsm1_vtab *p = (lsm1_vtab*)pVTab;
|
||||
const void *pKey;
|
||||
void *pFree = 0;
|
||||
int nKey;
|
||||
int eType;
|
||||
int i;
|
||||
int rc = LSM_OK;
|
||||
sqlite3_value *pValue;
|
||||
const unsigned char *pVal;
|
||||
unsigned char *pData;
|
||||
int nVal;
|
||||
unsigned char pSpace[100];
|
||||
unsigned char *pKey;
|
||||
unsigned char aKey[16];
|
||||
unsigned char pSpace[16];
|
||||
lsm1_vblob val;
|
||||
|
||||
if( argc==1 ){
|
||||
pVTab->zErrMsg = sqlite3_mprintf("cannot DELETE");
|
||||
@@ -635,78 +887,73 @@ int lsm1Update(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* "INSERT INTO tab(command) VALUES('....')" is used to implement
|
||||
/* "INSERT INTO tab(lsm1_command) VALUES('....')" is used to implement
|
||||
** special commands.
|
||||
*/
|
||||
if( sqlite3_value_type(argv[2+LSM1_COLUMN_COMMAND])!=SQLITE_NULL ){
|
||||
if( sqlite3_value_type(argv[3+p->nVal])!=SQLITE_NULL ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBKEY])==SQLITE_BLOB ){
|
||||
/* Use the blob key exactly as supplied */
|
||||
pKey = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBKEY]);
|
||||
nKey = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBKEY]);
|
||||
if( p->keyType==SQLITE_BLOB ){
|
||||
pKey = (u8*)sqlite3_value_blob(argv[2]);
|
||||
nKey = sqlite3_value_bytes(argv[2]);
|
||||
}else if( p->keyType==SQLITE_TEXT ){
|
||||
pKey = (u8*)sqlite3_value_text(argv[2]);
|
||||
nKey = sqlite3_value_bytes(argv[2]);
|
||||
}else{
|
||||
/* Use a key encoding that sorts in lexicographical order */
|
||||
rc = lsm1EncodeKey(argv[2+LSM1_COLUMN_KEY],
|
||||
(unsigned char**)&pKey,&nKey,
|
||||
pSpace,sizeof(pSpace));
|
||||
if( rc ) return rc;
|
||||
if( pKey!=(const void*)pSpace ) pFree = (void*)pKey;
|
||||
sqlite3_int64 v = sqlite3_value_int64(argv[2]);
|
||||
if( v>=0 ){
|
||||
nKey = lsm1PutVarint64(aKey, (sqlite3_uint64)v);
|
||||
pKey = aKey;
|
||||
}else{
|
||||
pVTab->zErrMsg = sqlite3_mprintf("key must be non-negative");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBVALUE])==SQLITE_BLOB ){
|
||||
pVal = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBVALUE]);
|
||||
nVal = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBVALUE]);
|
||||
rc = lsm_insert(p->pDb, pKey, nKey, pVal, nVal);
|
||||
}else{
|
||||
pValue = argv[2+LSM1_COLUMN_VALUE];
|
||||
eType = sqlite3_value_type(pValue);
|
||||
memset(&val, 0, sizeof(val));
|
||||
for(i=0; i<p->nVal; i++){
|
||||
sqlite3_value *pArg = argv[3+i];
|
||||
u8 eType = sqlite3_value_type(pArg);
|
||||
switch( eType ){
|
||||
case SQLITE_NULL: {
|
||||
rc = lsm_delete(p->pDb, pKey, nKey);
|
||||
lsm1VblobAppendVarint(&val, SQLITE_NULL);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB:
|
||||
case SQLITE_TEXT: {
|
||||
if( eType==SQLITE_TEXT ){
|
||||
pVal = sqlite3_value_text(pValue);
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_int64 v = sqlite3_value_int64(pArg);
|
||||
if( v>=0 && v<=240/6 ){
|
||||
lsm1VblobAppendVarint(&val, v*6);
|
||||
}else{
|
||||
pVal = (unsigned char*)sqlite3_value_blob(pValue);
|
||||
}
|
||||
nVal = sqlite3_value_bytes(pValue);
|
||||
pData = sqlite3_malloc( nVal+1 );
|
||||
if( pData==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pData[0] = (unsigned char)eType;
|
||||
memcpy(&pData[1], pVal, nVal);
|
||||
rc = lsm_insert(p->pDb, pKey, nKey, pData, nVal+1);
|
||||
sqlite3_free(pData);
|
||||
int n = lsm1PutSignedVarint64(pSpace, v);
|
||||
lsm1VblobAppendVarint(&val, SQLITE_INTEGER + n*6);
|
||||
lsm1VblobAppend(&val, pSpace, n);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_uint64 x;
|
||||
unsigned char aVal[9];
|
||||
int i;
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
*(sqlite3_int64*)&x = sqlite3_value_int64(pValue);
|
||||
}else{
|
||||
double r = sqlite3_value_double(pValue);
|
||||
assert( sizeof(r)==sizeof(x) );
|
||||
memcpy(&x, &r, sizeof(r));
|
||||
}
|
||||
for(i=8; x>0 && i>=1; i--){
|
||||
aVal[i] = x & 0xff;
|
||||
x >>= 8;
|
||||
}
|
||||
aVal[i] = (unsigned char)eType;
|
||||
rc = lsm_insert(p->pDb, pKey, nKey, &aVal[i], 9-i);
|
||||
double r = sqlite3_value_double(pArg);
|
||||
lsm1VblobAppendVarint(&val, SQLITE_FLOAT + 8*6);
|
||||
lsm1VblobAppend(&val, (u8*)&r, sizeof(r));
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n = sqlite3_value_bytes(pArg);
|
||||
lsm1VblobAppendVarint(&val, n*6 + SQLITE_BLOB);
|
||||
lsm1VblobAppend(&val, sqlite3_value_blob(pArg), n);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
int n = sqlite3_value_bytes(pArg);
|
||||
lsm1VblobAppendVarint(&val, n*6 + SQLITE_TEXT);
|
||||
lsm1VblobAppend(&val, sqlite3_value_text(pArg), n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_free(pFree);
|
||||
if( val.errNoMem ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = lsm_insert(p->pDb, pKey, nKey, val.a, val.n);
|
||||
sqlite3_free(val.a);
|
||||
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,26 +20,27 @@ load_lsm1_vtab db
|
||||
forcedelete testlsm.db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db);
|
||||
CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,UINT,b,c,d);
|
||||
PRAGMA table_info(x1);
|
||||
} {
|
||||
0 key {} 0 {} 0
|
||||
1 blobkey {} 0 {} 0
|
||||
2 value {} 0 {} 0
|
||||
3 blobvalue {} 0 {} 0
|
||||
0 a UINT 0 {} 0
|
||||
1 b {} 0 {} 0
|
||||
2 c {} 0 {} 0
|
||||
3 d {} 0 {} 0
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO x1(blobkey, blobvalue) VALUES(x'abcd', x'1234');
|
||||
SELECT quote(blobkey), quote(blobvalue) FROM x1;
|
||||
} {X'ABCD' X'1234'}
|
||||
INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33),(8,'banjo',x'333231',NULL),
|
||||
(12,NULL,3.25,-559281390);
|
||||
SELECT a, quote(b), quote(c), quote(d) FROM x1;
|
||||
} {8 'banjo' X'333231' NULL 12 NULL 3.25 -559281390 15 11 22 33}
|
||||
|
||||
do_catchsql_test 1.2 {
|
||||
UPDATE x1 SET blobvalue = x'7890' WHERE blobkey = x'abcd';
|
||||
UPDATE x1 SET d = d+1.0 WHERE a=15;
|
||||
} {1 {cannot UPDATE}}
|
||||
|
||||
do_catchsql_test 1.3 {
|
||||
DELETE FROM x1 WHERE blobkey = x'abcd'
|
||||
DELETE FROM x1 WHERE a=15;
|
||||
} {1 {cannot DELETE}}
|
||||
|
||||
do_test 1.4 {
|
||||
@@ -52,5 +53,3 @@ do_test 1.5 {
|
||||
} {testlsm.db}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
+7
-7
@@ -680,16 +680,16 @@ static int csvtabNext(sqlite3_vtab_cursor *cur){
|
||||
i++;
|
||||
}
|
||||
}while( pCur->rdr.cTerm==',' );
|
||||
while( i<pTab->nCol ){
|
||||
sqlite3_free(pCur->azVal[i]);
|
||||
pCur->azVal[i] = 0;
|
||||
pCur->aLen[i] = 0;
|
||||
i++;
|
||||
}
|
||||
if( z==0 || pCur->rdr.cTerm==EOF ){
|
||||
if( z==0 || (pCur->rdr.cTerm==EOF && i<pTab->nCol) ){
|
||||
pCur->iRowid = -1;
|
||||
}else{
|
||||
pCur->iRowid++;
|
||||
while( i<pTab->nCol ){
|
||||
sqlite3_free(pCur->azVal[i]);
|
||||
pCur->azVal[i] = 0;
|
||||
pCur->aLen[i] = 0;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
+415
-97
@@ -10,8 +10,8 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains the implementation of the "unionvtab" virtual
|
||||
** table. This module provides read-only access to multiple tables,
|
||||
** This file contains the implementation of the "unionvtab" and "swarmvtab"
|
||||
** virtual tables. These modules provide read-only access to multiple tables,
|
||||
** possibly in multiple database files, via a single database object.
|
||||
** The source tables must have the following characteristics:
|
||||
**
|
||||
@@ -25,26 +25,48 @@
|
||||
**
|
||||
** * Each table must contain a distinct range of rowid values.
|
||||
**
|
||||
** A "unionvtab" virtual table is created as follows:
|
||||
** The difference between the two virtual table modules is that for
|
||||
** "unionvtab", all source tables must be located in the main database or
|
||||
** in databases ATTACHed to the main database by the user. For "swarmvtab",
|
||||
** the tables may be located in any database file on disk. The "swarmvtab"
|
||||
** implementation takes care of opening and closing database files
|
||||
** automatically.
|
||||
**
|
||||
** CREATE VIRTUAL TABLE <name> USING unionvtab(<sql statement>);
|
||||
** UNIONVTAB
|
||||
**
|
||||
** The implementation evalutes <sql statement> whenever a unionvtab virtual
|
||||
** table is created or opened. It should return one row for each source
|
||||
** database table. The four columns required of each row are:
|
||||
** A "unionvtab" virtual table is created as follows:
|
||||
**
|
||||
** 1. The name of the database containing the table ("main" or "temp" or
|
||||
** the name of an attached database). Or NULL to indicate that all
|
||||
** databases should be searched for the table in the usual fashion.
|
||||
** CREATE VIRTUAL TABLE <name> USING unionvtab(<sql-statement>);
|
||||
**
|
||||
** 2. The name of the database table.
|
||||
** The implementation evalutes <sql statement> whenever a unionvtab virtual
|
||||
** table is created or opened. It should return one row for each source
|
||||
** database table. The four columns required of each row are:
|
||||
**
|
||||
** 3. The smallest rowid in the range of rowids that may be stored in the
|
||||
** database table (an integer).
|
||||
** 1. The name of the database containing the table ("main" or "temp" or
|
||||
** the name of an attached database). Or NULL to indicate that all
|
||||
** databases should be searched for the table in the usual fashion.
|
||||
**
|
||||
** 4. The largest rowid in the range of rowids that may be stored in the
|
||||
** database table (an integer).
|
||||
** 2. The name of the database table.
|
||||
**
|
||||
** 3. The smallest rowid in the range of rowids that may be stored in the
|
||||
** database table (an integer).
|
||||
**
|
||||
** 4. The largest rowid in the range of rowids that may be stored in the
|
||||
** database table (an integer).
|
||||
**
|
||||
** SWARMVTAB
|
||||
**
|
||||
** A "swarmvtab" virtual table is created similarly to a unionvtab table:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE <name>
|
||||
** USING swarmvtab(<sql-statement>, <callback>);
|
||||
**
|
||||
** The difference is that for a swarmvtab table, the first column returned
|
||||
** by the <sql statement> must return a path or URI that can be used to open
|
||||
** the database file containing the source table. The <callback> option
|
||||
** is optional. If included, it is the name of an application-defined
|
||||
** SQL function that is invoked with the URI of the file, if the file
|
||||
** does not already exist on disk.
|
||||
*/
|
||||
|
||||
#include "sqlite3ext.h"
|
||||
@@ -65,6 +87,30 @@ SQLITE_EXTENSION_INIT1
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The following is also copied from sqliteInt.h. To facilitate coverage
|
||||
** testing.
|
||||
*/
|
||||
#ifndef ALWAYS
|
||||
# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
|
||||
# define ALWAYS(X) (1)
|
||||
# define NEVER(X) (0)
|
||||
# elif !defined(NDEBUG)
|
||||
# define ALWAYS(X) ((X)?1:(assert(0),0))
|
||||
# define NEVER(X) ((X)?(assert(0),1):0)
|
||||
# else
|
||||
# define ALWAYS(X) (X)
|
||||
# define NEVER(X) (X)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The swarmvtab module attempts to keep the number of open database files
|
||||
** at or below this limit. This may not be possible if there are too many
|
||||
** simultaneous queries.
|
||||
*/
|
||||
#define SWARMVTAB_MAX_OPEN 9
|
||||
|
||||
typedef struct UnionCsr UnionCsr;
|
||||
typedef struct UnionTab UnionTab;
|
||||
typedef struct UnionSrc UnionSrc;
|
||||
@@ -79,6 +125,12 @@ struct UnionSrc {
|
||||
char *zTab; /* Source table name */
|
||||
sqlite3_int64 iMin; /* Minimum rowid */
|
||||
sqlite3_int64 iMax; /* Maximum rowid */
|
||||
|
||||
/* Fields used by swarmvtab only */
|
||||
char *zFile; /* Database file containing table zTab */
|
||||
int nUser; /* Current number of users */
|
||||
sqlite3 *db; /* Database handle */
|
||||
UnionSrc *pNextClosable; /* Next in list of closable sources */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -87,9 +139,17 @@ struct UnionSrc {
|
||||
struct UnionTab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
sqlite3 *db; /* Database handle */
|
||||
int bSwarm; /* 1 for "swarmvtab", 0 for "unionvtab" */
|
||||
int iPK; /* INTEGER PRIMARY KEY column, or -1 */
|
||||
int nSrc; /* Number of elements in the aSrc[] array */
|
||||
UnionSrc *aSrc; /* Array of source tables, sorted by rowid */
|
||||
|
||||
/* Used by swarmvtab only */
|
||||
char *zSourceStr; /* Expected unionSourceToStr() value */
|
||||
char *zNotFoundCallback; /* UDF to invoke if file not found on open */
|
||||
UnionSrc *pClosable; /* First in list of closable sources */
|
||||
int nOpen; /* Current number of open sources */
|
||||
int nMaxOpen; /* Maximum number of open sources */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -98,8 +158,20 @@ struct UnionTab {
|
||||
struct UnionCsr {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_stmt *pStmt; /* SQL statement to run */
|
||||
|
||||
/* Used by swarmvtab only */
|
||||
sqlite3_int64 iMaxRowid; /* Last rowid to visit */
|
||||
int iTab; /* Index of table read by pStmt */
|
||||
};
|
||||
|
||||
/*
|
||||
** Given UnionTab table pTab and UnionSrc object pSrc, return the database
|
||||
** handle that should be used to access the table identified by pSrc. This
|
||||
** is the main db handle for "unionvtab" tables, or the source-specific
|
||||
** handle for "swarmvtab".
|
||||
*/
|
||||
#define unionGetDb(pTab, pSrc) ((pTab)->bSwarm ? (pSrc)->db : (pTab)->db)
|
||||
|
||||
/*
|
||||
** If *pRc is other than SQLITE_OK when this function is called, it
|
||||
** always returns NULL. Otherwise, it attempts to allocate and return
|
||||
@@ -160,7 +232,7 @@ static void unionDequote(char *z){
|
||||
int iIn = 1;
|
||||
int iOut = 0;
|
||||
if( q=='[' ) q = ']';
|
||||
while( z[iIn] ){
|
||||
while( ALWAYS(z[iIn]) ){
|
||||
if( z[iIn]==q ){
|
||||
if( z[iIn+1]!=q ){
|
||||
/* Character iIn was the close quote. */
|
||||
@@ -202,6 +274,7 @@ static sqlite3_stmt *unionPrepare(
|
||||
char **pzErr /* OUT: Error message */
|
||||
){
|
||||
sqlite3_stmt *pRet = 0;
|
||||
assert( pzErr );
|
||||
if( *pRc==SQLITE_OK ){
|
||||
int rc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -250,6 +323,7 @@ static sqlite3_stmt *unionPreparePrintf(
|
||||
** In this case, *pzErr may be set to point to an error message
|
||||
** buffer allocated by sqlite3_malloc().
|
||||
*/
|
||||
#if 0
|
||||
static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
|
||||
int rc = sqlite3_reset(pStmt);
|
||||
if( *pRc==SQLITE_OK ){
|
||||
@@ -259,15 +333,39 @@ static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Call sqlite3_finalize() on SQL statement pStmt. If *pRc is set to
|
||||
** SQLITE_OK when this function is called, then it is set to the
|
||||
** value returned by sqlite3_finalize() before this function exits.
|
||||
*/
|
||||
static void unionFinalize(int *pRc, sqlite3_stmt *pStmt){
|
||||
static void unionFinalize(int *pRc, sqlite3_stmt *pStmt, char **pzErr){
|
||||
sqlite3 *db = sqlite3_db_handle(pStmt);
|
||||
int rc = sqlite3_finalize(pStmt);
|
||||
if( *pRc==SQLITE_OK ) *pRc = rc;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
*pRc = rc;
|
||||
if( rc ){
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op for unionvtab. For swarmvtab, it attempts to
|
||||
** close open database files until at most nMax are open. An SQLite error
|
||||
** code is returned if an error occurs, or SQLITE_OK otherwise.
|
||||
*/
|
||||
static void unionCloseSources(UnionTab *pTab, int nMax){
|
||||
while( pTab->pClosable && pTab->nOpen>nMax ){
|
||||
UnionSrc **pp;
|
||||
for(pp=&pTab->pClosable; (*pp)->pNextClosable; pp=&(*pp)->pNextClosable);
|
||||
assert( (*pp)->db );
|
||||
sqlite3_close((*pp)->db);
|
||||
(*pp)->db = 0;
|
||||
*pp = 0;
|
||||
pTab->nOpen--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -278,15 +376,52 @@ static int unionDisconnect(sqlite3_vtab *pVtab){
|
||||
UnionTab *pTab = (UnionTab*)pVtab;
|
||||
int i;
|
||||
for(i=0; i<pTab->nSrc; i++){
|
||||
sqlite3_free(pTab->aSrc[i].zDb);
|
||||
sqlite3_free(pTab->aSrc[i].zTab);
|
||||
UnionSrc *pSrc = &pTab->aSrc[i];
|
||||
sqlite3_free(pSrc->zDb);
|
||||
sqlite3_free(pSrc->zTab);
|
||||
sqlite3_free(pSrc->zFile);
|
||||
sqlite3_close(pSrc->db);
|
||||
}
|
||||
sqlite3_free(pTab->zSourceStr);
|
||||
sqlite3_free(pTab->zNotFoundCallback);
|
||||
sqlite3_free(pTab->aSrc);
|
||||
sqlite3_free(pTab);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check that the table identified by pSrc is a rowid table. If not,
|
||||
** return SQLITE_ERROR and set (*pzErr) to point to an English language
|
||||
** error message. If the table is a rowid table and no error occurs,
|
||||
** return SQLITE_OK and leave (*pzErr) unmodified.
|
||||
*/
|
||||
static int unionIsIntkeyTable(
|
||||
sqlite3 *db, /* Database handle */
|
||||
UnionSrc *pSrc, /* Source table to test */
|
||||
char **pzErr /* OUT: Error message */
|
||||
){
|
||||
int bPk = 0;
|
||||
const char *zType = 0;
|
||||
int rc;
|
||||
|
||||
sqlite3_table_column_metadata(
|
||||
db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0
|
||||
);
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc==SQLITE_ERROR
|
||||
|| (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType)))
|
||||
){
|
||||
rc = SQLITE_ERROR;
|
||||
*pzErr = sqlite3_mprintf("no such rowid table: %s%s%s",
|
||||
(pSrc->zDb ? pSrc->zDb : ""),
|
||||
(pSrc->zDb ? "." : ""),
|
||||
pSrc->zTab
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is other than SQLITE_OK when it is
|
||||
** called. In this case it returns NULL.
|
||||
@@ -306,41 +441,27 @@ static int unionDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static char *unionSourceToStr(
|
||||
int *pRc, /* IN/OUT: Error code */
|
||||
sqlite3 *db, /* Database handle */
|
||||
UnionTab *pTab, /* Virtual table object */
|
||||
UnionSrc *pSrc, /* Source table to test */
|
||||
sqlite3_stmt *pStmt,
|
||||
char **pzErr /* OUT: Error message */
|
||||
){
|
||||
char *zRet = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
int bPk = 0;
|
||||
const char *zType = 0;
|
||||
int rc;
|
||||
|
||||
sqlite3_table_column_metadata(
|
||||
db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0
|
||||
sqlite3 *db = unionGetDb(pTab, pSrc);
|
||||
int rc = unionIsIntkeyTable(db, pSrc, pzErr);
|
||||
sqlite3_stmt *pStmt = unionPrepare(&rc, db,
|
||||
"SELECT group_concat(quote(name) || '.' || quote(type)) "
|
||||
"FROM pragma_table_info(?, ?)", pzErr
|
||||
);
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc==SQLITE_ERROR
|
||||
|| (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType)))
|
||||
){
|
||||
rc = SQLITE_ERROR;
|
||||
*pzErr = sqlite3_mprintf("no such rowid table: %s%s%s",
|
||||
(pSrc->zDb ? pSrc->zDb : ""),
|
||||
(pSrc->zDb ? "." : ""),
|
||||
pSrc->zTab
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pStmt, 1, pSrc->zTab, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(pStmt, 2, pSrc->zDb, -1, SQLITE_STATIC);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
zRet = unionStrdup(&rc, (const char*)sqlite3_column_text(pStmt, 0));
|
||||
const char *z = (const char*)sqlite3_column_text(pStmt, 0);
|
||||
zRet = unionStrdup(&rc, z);
|
||||
}
|
||||
unionReset(&rc, pStmt, pzErr);
|
||||
unionFinalize(&rc, pStmt, pzErr);
|
||||
}
|
||||
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
@@ -356,34 +477,152 @@ static char *unionSourceToStr(
|
||||
** other error occurs, SQLITE_OK is returned.
|
||||
*/
|
||||
static int unionSourceCheck(UnionTab *pTab, char **pzErr){
|
||||
const char *zSql =
|
||||
"SELECT group_concat(quote(name) || '.' || quote(type)) "
|
||||
"FROM pragma_table_info(?, ?)";
|
||||
int rc = SQLITE_OK;
|
||||
char *z0 = 0;
|
||||
int i;
|
||||
|
||||
if( pTab->nSrc==0 ){
|
||||
*pzErr = sqlite3_mprintf("no source tables configured");
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char *z0 = 0;
|
||||
int i;
|
||||
assert( *pzErr==0 );
|
||||
z0 = unionSourceToStr(&rc, pTab, &pTab->aSrc[0], pzErr);
|
||||
for(i=1; i<pTab->nSrc; i++){
|
||||
char *z = unionSourceToStr(&rc, pTab, &pTab->aSrc[i], pzErr);
|
||||
if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){
|
||||
*pzErr = sqlite3_mprintf("source table schema mismatch");
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3_free(z);
|
||||
}
|
||||
sqlite3_free(z0);
|
||||
|
||||
pStmt = unionPrepare(&rc, pTab->db, zSql, pzErr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to open the swarmvtab database. If initially unable, invoke the
|
||||
** not-found callback UDF and then try again.
|
||||
*/
|
||||
static int unionOpenDatabaseInner(UnionTab *pTab, UnionSrc *pSrc, char **pzErr){
|
||||
int rc = SQLITE_OK;
|
||||
static const int openFlags =
|
||||
SQLITE_OPEN_READONLY | SQLITE_OPEN_URI;
|
||||
rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
|
||||
if( rc==SQLITE_OK ) return rc;
|
||||
if( pTab->zNotFoundCallback ){
|
||||
char *zSql = sqlite3_mprintf("SELECT \"%w\"(%Q);",
|
||||
pTab->zNotFoundCallback, pSrc->zFile);
|
||||
sqlite3_close(pSrc->db);
|
||||
pSrc->db = 0;
|
||||
if( zSql==0 ){
|
||||
*pzErr = sqlite3_mprintf("out of memory");
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3_exec(pTab->db, zSql, 0, 0, pzErr);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pSrc->db));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function may only be called for swarmvtab tables. The results of
|
||||
** calling it on a unionvtab table are undefined.
|
||||
**
|
||||
** For a swarmvtab table, this function ensures that source database iSrc
|
||||
** is open. If the database is opened successfully and the schema is as
|
||||
** expected, or if it is already open when this function is called, SQLITE_OK
|
||||
** is returned.
|
||||
**
|
||||
** Alternatively If an error occurs while opening the databases, or if the
|
||||
** database schema is unsuitable, an SQLite error code is returned and (*pzErr)
|
||||
** may be set to point to an English language error message. In this case it is
|
||||
** the responsibility of the caller to eventually free the error message buffer
|
||||
** using sqlite3_free().
|
||||
*/
|
||||
static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){
|
||||
int rc = SQLITE_OK;
|
||||
UnionSrc *pSrc = &pTab->aSrc[iSrc];
|
||||
|
||||
assert( pTab->bSwarm && iSrc<pTab->nSrc );
|
||||
if( pSrc->db==0 ){
|
||||
unionCloseSources(pTab, pTab->nMaxOpen-1);
|
||||
rc = unionOpenDatabaseInner(pTab, pSrc, pzErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
z0 = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[0], pStmt, pzErr);
|
||||
}
|
||||
for(i=1; i<pTab->nSrc; i++){
|
||||
char *z = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[i], pStmt, pzErr);
|
||||
if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){
|
||||
*pzErr = sqlite3_mprintf("source table schema mismatch");
|
||||
rc = SQLITE_ERROR;
|
||||
char *z = unionSourceToStr(&rc, pTab, pSrc, pzErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pTab->zSourceStr==0 ){
|
||||
pTab->zSourceStr = z;
|
||||
}else{
|
||||
if( sqlite3_stricmp(z, pTab->zSourceStr) ){
|
||||
*pzErr = sqlite3_mprintf("source table schema mismatch");
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3_free(z);
|
||||
}
|
||||
}
|
||||
sqlite3_free(z);
|
||||
}
|
||||
|
||||
unionFinalize(&rc, pStmt);
|
||||
sqlite3_free(z0);
|
||||
if( rc==SQLITE_OK ){
|
||||
pSrc->pNextClosable = pTab->pClosable;
|
||||
pTab->pClosable = pSrc;
|
||||
pTab->nOpen++;
|
||||
}else{
|
||||
sqlite3_close(pSrc->db);
|
||||
pSrc->db = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is a no-op for unionvtab tables. For swarmvtab, increment
|
||||
** the reference count for source table iTab. If the reference count was
|
||||
** zero before it was incremented, also remove the source from the closable
|
||||
** list.
|
||||
*/
|
||||
static void unionIncrRefcount(UnionTab *pTab, int iTab){
|
||||
if( pTab->bSwarm ){
|
||||
UnionSrc *pSrc = &pTab->aSrc[iTab];
|
||||
assert( pSrc->nUser>=0 && pSrc->db );
|
||||
if( pSrc->nUser==0 ){
|
||||
UnionSrc **pp;
|
||||
for(pp=&pTab->pClosable; *pp!=pSrc; pp=&(*pp)->pNextClosable);
|
||||
*pp = pSrc->pNextClosable;
|
||||
pSrc->pNextClosable = 0;
|
||||
}
|
||||
pSrc->nUser++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Finalize the SQL statement pCsr->pStmt and return the result.
|
||||
**
|
||||
** If this is a swarmvtab table (not unionvtab) and pCsr->pStmt was not
|
||||
** NULL when this function was called, also decrement the reference
|
||||
** count on the associated source table. If this means the source tables
|
||||
** refcount is now zero, add it to the closable list.
|
||||
*/
|
||||
static int unionFinalizeCsrStmt(UnionCsr *pCsr){
|
||||
int rc = SQLITE_OK;
|
||||
if( pCsr->pStmt ){
|
||||
UnionTab *pTab = (UnionTab*)pCsr->base.pVtab;
|
||||
UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab];
|
||||
rc = sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
if( pTab->bSwarm ){
|
||||
pSrc->nUser--;
|
||||
assert( pSrc->nUser>=0 );
|
||||
if( pSrc->nUser==0 ){
|
||||
pSrc->pNextClosable = pTab->pClosable;
|
||||
pTab->pClosable = pSrc;
|
||||
}
|
||||
unionCloseSources(pTab, pTab->nMaxOpen);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -393,10 +632,11 @@ static int unionSourceCheck(UnionTab *pTab, char **pzErr){
|
||||
**
|
||||
** The argv[] array contains the following:
|
||||
**
|
||||
** argv[0] -> module name ("unionvtab")
|
||||
** argv[0] -> module name ("unionvtab" or "swarmvtab")
|
||||
** argv[1] -> database name
|
||||
** argv[2] -> table name
|
||||
** argv[3] -> SQL statement
|
||||
** argv[4] -> not-found callback UDF name
|
||||
*/
|
||||
static int unionConnect(
|
||||
sqlite3 *db,
|
||||
@@ -407,14 +647,15 @@ static int unionConnect(
|
||||
){
|
||||
UnionTab *pTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int bSwarm = (pAux==0 ? 0 : 1);
|
||||
const char *zVtab = (bSwarm ? "swarmvtab" : "unionvtab");
|
||||
|
||||
(void)pAux; /* Suppress harmless 'unused parameter' warning */
|
||||
if( sqlite3_stricmp("temp", argv[1]) ){
|
||||
/* unionvtab tables may only be created in the temp schema */
|
||||
*pzErr = sqlite3_mprintf("unionvtab tables must be created in TEMP schema");
|
||||
*pzErr = sqlite3_mprintf("%s tables must be created in TEMP schema", zVtab);
|
||||
rc = SQLITE_ERROR;
|
||||
}else if( argc!=4 ){
|
||||
*pzErr = sqlite3_mprintf("wrong number of arguments for unionvtab");
|
||||
}else if( argc!=4 && argc!=5 ){
|
||||
*pzErr = sqlite3_mprintf("wrong number of arguments for %s", zVtab);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
int nAlloc = 0; /* Allocated size of pTab->aSrc[] */
|
||||
@@ -464,30 +705,60 @@ static int unionConnect(
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
pSrc = &pTab->aSrc[pTab->nSrc++];
|
||||
pSrc->zDb = unionStrdup(&rc, zDb);
|
||||
pSrc->zTab = unionStrdup(&rc, zTab);
|
||||
pSrc->iMin = iMin;
|
||||
pSrc->iMax = iMax;
|
||||
if( rc==SQLITE_OK ){
|
||||
pSrc = &pTab->aSrc[pTab->nSrc++];
|
||||
pSrc->zTab = unionStrdup(&rc, zTab);
|
||||
pSrc->iMin = iMin;
|
||||
pSrc->iMax = iMax;
|
||||
if( bSwarm ){
|
||||
pSrc->zFile = unionStrdup(&rc, zDb);
|
||||
}else{
|
||||
pSrc->zDb = unionStrdup(&rc, zDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
unionFinalize(&rc, pStmt);
|
||||
unionFinalize(&rc, pStmt, pzErr);
|
||||
pStmt = 0;
|
||||
|
||||
/* Verify that all source tables exist and have compatible schemas. */
|
||||
/* Capture the not-found callback UDF name */
|
||||
if( rc==SQLITE_OK && argc>=5 ){
|
||||
pTab->zNotFoundCallback = unionStrdup(&rc, argv[4]);
|
||||
unionDequote(pTab->zNotFoundCallback);
|
||||
}
|
||||
|
||||
/* It is an error if the SELECT statement returned zero rows. If only
|
||||
** because there is no way to determine the schema of the virtual
|
||||
** table in this case. */
|
||||
if( rc==SQLITE_OK && pTab->nSrc==0 ){
|
||||
*pzErr = sqlite3_mprintf("no source tables configured");
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* For unionvtab, verify that all source tables exist and have
|
||||
** compatible schemas. For swarmvtab, attach the first database and
|
||||
** check that the first table is a rowid table only. */
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab->db = db;
|
||||
rc = unionSourceCheck(pTab, pzErr);
|
||||
pTab->bSwarm = bSwarm;
|
||||
pTab->nMaxOpen = SWARMVTAB_MAX_OPEN;
|
||||
if( bSwarm ){
|
||||
rc = unionOpenDatabase(pTab, 0, pzErr);
|
||||
}else{
|
||||
rc = unionSourceCheck(pTab, pzErr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compose a CREATE TABLE statement and pass it to declare_vtab() */
|
||||
if( rc==SQLITE_OK ){
|
||||
pStmt = unionPreparePrintf(&rc, pzErr, db, "SELECT "
|
||||
UnionSrc *pSrc = &pTab->aSrc[0];
|
||||
sqlite3 *tdb = unionGetDb(pTab, pSrc);
|
||||
pStmt = unionPreparePrintf(&rc, pzErr, tdb, "SELECT "
|
||||
"'CREATE TABLE xyz('"
|
||||
" || group_concat(quote(name) || ' ' || type, ', ')"
|
||||
" || ')',"
|
||||
"max((cid+1) * (type='INTEGER' COLLATE nocase AND pk=1))-1 "
|
||||
"FROM pragma_table_info(%Q, ?)",
|
||||
pTab->aSrc[0].zTab, pTab->aSrc[0].zDb
|
||||
pSrc->zTab, pSrc->zDb
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
@@ -496,7 +767,7 @@ static int unionConnect(
|
||||
pTab->iPK = sqlite3_column_int(pStmt, 1);
|
||||
}
|
||||
|
||||
unionFinalize(&rc, pStmt);
|
||||
unionFinalize(&rc, pStmt, pzErr);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -508,7 +779,6 @@ static int unionConnect(
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xOpen
|
||||
*/
|
||||
@@ -526,25 +796,56 @@ static int unionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
*/
|
||||
static int unionClose(sqlite3_vtab_cursor *cur){
|
||||
UnionCsr *pCsr = (UnionCsr*)cur;
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
unionFinalizeCsrStmt(pCsr);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work of the xNext() method. Except that, if it
|
||||
** returns SQLITE_ROW, it should be called again within the same xNext()
|
||||
** method call. See unionNext() for details.
|
||||
*/
|
||||
static int doUnionNext(UnionCsr *pCsr){
|
||||
int rc = SQLITE_OK;
|
||||
assert( pCsr->pStmt );
|
||||
if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){
|
||||
UnionTab *pTab = (UnionTab*)pCsr->base.pVtab;
|
||||
rc = unionFinalizeCsrStmt(pCsr);
|
||||
if( rc==SQLITE_OK && pTab->bSwarm ){
|
||||
pCsr->iTab++;
|
||||
if( pCsr->iTab<pTab->nSrc ){
|
||||
UnionSrc *pSrc = &pTab->aSrc[pCsr->iTab];
|
||||
if( pCsr->iMaxRowid>=pSrc->iMin ){
|
||||
/* It is necessary to scan the next table. */
|
||||
rc = unionOpenDatabase(pTab, pCsr->iTab, &pTab->base.zErrMsg);
|
||||
pCsr->pStmt = unionPreparePrintf(&rc, &pTab->base.zErrMsg, pSrc->db,
|
||||
"SELECT rowid, * FROM %Q %s %lld",
|
||||
pSrc->zTab,
|
||||
(pSrc->iMax>pCsr->iMaxRowid ? "WHERE _rowid_ <=" : "-- "),
|
||||
pCsr->iMaxRowid
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCsr->pStmt );
|
||||
unionIncrRefcount(pTab, pCsr->iTab);
|
||||
rc = SQLITE_ROW;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** xNext
|
||||
*/
|
||||
static int unionNext(sqlite3_vtab_cursor *cur){
|
||||
UnionCsr *pCsr = (UnionCsr*)cur;
|
||||
int rc;
|
||||
assert( pCsr->pStmt );
|
||||
if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){
|
||||
rc = sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
do {
|
||||
rc = doUnionNext((UnionCsr*)cur);
|
||||
}while( rc==SQLITE_ROW );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -637,8 +938,7 @@ static int unionFilter(
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
unionFinalizeCsrStmt(pCsr);
|
||||
if( bZero ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -674,12 +974,25 @@ static int unionFilter(
|
||||
zSql = sqlite3_mprintf("%z %s rowid<=%lld", zSql, zWhere, iMax);
|
||||
}
|
||||
}
|
||||
|
||||
if( pTab->bSwarm ){
|
||||
pCsr->iTab = i;
|
||||
pCsr->iMaxRowid = iMax;
|
||||
rc = unionOpenDatabase(pTab, i, &pTab->base.zErrMsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( zSql==0 ) return rc;
|
||||
pCsr->pStmt = unionPrepare(&rc, pTab->db, zSql, &pTab->base.zErrMsg);
|
||||
sqlite3_free(zSql);
|
||||
if( zSql==0 ){
|
||||
return rc;
|
||||
}else{
|
||||
sqlite3 *db = unionGetDb(pTab, &pTab->aSrc[pCsr->iTab]);
|
||||
pCsr->pStmt = unionPrepare(&rc, db, zSql, &pTab->base.zErrMsg);
|
||||
if( pCsr->pStmt ){
|
||||
unionIncrRefcount(pTab, pCsr->iTab);
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
return unionNext(pVtabCursor);
|
||||
}
|
||||
@@ -791,8 +1104,13 @@ static int createUnionVtab(sqlite3 *db){
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
};
|
||||
int rc;
|
||||
|
||||
return sqlite3_create_module(db, "unionvtab", &unionModule, 0);
|
||||
rc = sqlite3_create_module(db, "unionvtab", &unionModule, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_module(db, "swarmvtab", &unionModule, (void*)db);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
** 2017-08-10
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements a virtual table that prints diagnostic information
|
||||
** on stdout when its key interfaces are called. This is intended for
|
||||
** interactive analysis and debugging of virtual table interfaces.
|
||||
**
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./vtablog
|
||||
** CREATE VIRTUAL TABLE temp.log USING vtablog(
|
||||
** schema='CREATE TABLE x(a,b,c)',
|
||||
** rows=25
|
||||
** );
|
||||
** SELECT * FROM log;
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
/* vtablog_vtab is a subclass of sqlite3_vtab which will
|
||||
** serve as the underlying representation of a vtablog virtual table
|
||||
*/
|
||||
typedef struct vtablog_vtab vtablog_vtab;
|
||||
struct vtablog_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
int nRow; /* Number of rows in the table */
|
||||
int iInst; /* Instance number for this vtablog table */
|
||||
int nCursor; /* Number of cursors created */
|
||||
};
|
||||
|
||||
/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
|
||||
** serve as the underlying representation of a cursor that scans
|
||||
** over rows of the result
|
||||
*/
|
||||
typedef struct vtablog_cursor vtablog_cursor;
|
||||
struct vtablog_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
int iCursor; /* Cursor number */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
};
|
||||
|
||||
/* Skip leading whitespace. Return a pointer to the first non-whitespace
|
||||
** character, or to the zero terminator if the string has only whitespace */
|
||||
static const char *vtablog_skip_whitespace(const char *z){
|
||||
while( isspace((unsigned char)z[0]) ) z++;
|
||||
return z;
|
||||
}
|
||||
|
||||
/* Remove trailing whitespace from the end of string z[] */
|
||||
static void vtablog_trim_whitespace(char *z){
|
||||
size_t n = strlen(z);
|
||||
while( n>0 && isspace((unsigned char)z[n]) ) n--;
|
||||
z[n] = 0;
|
||||
}
|
||||
|
||||
/* Dequote the string */
|
||||
static void vtablog_dequote(char *z){
|
||||
int j;
|
||||
char cQuote = z[0];
|
||||
size_t i, n;
|
||||
|
||||
if( cQuote!='\'' && cQuote!='"' ) return;
|
||||
n = strlen(z);
|
||||
if( n<2 || z[n-1]!=z[0] ) return;
|
||||
for(i=1, j=0; i<n-1; i++){
|
||||
if( z[i]==cQuote && z[i+1]==cQuote ) i++;
|
||||
z[j++] = z[i];
|
||||
}
|
||||
z[j] = 0;
|
||||
}
|
||||
|
||||
/* Check to see if the string is of the form: "TAG = VALUE" with optional
|
||||
** whitespace before and around tokens. If it is, return a pointer to the
|
||||
** first character of VALUE. If it is not, return NULL.
|
||||
*/
|
||||
static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){
|
||||
z = vtablog_skip_whitespace(z);
|
||||
if( strncmp(zTag, z, nTag)!=0 ) return 0;
|
||||
z = vtablog_skip_whitespace(z+nTag);
|
||||
if( z[0]!='=' ) return 0;
|
||||
return vtablog_skip_whitespace(z+1);
|
||||
}
|
||||
|
||||
/* Decode a parameter that requires a dequoted string.
|
||||
**
|
||||
** Return non-zero on an error.
|
||||
*/
|
||||
static int vtablog_string_parameter(
|
||||
char **pzErr, /* Leave the error message here, if there is one */
|
||||
const char *zParam, /* Parameter we are checking for */
|
||||
const char *zArg, /* Raw text of the virtual table argment */
|
||||
char **pzVal /* Write the dequoted string value here */
|
||||
){
|
||||
const char *zValue;
|
||||
zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg);
|
||||
if( zValue==0 ) return 0;
|
||||
if( *pzVal ){
|
||||
*pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam);
|
||||
return 1;
|
||||
}
|
||||
*pzVal = sqlite3_mprintf("%s", zValue);
|
||||
if( *pzVal==0 ){
|
||||
*pzErr = sqlite3_mprintf("out of memory");
|
||||
return 1;
|
||||
}
|
||||
vtablog_trim_whitespace(*pzVal);
|
||||
vtablog_dequote(*pzVal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* not used - yet */
|
||||
/* Return 0 if the argument is false and 1 if it is true. Return -1 if
|
||||
** we cannot really tell.
|
||||
*/
|
||||
static int vtablog_boolean(const char *z){
|
||||
if( sqlite3_stricmp("yes",z)==0
|
||||
|| sqlite3_stricmp("on",z)==0
|
||||
|| sqlite3_stricmp("true",z)==0
|
||||
|| (z[0]=='1' && z[1]==0)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3_stricmp("no",z)==0
|
||||
|| sqlite3_stricmp("off",z)==0
|
||||
|| sqlite3_stricmp("false",z)==0
|
||||
|| (z[0]=='0' && z[1]==0)
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The vtablogConnect() method is invoked to create a new
|
||||
** vtablog_vtab that describes the vtablog virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for vtablog_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the vtablog_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against vtablog will look like.
|
||||
*/
|
||||
static int vtablogConnectCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr,
|
||||
int isCreate
|
||||
){
|
||||
static int nInst = 0;
|
||||
vtablog_vtab *pNew;
|
||||
int i;
|
||||
int rc;
|
||||
int iInst = ++nInst;
|
||||
char *zSchema = 0;
|
||||
char *zNRow = 0;
|
||||
|
||||
printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst);
|
||||
printf(" argc=%d\n", argc);
|
||||
for(i=0; i<argc; i++){
|
||||
printf(" argv[%d] = ", i);
|
||||
if( argv[i] ){
|
||||
printf("[%s]\n", argv[i]);
|
||||
}else{
|
||||
printf("NULL\n");
|
||||
}
|
||||
}
|
||||
|
||||
for(i=3; i<argc; i++){
|
||||
const char *z = argv[i];
|
||||
if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if( zSchema==0 ){
|
||||
*pzErr = sqlite3_mprintf("no schema defined");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
rc = sqlite3_declare_vtab(db, zSchema);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->nRow = 10;
|
||||
if( zNRow ) pNew->nRow = atoi(zNRow);
|
||||
pNew->iInst = iInst;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
static int vtablogCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
|
||||
}
|
||||
static int vtablogConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This method is the destructor for vtablog_cursor objects.
|
||||
*/
|
||||
static int vtablogDisconnect(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
printf("vtablogDisconnect(%d)\n", pTab->iInst);
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for vtablog_cursor objects.
|
||||
*/
|
||||
static int vtablogDestroy(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
printf("vtablogDestroy(%d)\n", pTab->iInst);
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new vtablog_cursor object.
|
||||
*/
|
||||
static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)p;
|
||||
vtablog_cursor *pCur;
|
||||
printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor);
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->iCursor = pTab->nCursor;
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a vtablog_cursor.
|
||||
*/
|
||||
static int vtablogClose(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor);
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a vtablog_cursor to its next row of output.
|
||||
*/
|
||||
static int vtablogNext(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n",
|
||||
pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1);
|
||||
pCur->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the vtablog_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int vtablogColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
char zVal[50];
|
||||
|
||||
if( i<26 ){
|
||||
sqlite3_snprintf(sizeof(zVal),zVal,"%c%d",
|
||||
"abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid);
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
|
||||
}
|
||||
printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n",
|
||||
pTab->iInst, pCur->iCursor, i, zVal);
|
||||
sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** rowid is the same as the output value.
|
||||
*/
|
||||
static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("vtablogRowid(tab=%d, cursor=%d): %d\n",
|
||||
pTab->iInst, pCur->iCursor, (int)pCur->iRowid);
|
||||
*pRowid = pCur->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the cursor has been moved off of the last
|
||||
** row of output.
|
||||
*/
|
||||
static int vtablogEof(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
int rc = pCur->iRowid >= pTab->nRow;
|
||||
printf("vtablogEof(tab=%d, cursor=%d): %d\n",
|
||||
pTab->iInst, pCur->iCursor, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Output an sqlite3_value object's value as an SQL literal.
|
||||
*/
|
||||
static void vtablogQuote(sqlite3_value *p){
|
||||
char z[50];
|
||||
switch( sqlite3_value_type(p) ){
|
||||
case SQLITE_NULL: {
|
||||
printf("NULL");
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
|
||||
printf("%s", z);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
|
||||
printf("%s", z);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n = sqlite3_value_bytes(p);
|
||||
const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p);
|
||||
int i;
|
||||
printf("x'");
|
||||
for(i=0; i<n; i++) printf("%02x", z[i]);
|
||||
printf("'");
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const char *z = (const char*)sqlite3_value_text(p);
|
||||
int i;
|
||||
char c;
|
||||
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
|
||||
if( c==0 ){
|
||||
printf("'%s'",z);
|
||||
}else{
|
||||
printf("'");
|
||||
while( *z ){
|
||||
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
|
||||
if( c=='\'' ) i++;
|
||||
if( i ){
|
||||
printf("%.*s", i, z);
|
||||
z += i;
|
||||
}
|
||||
if( c=='\'' ){
|
||||
printf("'");
|
||||
continue;
|
||||
}
|
||||
if( c==0 ){
|
||||
break;
|
||||
}
|
||||
z++;
|
||||
}
|
||||
printf("'");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the vtablog_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to vtablogColumn() or vtablogRowid() or
|
||||
** vtablogEof().
|
||||
*/
|
||||
static int vtablogFilter(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
vtablog_cursor *pCur = (vtablog_cursor *)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor);
|
||||
pCur->iRowid = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the vtablog virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int vtablogBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("vtablogBestIndex(tab=%d):\n", pTab->iInst);
|
||||
pIdxInfo->estimatedCost = (double)500;
|
||||
pIdxInfo->estimatedRows = 500;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
|
||||
** the table.
|
||||
**
|
||||
** This implementation does not actually make any changes to the table
|
||||
** content. It merely logs the fact that the method was invoked
|
||||
*/
|
||||
static int vtablogUpdate(
|
||||
sqlite3_vtab *tab,
|
||||
int argc,
|
||||
sqlite3_value **argv,
|
||||
sqlite_int64 *pRowid
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
int i;
|
||||
printf("vtablogUpdate(tab=%d):\n", pTab->iInst);
|
||||
printf(" argc=%d\n", argc);
|
||||
for(i=0; i<argc; i++){
|
||||
printf(" argv[%d]=", i);
|
||||
vtablogQuote(argv[i]);
|
||||
printf("\n");
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** vtablog virtual table.
|
||||
*/
|
||||
static sqlite3_module vtablogModule = {
|
||||
0, /* iVersion */
|
||||
vtablogCreate, /* xCreate */
|
||||
vtablogConnect, /* xConnect */
|
||||
vtablogBestIndex, /* xBestIndex */
|
||||
vtablogDisconnect, /* xDisconnect */
|
||||
vtablogDestroy, /* xDestroy */
|
||||
vtablogOpen, /* xOpen - open a cursor */
|
||||
vtablogClose, /* xClose - close a cursor */
|
||||
vtablogFilter, /* xFilter - configure scan constraints */
|
||||
vtablogNext, /* xNext - advance a cursor */
|
||||
vtablogEof, /* xEof - check for end of scan */
|
||||
vtablogColumn, /* xColumn - read data */
|
||||
vtablogRowid, /* xRowid - read data */
|
||||
vtablogUpdate, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_vtablog_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
|
||||
return rc;
|
||||
}
|
||||
+1
-1
@@ -114,7 +114,7 @@ ifcapable fts3 {
|
||||
INSERT INTO data_xt VALUES('a', 'b', 1, 0);
|
||||
}
|
||||
} msg] $msg
|
||||
} {1 {SQLITE_ERROR - SQL logic error or missing database}}
|
||||
} {1 {SQLITE_ERROR - SQL logic error}}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
@@ -125,7 +125,7 @@ foreach {tn2 setup sql expect} {
|
||||
{1 SQLITE_IOERR_WRITE}
|
||||
{1 SQLITE_IOERR_READ}
|
||||
{1 SQLITE_IOERR_FSYNC}
|
||||
{1 {SQLITE_ERROR - SQL logic error or missing database}}
|
||||
{1 {SQLITE_ERROR - SQL logic error}}
|
||||
{1 {SQLITE_ERROR - unable to open database: rbu.db}}
|
||||
{1 {SQLITE_IOERR - unable to open database: rbu.db}}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ foreach {fault errlist} {
|
||||
{1 SQLITE_IOERR_READ}
|
||||
{1 {SQLITE_IOERR - unable to open database: test.db2}}
|
||||
{1 {SQLITE_ERROR - unable to open database: test.db2}}
|
||||
{1 {SQLITE_ERROR - SQL logic error or missing database}}
|
||||
{1 {SQLITE_ERROR - SQL logic error}}
|
||||
}
|
||||
|
||||
cantopen* {
|
||||
|
||||
+2
-2
@@ -119,14 +119,14 @@ do_test 3.2 {
|
||||
CREATE TABLE data_ft(x, rbu_rowid, rbu_control);
|
||||
INSERT INTO data_ft VALUES(NULL, 2, 1);
|
||||
} } msg] $msg]
|
||||
} {1 {SQLITE_ERROR - SQL logic error or missing database]}}
|
||||
} {1 {SQLITE_ERROR - SQL logic error]}}
|
||||
|
||||
do_test 3.3 {
|
||||
list [catch { apply_rbu_update test.db {
|
||||
CREATE TABLE data_ft(x, rbu_rowid, rbu_control);
|
||||
INSERT INTO data_ft VALUES('7 8 9', 1, 'x');
|
||||
} } msg] $msg]
|
||||
} {1 {SQLITE_ERROR - SQL logic error or missing database]}}
|
||||
} {1 {SQLITE_ERROR - SQL logic error]}}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4610,7 +4610,9 @@ static int rbuVfsAccess(
|
||||
if( *pResOut ){
|
||||
rc = SQLITE_CANTOPEN;
|
||||
}else{
|
||||
*pResOut = 1;
|
||||
sqlite3_int64 sz = 0;
|
||||
rc = rbuVfsFileSize(&pDb->base, &sz);
|
||||
*pResOut = (sz>0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -308,7 +308,7 @@ typedef struct sqlite3rbu sqlite3rbu;
|
||||
** not work out of the box with zipvfs. Refer to the comment describing
|
||||
** the zipvfs_create_vfs() API below for details on using RBU with zipvfs.
|
||||
*/
|
||||
sqlite3rbu *sqlite3rbu_open(
|
||||
SQLITE_API sqlite3rbu *sqlite3rbu_open(
|
||||
const char *zTarget,
|
||||
const char *zRbu,
|
||||
const char *zState
|
||||
@@ -347,7 +347,7 @@ sqlite3rbu *sqlite3rbu_open(
|
||||
** a description of the complications associated with using RBU with
|
||||
** zipvfs databases.
|
||||
*/
|
||||
sqlite3rbu *sqlite3rbu_vacuum(
|
||||
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
|
||||
const char *zTarget,
|
||||
const char *zState
|
||||
);
|
||||
@@ -383,7 +383,7 @@ sqlite3rbu *sqlite3rbu_vacuum(
|
||||
** Database handles returned by this function remain valid until the next
|
||||
** call to any sqlite3rbu_xxx() function other than sqlite3rbu_db().
|
||||
*/
|
||||
sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
|
||||
SQLITE_API sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
|
||||
|
||||
/*
|
||||
** Do some work towards applying the RBU update to the target db.
|
||||
@@ -397,7 +397,7 @@ sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
|
||||
** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops
|
||||
** that immediately return the same value.
|
||||
*/
|
||||
int sqlite3rbu_step(sqlite3rbu *pRbu);
|
||||
SQLITE_API int sqlite3rbu_step(sqlite3rbu *pRbu);
|
||||
|
||||
/*
|
||||
** Force RBU to save its state to disk.
|
||||
@@ -409,7 +409,7 @@ int sqlite3rbu_step(sqlite3rbu *pRbu);
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
||||
*/
|
||||
int sqlite3rbu_savestate(sqlite3rbu *pRbu);
|
||||
SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);
|
||||
|
||||
/*
|
||||
** Close an RBU handle.
|
||||
@@ -429,14 +429,14 @@ int sqlite3rbu_savestate(sqlite3rbu *pRbu);
|
||||
** update has been partially applied, or SQLITE_DONE if it has been
|
||||
** completely applied.
|
||||
*/
|
||||
int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
|
||||
SQLITE_API int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
|
||||
|
||||
/*
|
||||
** Return the total number of key-value operations (inserts, deletes or
|
||||
** updates) that have been performed on the target database since the
|
||||
** current RBU update was started.
|
||||
*/
|
||||
sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
|
||||
SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
|
||||
|
||||
/*
|
||||
** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100)
|
||||
@@ -478,7 +478,7 @@ sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
|
||||
** table exists but is not correctly populated, the value of the *pnOne
|
||||
** output variable during stage 1 is undefined.
|
||||
*/
|
||||
void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
|
||||
SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
|
||||
|
||||
/*
|
||||
** Obtain an indication as to the current stage of an RBU update or vacuum.
|
||||
@@ -516,7 +516,7 @@ void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
|
||||
#define SQLITE_RBU_STATE_DONE 4
|
||||
#define SQLITE_RBU_STATE_ERROR 5
|
||||
|
||||
int sqlite3rbu_state(sqlite3rbu *pRbu);
|
||||
SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
|
||||
|
||||
/*
|
||||
** Create an RBU VFS named zName that accesses the underlying file-system
|
||||
@@ -560,7 +560,7 @@ int sqlite3rbu_state(sqlite3rbu *pRbu);
|
||||
** file-system via "rbu" all the time, even if it only uses RBU functionality
|
||||
** occasionally.
|
||||
*/
|
||||
int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
|
||||
SQLITE_API int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
|
||||
|
||||
/*
|
||||
** Deregister and destroy an RBU vfs created by an earlier call to
|
||||
@@ -570,7 +570,7 @@ int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
|
||||
** before all database handles that use it have been closed, the results
|
||||
** are undefined.
|
||||
*/
|
||||
void sqlite3rbu_destroy_vfs(const char *zName);
|
||||
SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
|
||||
@@ -228,6 +228,10 @@ do_execsql_test rtreeA-7.100 {
|
||||
do_catchsql_test rtreeA-7.110 {
|
||||
SELECT * FROM t1 WHERE x1>0 AND x1<100 AND x2>0 AND x2<100;
|
||||
} {1 {undersize RTree blobs in "t1_node"}}
|
||||
do_test rtreeA-7.120 {
|
||||
sqlite3_extended_errcode db
|
||||
} {SQLITE_CORRUPT}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
C Version\s3.20.1
|
||||
D 2017-08-24T16:21:36.477
|
||||
C Fix\sa\stypo\sin\scsv01.test\sintroduced\sby\sthe\sprevious\scommit.
|
||||
D 2017-08-10T19:12:04.643
|
||||
F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016
|
||||
F README.md f5c87359573c4d255425e588a56554b50fdcc2afba4e017a2e02a43701456afd
|
||||
F VERSION 2fd9e47664e2bdb2b12a5fcced76c6768d35cd2730c4d8d5948638d6e1697176
|
||||
F VERSION f81232df28e2d3ff049feefad5fbd5489cc33697f6bd2ecf61af7f0dde3b83d0
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
@@ -30,7 +30,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure 2158ca3525085497feff8aa56386ee451bb0ebe49ef5d5fde3ac00e255fbc808 x
|
||||
F configure 2500b432572804093c9a2516e2b16dbcec5d797ea1cd654915cfecd1d7a39ee3 x
|
||||
F configure.ac 13f45f02e6c51dd0e347315b5401c3f047712b7f79b7f35619115c23755afcff
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html 1f8b8d4c9f5cfe40e679fee279cc9eb2da8e6eb74ad406028538d7864cc4b6cb
|
||||
@@ -249,10 +249,10 @@ F ext/lsm1/lsm_str.c 65e361b488c87b10bf3e5c0070b14ffc602cf84f094880bece77bbf6678
|
||||
F ext/lsm1/lsm_tree.c 682679d7ef2b8b6f2fe77aeb532c8d29695bca671c220b0abac77069de5fb9fb
|
||||
F ext/lsm1/lsm_unix.c 57361bcf5b1a1a028f5d66571ee490e9064d2cfb145a2cc9e5ddade467bb551b
|
||||
F ext/lsm1/lsm_varint.c 43f954af668a66c7928b81597c14d6ad4be9fedbc276bbd80f52fa28a02fdb62
|
||||
F ext/lsm1/lsm_vtab.c d5af32abe32601b3f2618b9488225db9ca06af803ddee1aaaf1653e08e9d112b
|
||||
F ext/lsm1/lsm_vtab.c 976ded75b78be59fc358cf5c06641cda01a4bd0b52374419ab36bf37f646d5df
|
||||
F ext/lsm1/lsm_win32.c 0a4acbd7e8d136dd3a5753f0a9e7a9802263a9d96cef3278cf120bcaa724db7c
|
||||
F ext/lsm1/test/lsm1_common.tcl 5ed4bab07c93be2e4f300ebe46007ecf4b3e20bc5fbe1dedaf04a8774a6d8d82
|
||||
F ext/lsm1/test/lsm1_simple.test 3bb38951450cd1f12a6c294949334d6fbb109a3da38c48eaf0877a37c43a0fab
|
||||
F ext/lsm1/test/lsm1_simple.test 0ca15907fbfe09b6606f93149416786709e0cd55a69ecf53f605fadefa64228c
|
||||
F ext/misc/README.md 8e008c8d2b02e09096b31dfba033253ac27c6c06a18aa5826e299fa7601d90b2
|
||||
F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87
|
||||
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
|
||||
@@ -260,7 +260,7 @@ F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c
|
||||
F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
|
||||
F ext/misc/completion.c 52c3f01523e3e387eb321b4739a89d1fe47cbe6025aa1f2d8d3685e9e365df0f
|
||||
F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
|
||||
F ext/misc/csv.c d91c0388445b08f6e373dd0e8fc024d4551b1fcaf64e876a1c3f4fac8a63adc2
|
||||
F ext/misc/csv.c b10ea114cf29f446e384064ae61e01373b431f6f172896f41ca0ba3507709003
|
||||
F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e
|
||||
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
|
||||
F ext/misc/fileio.c b1aa06c0f1dac277695d4529e5e976c65ab5678dcbb53a0304deaa8adc44b332
|
||||
@@ -281,14 +281,15 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c a4723b6aff748a417b5091b68a46443265c40f0d
|
||||
F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
|
||||
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
|
||||
F ext/misc/unionvtab.c 56fd163d2b6d2f4df0078be482fc9a874658ce51cce33f180c08834193449c78
|
||||
F ext/misc/unionvtab.c 1e0ebc5078e1a916db191bcd88f87e94ea7ba4aa563ee30ff706261cb4b39461
|
||||
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
||||
F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
|
||||
F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
|
||||
F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
|
||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||
F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
|
||||
F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
|
||||
F ext/rbu/rbu10.test 046b0980041d30700464a800bbf6733ed2df515d
|
||||
F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee
|
||||
F ext/rbu/rbu11.test 9bc68c2d3dbeb1720153626e3bd0466dcc017702
|
||||
F ext/rbu/rbu12.test bde22ed0004dd5d1888c72a84ae407e574aeae16
|
||||
F ext/rbu/rbu13.test 462ff799c4afedc3ef8a47ff818c0ffbf14ae4f2
|
||||
@@ -307,18 +308,18 @@ F ext/rbu/rbucrash.test 61470d977a06a0abc2ec35b05d82a1d7d87d10f4ffabad14c1c231ed
|
||||
F ext/rbu/rbucrash2.test b2ecbdd7bb72c88bd217c65bd00dafa07f7f2d4d
|
||||
F ext/rbu/rbudiff.test 3e605cf624d00d04d0fb1316a3acec4fbe3b3ac5
|
||||
F ext/rbu/rbudor.test 99b05cc0df613e962c2c8085cfb05686a09cf315
|
||||
F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
|
||||
F ext/rbu/rbufault.test 2654aef20f8ee7de37c9c1997a44f2773dc7bf24887adea39fb19314ef32cb90
|
||||
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
|
||||
F ext/rbu/rbufault3.test 54a399888ac4af44c68f9f58afbed23149428bca
|
||||
F ext/rbu/rbufault3.test 0913c1aeaee266d9c36c33179341a5a504aad7d423d1979cfec43c8346a29899
|
||||
F ext/rbu/rbufault4.test 34e70701cbec51571ffbd9fbf9d4e0f2ec495ca7
|
||||
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
|
||||
F ext/rbu/rbufts.test a2bbd202c9321fba15fb4a62a90add7d70e07bd8404e1e598135adbfff8a0508
|
||||
F ext/rbu/rbuprogress.test 1849d4e0e50616edf5ce75ce7db86622e656b5cf
|
||||
F ext/rbu/rburesume.test 8acb77f4a422ff55acfcfc9cc15a5cb210b1de83
|
||||
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
|
||||
F ext/rbu/rbuvacuum.test ff357e9b556ca7ad4673da0ff7f244def919ff858e0f9f350d3e30fdd83a62a8
|
||||
F ext/rbu/rbuvacuum2.test 2074ab14fe66e1c7e7210c62562650dcd215bbaa
|
||||
F ext/rbu/sqlite3rbu.c d1438580a451eebda3bfd42ef69b677512f00125285e0e4e789b6131a45f6dd8
|
||||
F ext/rbu/sqlite3rbu.h fc25e1fcd99b5c6d32b1b5b1c73122632e873ac89bd0be9bf646db362b7ce02c
|
||||
F ext/rbu/sqlite3rbu.c 920941a6ff7dbbea0970717c43662878fda5c37e43752de329f0fdd76680ab75
|
||||
F ext/rbu/sqlite3rbu.h 82c102e5ae41025e3b245d3d5944315f82811da85e2cd363a75caa97cbd0cd3e
|
||||
F ext/rbu/test_rbu.c ec18cfc69a104309df23c359e3c80306c9a6bdd1d2c53c8b70ae158e9832dcd6
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 4f1804b80ae06ddf7ff69192aacdceee283646dc6a328acb951f116147445212
|
||||
@@ -332,7 +333,7 @@ F ext/rtree/rtree6.test 773a90db2dce6a8353dd0d5b64bca69b29761196
|
||||
F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971
|
||||
F ext/rtree/rtree8.test 076d9d5b783b61b7a23a5ab45fc899551dfffd821974f36ee599ff29f4de7a61
|
||||
F ext/rtree/rtree9.test 8bfa84dfaba1c897468a2448c28db0a00ad12d464225b5993c7814e907f3776f
|
||||
F ext/rtree/rtreeA.test e25d76c1701f8591e7a0b6de8224d5dbc1418c562654c7240e6f33f37b1e36f7
|
||||
F ext/rtree/rtreeA.test abb1e2434defc8cdc5e3195a18ead3681cae04565c06069251d1998796e77d55
|
||||
F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
|
||||
F ext/rtree/rtreeC.test c0a9c67f2efa98b6fae12acb8a28348d231a481d
|
||||
F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc
|
||||
@@ -388,43 +389,43 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 850ede4e607f12fa25ea4f3cb6ece2b2e29d1aa50e3f786ce49d615788849552
|
||||
F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594
|
||||
F src/analyze.c 0d0ccf7520a201d8747ea2f02c92c26e26f801bc161f714f27b9f7630dde0421
|
||||
F src/attach.c 3bd555e28382603e80d430dfebb2270f86e1e375b4c4be3e1ab1aec3a0c44943
|
||||
F src/attach.c 07b706e336fd3cedbd855e1f8266d10e82fecae07daf86717b5760cd7784c584
|
||||
F src/auth.c 79f96c6f33bf0e5da8d1c282cee5ebb1852bb8a6ccca3e485d7c459b035d9c3c
|
||||
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
|
||||
F src/btree.c f55ea8f456d103328d61076be40fa39acbfea05eaa4eccfed275532a63c867c4
|
||||
F src/btree.c 1a17ba1a765d80c3ca39ce33ff55f92e1f51eb84bbbdab5377f11d36b1515fa1
|
||||
F src/btree.h 3edc5329bc59534d2d15b4f069a9f54b779a7e51289e98fa481ae3c0e526a5ca
|
||||
F src/btreeInt.h 97700795edf8a43245720414798b7b29d8e465aef46bf301ffacd431910c0da1
|
||||
F src/build.c c3837ad57cea1351cd824a646836d2bb678f264ae160d691b632d495c8be2f5b
|
||||
F src/callback.c 8e14b60d1ed1c87c02cb5f121ecda99224f2aea6524a77ee6f72c9b5c7110f84
|
||||
F src/build.c 33b0f6055bd990ed052b96e71368acefcd98daa21ccf21f91aa90e8b769c2219
|
||||
F src/callback.c 930648a084a3adc741c6471adfbdc50ba47ba3542421cb80a26f259f467de65e
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 928954802b1397d9fb1378c7eb702c94b4735bbab1d5793e21b6a77734f56a1b
|
||||
F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0
|
||||
F src/date.c 48f743d88bbe88f848532d333cca84f26e52a4f217e86f86be7fc1b919c33d74
|
||||
F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
|
||||
F src/delete.c 939bd15e6b54b82b951e1c0ffc2ff2b4ab579196780a1f6d394e47bd6f799b6c
|
||||
F src/delete.c 21a5f1812fdb599e9f7afb9f650bdabab60a3afd51d7e94e539c982f647b0023
|
||||
F src/expr.c fdb2fc465cabbf372fecad1fc2b291758bec74150b4db0fb945332e09df28a0e
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 5ff2c895fe087756d8085dc1a9bc229b5670e2a65c3929dd87c71e43649af333
|
||||
F src/func.c e2854b19386b93ad6b498a3f3b7d6baa98ec14cfe84530fb12fce4414263d871
|
||||
F src/func.c ed8888ae80b39f5a5d403954e4a05e0a38303523dff8143161439c142d31dec1
|
||||
F src/global.c 8a6ab6b4d91effb96ffa81b39f0d70c862abca157f8aaa194600a4a8b7923344
|
||||
F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
|
||||
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
|
||||
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 487d2c1cd8d549f8d9492377ecc4d2f507b1bd5185e96c54389f6b607dce854c
|
||||
F src/insert.c d2d1bf12d2b5382450620d7cede84c7ffe57e6a89fa9a908f1aba68df2731cd9
|
||||
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
|
||||
F src/loadext.c 20865b183bb8a3723d59cf1efffc3c50217eb452c1021d077b908c94da26b0b2
|
||||
F src/main.c 3a9da9e3974d8a32ef6ca15b75503d540af22d284beb75bc7f0d93254ca3f8f7
|
||||
F src/main.c 42f6a2660c7a1d643cc7e863d2dcd630c6aa1e8343f5478b0592120ab84c97ba
|
||||
F src/malloc.c e20bb2b48abec52d3faf01cce12e8b4f95973755fafec98d45162dfdab111978
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
|
||||
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
|
||||
F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a
|
||||
F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
|
||||
F src/memjournal.c 95752936c11dc6995672d1dd783cd633eea0cc95
|
||||
F src/memjournal.c 6f3d36a0a8f72f48f6c3c722f04301ac64f2515435fa42924293e46fc7994661
|
||||
F src/msvc.h 4942752b6a253116baaa8de75256c51a459a5e81
|
||||
F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c
|
||||
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
|
||||
@@ -436,39 +437,39 @@ F src/os.c add02933b1dce7a39a005b00a2f5364b763e9a24
|
||||
F src/os.h 8e976e59eb4ca1c0fca6d35ee803e38951cb0343
|
||||
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
|
||||
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
|
||||
F src/os_unix.c 30e2c43e4955db990e5b5a81e901f8aa74cc8820
|
||||
F src/os_win.c 2a6c73eef01c51a048cc4ddccd57f981afbec18a
|
||||
F src/os_unix.c a361273749229755f92c8f0e3e4855054ad39bbc5c65773e8db5d0b79afa632c
|
||||
F src/os_win.c 964165b66cde03abc72fe948198b01be608436894732eadb94c8720d2467f223
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 14f6982c470c05b8e85575c69e9c1712010602e20400f8670d8699e21283e0e4
|
||||
F src/pager.c 1e63b0299cf123cf38c48413ec03190f56c1e7d0ccc6573c467d8ac240b898e9
|
||||
F src/pager.h f2a99646c5533ffe11afa43e9e0bea74054e4efa
|
||||
F src/parse.y e384cb73f99e1b074085c974b37f4d830e885359e4b60837e30f7d67c16ba65b
|
||||
F src/parse.y 52ef3cecd0934e9da4a45b585883a03243ad615d338ad94f44501a05891dcdfa
|
||||
F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870
|
||||
F src/pcache.h 521bb9610d38ef17a3cc9b5ddafd4546c2ea67fa3d0e464823d73c2a28d50e11
|
||||
F src/pcache1.c 1195a21fe28e223e024f900b2011e80df53793f0356a24caace4188b098540dc
|
||||
F src/pcache1.c 0b793738b5dddaf0a645784835c6b5557b1ecfaee339af9c26810c6ecdb273aa
|
||||
F src/pragma.c cd6aeda3587be6c5c08f9b2d45eae6068666a03c9d077c8c43cdb85fb0aa70f2
|
||||
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
|
||||
F src/prepare.c dd250f904739b1dc449c131ac527c35e3424d94082dd111321bd83f80c6bb0fe
|
||||
F src/prepare.c 3cbb99757d7295997674972f9dd2331c5c544368854ca08954c9beb1e9b6145a
|
||||
F src/printf.c 8757834f1b54dae512fb25eb1acc8e94a0d15dd2290b58f2563f65973265adb2
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 4324a94573b1e29286f8121e4881db59eaedc014afeb274c8d3e07ed282e0e20
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 31b35ddf55f1021f7148a01306984b057c11ebb6e3463d94677225e0a1e301a3
|
||||
F src/select.c 3fd19c98c5223d411b883502d1ac928ddb762a1ea8f031d910210316545fc67c
|
||||
F src/shell.c bd6a37cbe8bf64ef6a6a74fdc50f067d3148149b4ce2b4d03154663e66ded55f
|
||||
F src/shell.c.in b5725acacba95ccefa57b6d068f710e29ba8239c3aa704628a1902a1f729c175
|
||||
F src/sqlite.h.in a7e29d5977916cded93ff6cf0d56ada657e13e91adbc2039e5cf02e41122a45f
|
||||
F src/sqlite.h.in 72f1775c7a134f9e358eedafe1ebc703c28b0d705d976464ddbf6a9219448952
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 0f9f72b86a3792314f5db7a1dfbc2c82376bcd8d0919ceb80637bca126ec3c68
|
||||
F src/sqliteInt.h bd6be75bc43d38ada272ef0b3472bc44ef0cc15536bea22c349ca1a2812a19ce
|
||||
F src/sqlite3ext.h a1fd3aa82f967da436164e0728a7d6841651fd0c6e27b9044e0eb9f6c8462e47
|
||||
F src/sqliteInt.h 07e4d3c8021aea80e3bbafab4dd52833cfcfa4f000210af0d15c7fdaed2f09fc
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
F src/tclsqlite.c 2c29b0b76e91edfd1b43bf135c32c8674710089197327682b6b7e6af88062c3d
|
||||
F src/test1.c cfb78b728b37ae3a2b14fe1b3a6c766e0da41370eda112594e698c94011b622e
|
||||
F src/tclsqlite.c 487951d81f9704800fd9f0ffdaa2f935a83ccb6be3575c2c4ef83e4789b4c828
|
||||
F src/test1.c 8513b17ca4a7a9ba28748535d178b6e472ec7394ae0eea53907f2d3bcdbab2df
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
|
||||
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
|
||||
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
|
||||
F src/test6.c 004ad42f121f693b8cbe060d1a330678abc61620
|
||||
F src/test6.c e8d839fbc552ce044bec8234561a2d5b8819b48e29548ad0ba400471697946a8
|
||||
F src/test7.c 5612e9aecf934d6df7bba6ce861fdf5ba5456010
|
||||
F src/test8.c 4f4904721167b32f7a4fa8c7b32a07a673d6cc86
|
||||
F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5
|
||||
@@ -481,7 +482,7 @@ F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
|
||||
F src/test_config.c abf6fc1fe9d041b699578c42e3db81f8831c4f5b804f1927958102ee8f2b773e
|
||||
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
|
||||
F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e
|
||||
F src/test_devsym.c 4e58dec2602d8e139ca08659f62a62450587cb58
|
||||
F src/test_devsym.c 1960abbb234b97e9b920f07e99503fc04b443f62bbc3c6ff2c2cea2133e3b8a2
|
||||
F src/test_fs.c 35a2f7dd8a915900873386331386d9ba1ae1b5026d74fd20c2807bc76221f291
|
||||
F src/test_func.c a4fdab3363b436c1b12660e9362ce3f3782b7b5e
|
||||
F src/test_hexio.c 1d4469ca61ab202a1fcec6543f584d2407205e8d
|
||||
@@ -505,7 +506,7 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
|
||||
F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef
|
||||
F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
|
||||
F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
|
||||
F src/test_tclvar.c df9fe1213c2634687a9ca0b0bec0d2119d359ae3
|
||||
F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc
|
||||
F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
|
||||
F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
@@ -515,29 +516,29 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
|
||||
F src/treeview.c 2ee4a5dada213d5ab08a742af5c876cee6f1aaae65f10a61923f3fb63846afef
|
||||
F src/trigger.c d1cae560bfacc8bfb3a072d73658245c1714c0389097da69b4cb23877a082d7e
|
||||
F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc
|
||||
F src/trigger.c 48e0f7ed6749ce4d50a695e09e20ce9cf84ecabf2691852c965a51e0b620eccc
|
||||
F src/update.c 5404be9e840717323a69209190cdbc9d0d34adaedaaf1d1a1069babf2c4171c0
|
||||
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
F src/util.c fc081ec6f63448dcd80d3dfad35baecfa104823254a815b081a4d9fe76e1db23
|
||||
F src/vacuum.c 874c0f2f15ab2908748297d587d22d485ea96d55aaec91d4775dddb2e24d2ecf
|
||||
F src/vdbe.c 1e541ec7ff409bbabcc6b4f154957296fff5827c16c2ab0056348acae75685bf
|
||||
F src/vacuum.c 90839322fd5f00df9617eb21b68beda9b6e2a2937576b0d65985e4aeb1c53739
|
||||
F src/vdbe.c 821b3edde2d17ec60da0617db1018a88f38634c359d22f3c8f7be10336c82636
|
||||
F src/vdbe.h d50cadf12bcf9fb99117ef392ce1ea283aa429270481426b6e8b0280c101fd97
|
||||
F src/vdbeInt.h ff2b7db0968d20e6184aee256d2e535d565f5a172e3588a78adb166a41fc4911
|
||||
F src/vdbeapi.c d362429ebb02df609a71a24abb295406b47252a3caa231d0bd0d715033b5d362
|
||||
F src/vdbeaux.c 3fe68bad02b33b09e08bdc0ad90d6b92b3d571f7864c3d047abca1bde050751c
|
||||
F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
|
||||
F src/vdbemem.c 9ca2854976f35db40341977e688a08204c96427505f5b90215dc7970f6ea42c4
|
||||
F src/vdbesort.c f512c68d0bf7e0105316a5594c4329358c8ee9cae3b25138df041d97516c0372
|
||||
F src/vdbeapi.c 05d6b14ab73952db0d73f6452d6960216997bd966a710266b2fe051f25326abc
|
||||
F src/vdbeaux.c 1bef372f59f9e1dba5ead70cc5c24bf978bab0b9fdc2f69692afaa3a2d4dd8f3
|
||||
F src/vdbeblob.c db3cf91060f6f4b2f1358a4200e844697990752177784c7c95da00b7ac9f1c7b
|
||||
F src/vdbemem.c b7fac20534c79b7554dab2e8a180c585a8bc1b9c85149d1b2d9746cf314d06ed
|
||||
F src/vdbesort.c fea2bea25f5e9ccd91e0760d7359f0365f9fba1aaeac7216c71cad78765f58e3
|
||||
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
|
||||
F src/vtab.c 35b9bdc2b41de32a417141d12097bcc4e29a77ed7cdb8f836d1d2305d946b61b
|
||||
F src/vtab.c f1d5c23132fb0247af3e86146404112283ddedb6c518de0d4edc91cfb36970ef
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
|
||||
F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
|
||||
F src/walker.c a7ca64ce08a83a20d32186fbe06bca9234e348cfcf07959ee322fdc3e8a6173a
|
||||
F src/where.c cbe8ddffbcec7ce86f7a800fe8fd10aee412c76c87e0dd3732a1682e68d74cd9
|
||||
F src/whereInt.h 93bb90b77d39901eda31b44d8e90da1351193ccfe96876f89b58a93a33b84c3d
|
||||
F src/wherecode.c c0c4c31573486cd14bb2cbfc63e41eda591609e5190416261999f211bf97abc1
|
||||
F src/whereexpr.c bf983d2d33e325cd63a36c40b8de289fd3d7b4d9f2db9052fb8f59f7161a34a0
|
||||
F src/wherecode.c e7be3b7f4c11908500cdf02b299d190d3742659533f58e0f4047962fdb5a48da
|
||||
F src/whereexpr.c 1e55d79174522fe3b9b7ab224ebedc03da6ec5e6d204d740fa73e71280f54574
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
|
||||
@@ -572,8 +573,9 @@ F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
|
||||
F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a
|
||||
F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0
|
||||
F test/atof1.test ff0b0156fd705b67c506e1f2bfe9e26102bea9bd
|
||||
F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061
|
||||
F test/attach.test f4b8918ba2f3e88e6883b8452340545f10a1388af808343c37fc5c577be8281c
|
||||
F test/attach2.test 0ec5defa340363de6cd50fd595046465e9aaba2d
|
||||
F test/attach2.test 567047a7607aae8ebb3794642ebb168abe66b4af366fcd0cf7f616a1495cd43f
|
||||
F test/attach3.test c59d92791070c59272e00183b7353eeb94915976
|
||||
F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c
|
||||
F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0
|
||||
@@ -607,6 +609,7 @@ F test/bestindex4.test 4cb5ff7dbaebadb87d366f51969271778423b455
|
||||
F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
|
||||
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
|
||||
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
|
||||
F test/bigmmap.test ed6058a7794be26865c94d5bb62e12cdc4f7f01562b3b04f13eb3cdc52783921
|
||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||
F test/bigsort.test 8299fa9298f4f1e02fc7d2712e8b77d6cd60e5a2
|
||||
F test/bind.test 1e136709b306f7ed3192d349c2930d89df6ab621654ad6f1a72381d3fe76f483
|
||||
@@ -692,7 +695,7 @@ F test/crashM.test d95f59046fa749b0d0822edf18a717788c8f318d
|
||||
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
|
||||
F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
|
||||
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/csv01.test e0ba3caaa57e4c667a0b45977689fb8082f14348
|
||||
F test/csv01.test 526fc6aefd052badd5a0283f86b4b395c3df76bfe98d96c801f494f5e2c7836c
|
||||
F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807f3
|
||||
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
|
||||
F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b
|
||||
@@ -754,7 +757,7 @@ F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
|
||||
F test/expr.test 66a2c9ac34f74f036faa4092f5402c7d3162fc93
|
||||
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
|
||||
F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
|
||||
F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7
|
||||
F test/fallocate.test 87b5e43c872b7e69cd80b7b8813eb102b571a75d45dda24e38b65537bcc85733
|
||||
F test/filectrl.test 6e871c2d35dead1d9a88e176e8d2ca094fec6bb3
|
||||
F test/filefmt.test f393e80c4b8d493b7a7f8f3809a8425bbf4292af1f5140f01cb1427798a2bbd4
|
||||
F test/fkey1.test ba64806ff9a04eecab2679caad377ae99a5e94e4
|
||||
@@ -978,7 +981,7 @@ F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
|
||||
F test/kvtest.c d2b8cfc91047ebf6cac4f3a04f19c3a864e4ecfd683bbb65c395df450b8dc79c
|
||||
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
|
||||
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
|
||||
F test/like.test 3d26ae14d7042a0e96f7b0b9e9ad2c8ca6ed122772439c6b1c691fe167e15a37
|
||||
F test/like.test 67d7431c9b664254febce9e90fd2f47c7c75c8b38444e2a50ef9ec2776b84ca8
|
||||
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
|
||||
F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4
|
||||
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
|
||||
@@ -1029,7 +1032,7 @@ F test/minmax.test 6751e87b409fe11b02e70a306d846fa544e25a41
|
||||
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f
|
||||
F test/misc1.test 6430dabfb4b4fa480633590118964201f94d3ccc
|
||||
F test/misc1.test 51ec3f56a2a7965de226964cff856695e743186826561536647f1e8b7d1d0eb3
|
||||
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
|
||||
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
|
||||
F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
|
||||
@@ -1071,7 +1074,7 @@ F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
|
||||
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
|
||||
F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3
|
||||
F test/oserror.test b32dc34f2363ef18532e3a0a7358e3e7e321974f
|
||||
F test/ossfuzz.c f5abed3177f719df3c3109901fcdd26b9fb7f581c8da50fc26f3a81ddfb2c2ae
|
||||
F test/ossfuzz.c 7f5cc87a0280a5854c1bfa7d5c4d07d34731f08ec34dc9c916aa35ed292b1468
|
||||
F test/ossshell.c 296ab63067841bd1b1e97b46a0b2af48ee7f69d50d1a723008bee12dd7122622
|
||||
F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f
|
||||
F test/pager1.test 8149b2a8986fee667ab6a8171ab310be19e77ae215bebad0e90c857b0df1935c
|
||||
@@ -1087,7 +1090,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
|
||||
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
|
||||
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
|
||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||
F test/permutations.test 5e2e5439642898e0947ced066ad09b82bd817ddfb83dc71291b4c957efc84b62
|
||||
F test/permutations.test 3b94f8fd431d39fac4952eb5dc38e1bb2b4518e1ac967d66f5abc815c104aeb6
|
||||
F test/pragma.test f274259d6393b6681eb433beb8dd39a26ec06a4431052a4880b43b84912a3f58
|
||||
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
|
||||
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
|
||||
@@ -1113,7 +1116,7 @@ F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.tcl 7bb585433ce7fb2a2c255ae4b5e24f1bc27fe177ec1120f886cc4852f48f5ee9 x
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
|
||||
F test/rollback.test f580934279800d480a19176c6b44909df31ce7ad45267ea475a541daa522f3d3
|
||||
F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5
|
||||
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||
@@ -1131,7 +1134,7 @@ F test/rowvalue9.test d8dd2c6ecac432dadaa79e41dc2434f007be1b6b
|
||||
F test/rowvaluefault.test 7cd9ccc6c2fbdd881672984087aad0491bb75504
|
||||
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
||||
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
|
||||
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
|
||||
F test/savepoint.test 1f8a6b1aea9a0d05837adc463d4bf47bd9d0f1c842f1c2a9caccd639baf34bf9
|
||||
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
|
||||
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
|
||||
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
|
||||
@@ -1144,6 +1147,7 @@ F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
|
||||
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
|
||||
F test/schema4.test 3b26c9fa916abb6dadf894137adcf41b7796f7b9
|
||||
F test/schema5.test 29699b4421f183c8f0e88bd28ce7d75d13ea653e
|
||||
F test/schema6.test 5b21bbdd405bc93b3e6af5e6ece64d230e35f65cc4035e5c2b89fc8a090d7270
|
||||
F test/securedel.test 5f997cb6bd38727b81e0985f53ec386c99db6441b2b9e6357240649d29017239
|
||||
F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
|
||||
F test/select1.test be62204d2bd9a5a8a149e9974cfddce893d8f686
|
||||
@@ -1229,10 +1233,13 @@ F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
|
||||
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
|
||||
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
|
||||
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
|
||||
F test/swarmvtab.test 05c4ca7b6ab0cc6f4c335a510347f99d741fa71366004699cf7dfa3cff4e2d17
|
||||
F test/swarmvtab2.test 038ef9bcad6fd2fb9e395196080cf23e223ddb1219015049a61540c161bc577d
|
||||
F test/swarmvtabfault.test 73563eefe3073c6fb3bb14475fb4ef5d4f2e3a67a02947ee0ca08980ea3dd7fe
|
||||
F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849
|
||||
F test/sync.test 2f84bdbc2b2df1fcb0220575b4b9f8cea94b7529
|
||||
F test/sync2.test 6be8ed007fa063b147773c1982b5bdba97a32badc536bdc6077eff5cf8710ece
|
||||
F test/syscall.test 7a60601770172a8014a4d222d5f3d95a5d2b5c47fbb0374e2698e89c99e37256
|
||||
F test/syscall.test a39d9a36f852ae6e4800f861bc2f2e83f68bbc2112d9399931ecfadeabd2d69d
|
||||
F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
|
||||
F test/tabfunc01.test c47171c36b3d411df2bd49719dcaa5d034f8d277477fd41d253940723b969a51
|
||||
F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f
|
||||
@@ -1246,7 +1253,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 581f0185434daf7026ccede4c07e8d1479186ec5
|
||||
F test/tester.tcl eb7ec55fe074a909423c1de701f7c545417b8aa96787b8c3e7a79203f2cebec8
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1434,7 +1441,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unionvtab.test 595fb601de00188ca5e7c6dbe266c17a0faf234cf434ce85eccec1a929ef9baf
|
||||
F test/unionvtabfault.test 18f4f878e550ae3c5efa195b1420c78607c0f9810f7a71a49ab6379dfe501f32
|
||||
F test/unionvtabfault.test 26b6854d5aef9005cd630513025690bff1b7378ae9c97b81e2a3cbe84eee0f58
|
||||
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
|
||||
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
|
||||
F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
|
||||
@@ -1455,7 +1462,7 @@ F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
||||
F test/view.test 765802c7a66d37fabd5ac8e2f2dbe572b43eb9ab
|
||||
F test/vtab1.test 8f91b9538d1404c3932293a588c4344218a0c94792d4289bb55e41020e7b3fff
|
||||
F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad
|
||||
F test/vtab2.test 14d4ab26cee13ba6cf5c5601b158e4f57552d3b055cdd9406cf7f711e9c84082
|
||||
F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e
|
||||
F test/vtab4.test 8e73ed268f3d596bc3590f45fc948fb40f28e9c3
|
||||
F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391
|
||||
@@ -1467,10 +1474,11 @@ F test/vtabA.test 1317f06a03597eee29f40a49b6c21e1aaba4285f
|
||||
F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796
|
||||
F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
|
||||
F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
|
||||
F test/vtabE.test d5024aa42754962f6bb0afd261681686488e7afe
|
||||
F test/vtabE.test 2a143fe75a11275781d1fd1988d86b66a3f69cb98f4add62e3da8fd0f637b45f
|
||||
F test/vtabF.test 1918844c7c902f6a16c8dacf1ec8f84886d6e78b
|
||||
F test/vtabH.test 26d54e8b5407f797638b787a55f9c88323850a58dd142de02d06b9a1159bd283
|
||||
F test/vtabH.test 3cf9aa1c1c4381b3b3ac33f933376f06fbb99d2294a83c79b7562d3ed87be450
|
||||
F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f
|
||||
F test/vtabJ.test d7b73675708cf63cfcb9d443bb451fc01a028347275b7311e51f9fdf3ca6757f
|
||||
F test/vtab_alter.test 736e66fb5ec7b4fee58229aa3ada2f27ec58bc58c00edae4836890c3784c6783
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
|
||||
@@ -1513,7 +1521,7 @@ F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
F test/where7.test f520bcec2c3d12dc4615623b06b2aec7c2d67e94
|
||||
F test/where8.test 98eedca0d375fb400b8377269c4b4686582dfb45
|
||||
F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2
|
||||
F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
|
||||
F test/whereA.test 6c6a420ca7d313242f9b1bd471dc80e4d0f8323700ba9c78df0bb843d4daa3b4
|
||||
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
|
||||
F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6
|
||||
F test/whereD.test 711d4df58d6d4fb9b3f5ce040b818564198be002
|
||||
@@ -1542,12 +1550,13 @@ F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
|
||||
F test/without_rowid5.test 89b1c587bd92a0590e440da33e7666bf4891572a
|
||||
F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e
|
||||
F test/wordcount.c 06efb84b7c48a4973c2c24ea06c93d00bce24389
|
||||
F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
|
||||
F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
|
||||
F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e
|
||||
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
|
||||
F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d
|
||||
F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
|
||||
F tool/addopcodes.tcl edbd53806bf20e25af2373ad0c091be4385081c1aa1813b916bf093f94ed8380
|
||||
F tool/addopcodes.tcl 7181c041d495e3f26acc36d15c86923ed722285f9015f017f41a3efdb9a0dab4
|
||||
F tool/build-all-msvc.bat c12328d06c45fec8baada5949e3d5af54bf8c887 x
|
||||
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
|
||||
F tool/cg_anno.tcl f95b0006c52cf7f0496b506343415b6ee3cdcdd3 x
|
||||
@@ -1561,7 +1570,7 @@ 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 5a04dff28578a67415cea5bf981b893c50cebfdd4388fb21254d1892525edfd8
|
||||
F tool/lemon.c e6056373044d55296d21f81467dba7632bbb81dc49af072b3f0e76338771497e
|
||||
F tool/lempar.c 10579a61dc2290182725e7abdefe311dd8b521a8f7f0aabbfc571e9012a09eaf
|
||||
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
|
||||
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
|
||||
@@ -1572,13 +1581,13 @@ F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9
|
||||
F tool/mkkeywordhash.c 2e852ac0dfdc5af18886dc1ce7e9676d11714ae3df0a282dc7d90b3a0fe2033c
|
||||
F tool/mkmsvcmin.tcl cbd93f1cfa3a0a9ae56fc958510aa3fc3ac65e29cb111716199e3d0e66eefaa4
|
||||
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
|
||||
F tool/mkopcodeh.tcl bb04ab6e5e2000c91e0c69a597e7e36e002320d123e2e1944cb2819181b72ee9
|
||||
F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 2144bc8550a6471a029db262a132d2df4b9e0db61b90398bf64f5b7b3f8d92cd
|
||||
F tool/mkshellc.tcl 69c38ecd7b74b2b0799a35ce20e1e3998e504d8c99c100ca4b98ae9d8f6279bc
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl fef88397668ae83166735c41af99d79f56afaabb
|
||||
F tool/mksqlite3c.tcl f6214285bec900d28441366ca31af327aade18bbc424b0480497966ec05bc43c
|
||||
F tool/mksqlite3c.tcl a4b36eaa002ed00a0ab2c93d999a14f1acae98ff09a85382e5abc05a91edb82b
|
||||
F tool/mksqlite3h.tcl 51bd5e7e840a920388a5966c9f2ccc618f434c57bd68c1bab4085b2553e1e237
|
||||
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
|
||||
F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
|
||||
@@ -1637,10 +1646,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 70338c7cb128855bb134dddebf04fbcf4c0c36b3127a53b9e24cd9f7fd235905
|
||||
R f28d3a18ae99c0abfb9dbb754ba936b5
|
||||
T +bgcolor * #d0c0ff
|
||||
T +sym-release *
|
||||
T +sym-version-3.20.1 *
|
||||
U drh
|
||||
Z 060abdbd0746413c79f6ad9e0e76e1d3
|
||||
P b0e3b88a792623f7761586d8bfac058ddf939f49944ff0c4376627ff920895db
|
||||
R f6522b1ac79c489b723c146b9fd586f4
|
||||
U dan
|
||||
Z 105e790a02c4f620d8e963aaaf4e7ac3
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
8d3a7ea6c5690d6b7c3767558f4f01b511c55463e3f9e64506801fe9b74dce34
|
||||
f38ff718278c8ff477d1c6379ced32167063de15f0666c8eac2e4dd30cbd975c
|
||||
+7
-7
@@ -403,9 +403,9 @@ void sqlite3AlterRenameTable(
|
||||
char *zWhere = 0; /* Where clause to locate temp triggers */
|
||||
#endif
|
||||
VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */
|
||||
int savedDbFlags; /* Saved value of db->flags */
|
||||
u32 savedDbFlags; /* Saved value of db->mDbFlags */
|
||||
|
||||
savedDbFlags = db->flags;
|
||||
savedDbFlags = db->mDbFlags;
|
||||
if( NEVER(db->mallocFailed) ) goto exit_rename_table;
|
||||
assert( pSrc->nSrc==1 );
|
||||
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
|
||||
@@ -414,7 +414,7 @@ void sqlite3AlterRenameTable(
|
||||
if( !pTab ) goto exit_rename_table;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
db->flags |= SQLITE_PreferBuiltin;
|
||||
db->mDbFlags |= DBFLAG_PreferBuiltin;
|
||||
|
||||
/* Get a NULL terminated version of the new table name. */
|
||||
zName = sqlite3NameFromToken(db, pName);
|
||||
@@ -579,7 +579,7 @@ void sqlite3AlterRenameTable(
|
||||
exit_rename_table:
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
sqlite3DbFree(db, zName);
|
||||
db->flags = savedDbFlags;
|
||||
db->mDbFlags = savedDbFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -680,11 +680,11 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
|
||||
if( zCol ){
|
||||
char *zEnd = &zCol[pColDef->n-1];
|
||||
int savedDbFlags = db->flags;
|
||||
u32 savedDbFlags = db->mDbFlags;
|
||||
while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
|
||||
*zEnd-- = '\0';
|
||||
}
|
||||
db->flags |= SQLITE_PreferBuiltin;
|
||||
db->mDbFlags |= DBFLAG_PreferBuiltin;
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\".%s SET "
|
||||
"sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
|
||||
@@ -693,7 +693,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
zTab
|
||||
);
|
||||
sqlite3DbFree(db, zCol);
|
||||
db->flags = savedDbFlags;
|
||||
db->mDbFlags = savedDbFlags;
|
||||
}
|
||||
|
||||
/* Make sure the schema version is at least 3. But do not upgrade
|
||||
|
||||
@@ -93,10 +93,6 @@ static void attachFunc(
|
||||
);
|
||||
goto attach_error;
|
||||
}
|
||||
if( !db->autoCommit ){
|
||||
zErrDyn = sqlite3MPrintf(db, "cannot ATTACH database within transaction");
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zDbSName;
|
||||
assert( z && zName );
|
||||
@@ -288,11 +284,6 @@ static void detachFunc(
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);
|
||||
goto detach_error;
|
||||
}
|
||||
if( !db->autoCommit ){
|
||||
sqlite3_snprintf(sizeof(zErr), zErr,
|
||||
"cannot DETACH database within transaction");
|
||||
goto detach_error;
|
||||
}
|
||||
if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){
|
||||
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
|
||||
goto detach_error;
|
||||
|
||||
+16
-17
@@ -731,7 +731,7 @@ static int SQLITE_NOINLINE saveCursorsOnList(
|
||||
return rc;
|
||||
}
|
||||
}else{
|
||||
testcase( p->iPage>0 );
|
||||
testcase( p->iPage>=0 );
|
||||
btreeReleaseAllCursorPages(p);
|
||||
}
|
||||
}
|
||||
@@ -3981,7 +3981,6 @@ int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
|
||||
if( pBtree ){
|
||||
sqlite3BtreeEnter(pBtree);
|
||||
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
|
||||
int i;
|
||||
if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
|
||||
if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){
|
||||
rc = saveCursorPosition(p);
|
||||
@@ -3995,10 +3994,7 @@ int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
|
||||
p->eState = CURSOR_FAULT;
|
||||
p->skipNext = errCode;
|
||||
}
|
||||
for(i=0; i<=p->iPage; i++){
|
||||
releasePage(p->apPage[i]);
|
||||
p->apPage[i] = 0;
|
||||
}
|
||||
btreeReleaseAllCursorPages(p);
|
||||
}
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
}
|
||||
@@ -4315,7 +4311,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
|
||||
}while( ALWAYS(pPrev) );
|
||||
}
|
||||
for(i=0; i<=pCur->iPage; i++){
|
||||
releasePage(pCur->apPage[i]);
|
||||
releasePageNotNull(pCur->apPage[i]);
|
||||
}
|
||||
unlockBtreeIfUnused(pBt);
|
||||
sqlite3_free(pCur->aOverflow);
|
||||
@@ -4938,13 +4934,7 @@ static int moveToRoot(BtCursor *pCur){
|
||||
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
|
||||
assert( CURSOR_VALID < CURSOR_REQUIRESEEK );
|
||||
assert( CURSOR_FAULT > CURSOR_REQUIRESEEK );
|
||||
if( pCur->eState>=CURSOR_REQUIRESEEK ){
|
||||
if( pCur->eState==CURSOR_FAULT ){
|
||||
assert( pCur->skipNext!=SQLITE_OK );
|
||||
return pCur->skipNext;
|
||||
}
|
||||
sqlite3BtreeClearCursor(pCur);
|
||||
}
|
||||
assert( pCur->eState < CURSOR_REQUIRESEEK || pCur->iPage<0 );
|
||||
|
||||
if( pCur->iPage>=0 ){
|
||||
if( pCur->iPage ){
|
||||
@@ -4959,6 +4949,13 @@ static int moveToRoot(BtCursor *pCur){
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
assert( pCur->iPage==(-1) );
|
||||
if( pCur->eState>=CURSOR_REQUIRESEEK ){
|
||||
if( pCur->eState==CURSOR_FAULT ){
|
||||
assert( pCur->skipNext!=SQLITE_OK );
|
||||
return pCur->skipNext;
|
||||
}
|
||||
sqlite3BtreeClearCursor(pCur);
|
||||
}
|
||||
rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
|
||||
0, pCur->curPagerFlags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -6120,7 +6117,7 @@ static int clearCell(
|
||||
unsigned char *pCell, /* First byte of the Cell */
|
||||
CellInfo *pInfo /* Size information about the cell */
|
||||
){
|
||||
BtShared *pBt = pPage->pBt;
|
||||
BtShared *pBt;
|
||||
Pgno ovflPgno;
|
||||
int rc;
|
||||
int nOvfl;
|
||||
@@ -6136,6 +6133,7 @@ static int clearCell(
|
||||
return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
||||
}
|
||||
ovflPgno = get4byte(pCell + pInfo->nSize - 4);
|
||||
pBt = pPage->pBt;
|
||||
assert( pBt->usableSize > 4 );
|
||||
ovflPageSize = pBt->usableSize - 4;
|
||||
nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize;
|
||||
@@ -8254,7 +8252,7 @@ int sqlite3BtreeInsert(
|
||||
pCur->apPage[pCur->iPage]->nOverflow = 0;
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){
|
||||
rc = moveToRoot(pCur);
|
||||
btreeReleaseAllCursorPages(pCur);
|
||||
if( pCur->pKeyInfo ){
|
||||
assert( pCur->pKey==0 );
|
||||
pCur->pKey = sqlite3Malloc( pX->nKey );
|
||||
@@ -8268,7 +8266,7 @@ int sqlite3BtreeInsert(
|
||||
pCur->nKey = pX->nKey;
|
||||
}
|
||||
}
|
||||
assert( pCur->apPage[pCur->iPage]->nOverflow==0 );
|
||||
assert( pCur->iPage<0 || pCur->apPage[pCur->iPage]->nOverflow==0 );
|
||||
|
||||
end_insert:
|
||||
return rc;
|
||||
@@ -8438,6 +8436,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
}else{
|
||||
rc = moveToRoot(pCur);
|
||||
if( bPreserve ){
|
||||
btreeReleaseAllCursorPages(pCur);
|
||||
pCur->eState = CURSOR_REQUIRESEEK;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-9
@@ -479,7 +479,7 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
|
||||
}
|
||||
freeIndex(db, pIndex);
|
||||
}
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -551,7 +551,7 @@ void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
|
||||
sqlite3SchemaClear(pDb->pSchema);
|
||||
}
|
||||
}
|
||||
db->flags &= ~SQLITE_InternChanges;
|
||||
db->mDbFlags &= ~DBFLAG_SchemaChange;
|
||||
sqlite3VtabUnlockList(db);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
sqlite3CollapseDatabaseArray(db);
|
||||
@@ -561,7 +561,7 @@ void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
|
||||
** This routine is called when a commit occurs.
|
||||
*/
|
||||
void sqlite3CommitInternalChanges(sqlite3 *db){
|
||||
db->flags &= ~SQLITE_InternChanges;
|
||||
db->mDbFlags &= ~DBFLAG_SchemaChange;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -665,7 +665,7 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
|
||||
pDb = &db->aDb[iDb];
|
||||
p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, 0);
|
||||
sqlite3DeleteTable(db, p);
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -778,7 +778,8 @@ int sqlite3TwoPartName(
|
||||
return -1;
|
||||
}
|
||||
}else{
|
||||
assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
|
||||
assert( db->init.iDb==0 || db->init.busy
|
||||
|| (db->mDbFlags & DBFLAG_Vacuum)!=0);
|
||||
iDb = db->init.iDb;
|
||||
*pUnqual = pName1;
|
||||
}
|
||||
@@ -1680,7 +1681,7 @@ static int hasColumn(const i16 *aiCol, int nCol, int x){
|
||||
** schema to the rootpage from the main table.
|
||||
** (5) Add all table columns to the PRIMARY KEY Index object
|
||||
** so that the PRIMARY KEY is a covering index. The surplus
|
||||
** columns are part of KeyInfo.nXField and are not used for
|
||||
** columns are part of KeyInfo.nAllField and are not used for
|
||||
** sorting or lookup or uniqueness checks.
|
||||
** (6) Replace the rowid tail on all automatically generated UNIQUE
|
||||
** indices with the PRIMARY KEY columns.
|
||||
@@ -2055,7 +2056,7 @@ void sqlite3EndTable(
|
||||
return;
|
||||
}
|
||||
pParse->pNewTable = 0;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
if( !p->pSelect ){
|
||||
@@ -2835,7 +2836,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
|
||||
sqlite3VdbeAddOp3(v, OP_Last, iIdx, 0, -1);
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
@@ -3324,7 +3325,7 @@ void sqlite3CreateIndex(
|
||||
sqlite3OomFault(db);
|
||||
goto exit_create_index;
|
||||
}
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
if( pTblName!=0 ){
|
||||
pIndex->tnum = db->init.newTnum;
|
||||
}
|
||||
|
||||
+2
-2
@@ -374,7 +374,7 @@ FuncDef *sqlite3FindFunction(
|
||||
|
||||
/* If no match is found, search the built-in functions.
|
||||
**
|
||||
** If the SQLITE_PreferBuiltin flag is set, then search the built-in
|
||||
** If the DBFLAG_PreferBuiltin flag is set, then search the built-in
|
||||
** functions even if a prior app-defined function was found. And give
|
||||
** priority to built-in functions.
|
||||
**
|
||||
@@ -384,7 +384,7 @@ FuncDef *sqlite3FindFunction(
|
||||
** new function. But the FuncDefs for built-in functions are read-only.
|
||||
** So we must not search for built-ins when creating a new function.
|
||||
*/
|
||||
if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){
|
||||
if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){
|
||||
bestScore = 0;
|
||||
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
|
||||
p = functionSearch(h, zName);
|
||||
|
||||
@@ -184,6 +184,9 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#if SQLITE_ENABLE_ATOMIC_WRITE
|
||||
"ENABLE_ATOMIC_WRITE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
"ENABLE_BATCH_ATOMIC_WRITE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_CEROD
|
||||
"ENABLE_CEROD",
|
||||
#endif
|
||||
|
||||
+5
-1
@@ -502,7 +502,11 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
}else if( pPk ){
|
||||
addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iEphCur, 0, iKey);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);
|
||||
}
|
||||
assert( nKey==0 ); /* OP_Found will use a composite key */
|
||||
}else{
|
||||
addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
|
||||
|
||||
+22
-8
@@ -1706,9 +1706,14 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
/*
|
||||
** pExpr points to an expression which implements a function. If
|
||||
** it is appropriate to apply the LIKE optimization to that function
|
||||
** then set aWc[0] through aWc[2] to the wildcard characters and
|
||||
** return TRUE. If the function is not a LIKE-style function then
|
||||
** return FALSE.
|
||||
** then set aWc[0] through aWc[2] to the wildcard characters and the
|
||||
** escape character and then return TRUE. If the function is not a
|
||||
** LIKE-style function then return FALSE.
|
||||
**
|
||||
** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE
|
||||
** operator if c is a string literal that is exactly one byte in length.
|
||||
** That one byte is stored in aWc[3]. aWc[3] is set to zero if there is
|
||||
** no ESCAPE clause.
|
||||
**
|
||||
** *pIsNocase is set to true if uppercase and lowercase are equivalent for
|
||||
** the function (default for LIKE). If the function makes the distinction
|
||||
@@ -1717,17 +1722,26 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
*/
|
||||
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
FuncDef *pDef;
|
||||
if( pExpr->op!=TK_FUNCTION
|
||||
|| !pExpr->x.pList
|
||||
|| pExpr->x.pList->nExpr!=2
|
||||
){
|
||||
int nExpr;
|
||||
if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
|
||||
return 0;
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
|
||||
nExpr = pExpr->x.pList->nExpr;
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0);
|
||||
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
|
||||
return 0;
|
||||
}
|
||||
if( nExpr<3 ){
|
||||
aWc[3] = 0;
|
||||
}else{
|
||||
Expr *pEscape = pExpr->x.pList->a[2].pExpr;
|
||||
char *zEscape;
|
||||
if( pEscape->op!=TK_STRING ) return 0;
|
||||
zEscape = pEscape->u.zToken;
|
||||
if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
|
||||
aWc[3] = zEscape[0];
|
||||
}
|
||||
|
||||
/* The memcpy() statement assumes that the wildcard characters are
|
||||
** the first three statements in the compareInfo structure. The
|
||||
|
||||
+10
-10
@@ -226,7 +226,7 @@ static int autoIncBegin(
|
||||
){
|
||||
int memId = 0; /* Register holding maximum rowid */
|
||||
if( (pTab->tabFlags & TF_Autoincrement)!=0
|
||||
&& (pParse->db->flags & SQLITE_Vacuum)==0
|
||||
&& (pParse->db->mDbFlags & DBFLAG_Vacuum)==0
|
||||
){
|
||||
Parse *pToplevel = sqlite3ParseToplevel(pParse);
|
||||
AutoincInfo *pInfo;
|
||||
@@ -2059,7 +2059,7 @@ static int xferOptimization(
|
||||
Column *pDestCol = &pDest->aCol[i];
|
||||
Column *pSrcCol = &pSrc->aCol[i];
|
||||
#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
|
||||
if( (db->flags & SQLITE_Vacuum)==0
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0
|
||||
&& (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
|
||||
){
|
||||
return 0; /* Neither table may have __hidden__ columns */
|
||||
@@ -2135,15 +2135,15 @@ static int xferOptimization(
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
|
||||
assert( HasRowid(pDest) || destHasUniqueIdx );
|
||||
if( (db->flags & SQLITE_Vacuum)==0 && (
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0 && (
|
||||
(pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
|
||||
|| destHasUniqueIdx /* (2) */
|
||||
|| (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */
|
||||
)){
|
||||
/* In some circumstances, we are able to run the xfer optimization
|
||||
** only if the destination table is initially empty. Unless the
|
||||
** SQLITE_Vacuum flag is set, this block generates code to make
|
||||
** that determination. If SQLITE_Vacuum is set, then the destination
|
||||
** DBFLAG_Vacuum flag is set, this block generates code to make
|
||||
** that determination. If DBFLAG_Vacuum is set, then the destination
|
||||
** table is always empty.
|
||||
**
|
||||
** Conditions under which the destination must be empty:
|
||||
@@ -2179,8 +2179,8 @@ static int xferOptimization(
|
||||
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( db->flags & SQLITE_Vacuum ){
|
||||
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
|
||||
if( db->mDbFlags & DBFLAG_Vacuum ){
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|
|
||||
OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
|
||||
}else{
|
||||
@@ -2211,13 +2211,13 @@ static int xferOptimization(
|
||||
VdbeComment((v, "%s", pDestIdx->zName));
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( db->flags & SQLITE_Vacuum ){
|
||||
if( db->mDbFlags & DBFLAG_Vacuum ){
|
||||
/* This INSERT command is part of a VACUUM operation, which guarantees
|
||||
** that the destination table is empty. If all indexed columns use
|
||||
** collation sequence BINARY, then it can also be assumed that the
|
||||
** index will be populated by inserting keys in strictly sorted
|
||||
** order. In this case, instead of seeking within the b-tree as part
|
||||
** of every OP_IdxInsert opcode, an OP_Last is added before the
|
||||
** of every OP_IdxInsert opcode, an OP_SeekEnd is added before the
|
||||
** OP_IdxInsert to seek to the point within the b-tree where each key
|
||||
** should be inserted. This is faster.
|
||||
**
|
||||
@@ -2232,7 +2232,7 @@ static int xferOptimization(
|
||||
}
|
||||
if( i==pSrcIdx->nColumn ){
|
||||
idxInsFlags = OPFLAG_USESEEKRESULT;
|
||||
sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
}
|
||||
}
|
||||
if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
|
||||
|
||||
+3
-3
@@ -824,7 +824,7 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
|
||||
if( aFlagOp[i].op==op ){
|
||||
int onoff = va_arg(ap, int);
|
||||
int *pRes = va_arg(ap, int*);
|
||||
int oldFlags = db->flags;
|
||||
u32 oldFlags = db->flags;
|
||||
if( onoff>0 ){
|
||||
db->flags |= aFlagOp[i].mask;
|
||||
}else if( onoff==0 ){
|
||||
@@ -1259,7 +1259,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
** the database rollback and schema reset, which can cause false
|
||||
** corruption reports in some cases. */
|
||||
sqlite3BtreeEnterAll(db);
|
||||
schemaChange = (db->flags & SQLITE_InternChanges)!=0 && db->init.busy==0;
|
||||
schemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0 && db->init.busy==0;
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *p = db->aDb[i].pBt;
|
||||
@@ -1273,7 +1273,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
sqlite3VtabRollback(db);
|
||||
sqlite3EndBenignMalloc();
|
||||
|
||||
if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){
|
||||
if( (db->mDbFlags&DBFLAG_SchemaChange)!=0 && db->init.busy==0 ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
}
|
||||
|
||||
+24
-8
@@ -96,7 +96,8 @@ static int memjrnlRead(
|
||||
int iChunkOffset;
|
||||
FileChunk *pChunk;
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
if( (iAmt+iOfst)>p->endpoint.iOffset ){
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
@@ -215,7 +216,8 @@ static int memjrnlWrite(
|
||||
** atomic-write optimization. In this case the first 28 bytes of the
|
||||
** journal file may be written as part of committing the transaction. */
|
||||
assert( iOfst==p->endpoint.iOffset || iOfst==0 );
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
if( iOfst==0 && p->pFirst ){
|
||||
assert( p->nChunkSize>iAmt );
|
||||
memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
|
||||
@@ -384,17 +386,31 @@ void sqlite3MemJournalOpen(sqlite3_file *pJfd){
|
||||
sqlite3JournalOpen(0, 0, pJfd, 0, -1);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
/*
|
||||
** If the argument p points to a MemJournal structure that is not an
|
||||
** in-memory-only journal file (i.e. is one that was opened with a +ve
|
||||
** nSpill parameter), and the underlying file has not yet been created,
|
||||
** create it now.
|
||||
** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying
|
||||
** file has not yet been created, create it now.
|
||||
*/
|
||||
int sqlite3JournalCreate(sqlite3_file *p){
|
||||
int sqlite3JournalCreate(sqlite3_file *pJfd){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
|
||||
rc = memjrnlCreateFile((MemJournal*)p);
|
||||
MemJournal *p = (MemJournal*)pJfd;
|
||||
if( p->pMethod==&MemJournalMethods && (
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
p->nSpill>0
|
||||
#else
|
||||
/* While this appears to not be possible without ATOMIC_WRITE, the
|
||||
** paths are complex, so it seems prudent to leave the test in as
|
||||
** a NEVER(), in case our analysis is subtly flawed. */
|
||||
NEVER(p->nSpill>0)
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
|| (p->flags & SQLITE_OPEN_MAIN_JOURNAL)
|
||||
#endif
|
||||
)){
|
||||
rc = memjrnlCreateFile(p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+92
-34
@@ -90,6 +90,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
@@ -220,10 +221,8 @@ struct unixFile {
|
||||
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
|
||||
void *pMapRegion; /* Memory mapped region */
|
||||
#endif
|
||||
#ifdef __QNXNTO__
|
||||
int sectorSize; /* Device sector size */
|
||||
int deviceCharacteristics; /* Precomputed device characteristics */
|
||||
#endif
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
int openFlags; /* The flags specified at open() */
|
||||
#endif
|
||||
@@ -328,6 +327,20 @@ static pid_t randomnessPid = 0;
|
||||
# define lseek lseek64
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
/*
|
||||
** Linux-specific IOCTL magic numbers used for controlling F2FS
|
||||
*/
|
||||
#define F2FS_IOCTL_MAGIC 0xf5
|
||||
#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1)
|
||||
#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2)
|
||||
#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3)
|
||||
#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
|
||||
#define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32)
|
||||
#define F2FS_FEATURE_ATOMIC_WRITE 0x0004
|
||||
#endif /* __linux__ */
|
||||
|
||||
|
||||
/*
|
||||
** Different Unix systems declare open() in different ways. Same use
|
||||
** open(const char*,int,mode_t). Others use open(const char*,int,...).
|
||||
@@ -500,6 +513,9 @@ static struct unix_syscall {
|
||||
#endif
|
||||
#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent)
|
||||
|
||||
{ "ioctl", (sqlite3_syscall_ptr)ioctl, 0 },
|
||||
#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
|
||||
|
||||
}; /* End of the overrideable system calls */
|
||||
|
||||
|
||||
@@ -3777,6 +3793,21 @@ static int unixGetTempname(int nBuf, char *zBuf);
|
||||
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
switch( op ){
|
||||
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: {
|
||||
int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE);
|
||||
return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: {
|
||||
int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE);
|
||||
return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
|
||||
int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE);
|
||||
return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK;
|
||||
}
|
||||
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
|
||||
|
||||
case SQLITE_FCNTL_LOCKSTATE: {
|
||||
*(int*)pArg = pFile->eFileLock;
|
||||
return SQLITE_OK;
|
||||
@@ -3827,6 +3858,14 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
if( newLimit>sqlite3GlobalConfig.mxMmap ){
|
||||
newLimit = sqlite3GlobalConfig.mxMmap;
|
||||
}
|
||||
|
||||
/* The value of newLimit may be eventually cast to (size_t) and passed
|
||||
** to mmap(). Restrict its value to 2GB if (size_t) is not at least a
|
||||
** 64-bit type. */
|
||||
if( newLimit>0 && sizeof(size_t)<8 ){
|
||||
newLimit = (newLimit & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
*(i64*)pArg = pFile->mmapSizeMax;
|
||||
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
|
||||
pFile->mmapSizeMax = newLimit;
|
||||
@@ -3860,30 +3899,41 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector size in bytes of the underlying block device for
|
||||
** the specified file. This is almost always 512 bytes, but may be
|
||||
** larger for some devices.
|
||||
** If pFd->sectorSize is non-zero when this function is called, it is a
|
||||
** no-op. Otherwise, the values of pFd->sectorSize and
|
||||
** pFd->deviceCharacteristics are set according to the file-system
|
||||
** characteristics.
|
||||
**
|
||||
** SQLite code assumes this function cannot fail. It also assumes that
|
||||
** if two files are created in the same file-system directory (i.e.
|
||||
** a database and its journal file) that the sector size will be the
|
||||
** same for both.
|
||||
** There are two versions of this function. One for QNX and one for all
|
||||
** other systems.
|
||||
*/
|
||||
#ifndef __QNXNTO__
|
||||
static int unixSectorSize(sqlite3_file *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
#endif
|
||||
#ifndef __QNXNTO__
|
||||
static void setDeviceCharacteristics(unixFile *pFd){
|
||||
assert( pFd->deviceCharacteristics==0 || pFd->sectorSize!=0 );
|
||||
if( pFd->sectorSize==0 ){
|
||||
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
int res;
|
||||
u32 f = 0;
|
||||
|
||||
/*
|
||||
** The following version of unixSectorSize() is optimized for QNX.
|
||||
*/
|
||||
#ifdef __QNXNTO__
|
||||
/* Check for support for F2FS atomic batch writes. */
|
||||
res = osIoctl(pFd->h, F2FS_IOC_GET_FEATURES, &f);
|
||||
if( res==0 && (f & F2FS_FEATURE_ATOMIC_WRITE) ){
|
||||
pFd->deviceCharacteristics = SQLITE_IOCAP_BATCH_ATOMIC;
|
||||
}
|
||||
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
|
||||
|
||||
/* Set the POWERSAFE_OVERWRITE flag if requested. */
|
||||
if( pFd->ctrlFlags & UNIXFILE_PSOW ){
|
||||
pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
||||
}
|
||||
|
||||
pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#include <sys/dcmd_blk.h>
|
||||
#include <sys/statvfs.h>
|
||||
static int unixSectorSize(sqlite3_file *id){
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
static void setDeviceCharacteristics(unixFile *pFile){
|
||||
if( pFile->sectorSize == 0 ){
|
||||
struct statvfs fsInfo;
|
||||
|
||||
@@ -3952,9 +4002,24 @@ static int unixSectorSize(sqlite3_file *id){
|
||||
pFile->deviceCharacteristics = 0;
|
||||
pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
return pFile->sectorSize;
|
||||
}
|
||||
#endif /* __QNXNTO__ */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the sector size in bytes of the underlying block device for
|
||||
** the specified file. This is almost always 512 bytes, but may be
|
||||
** larger for some devices.
|
||||
**
|
||||
** SQLite code assumes this function cannot fail. It also assumes that
|
||||
** if two files are created in the same file-system directory (i.e.
|
||||
** a database and its journal file) that the sector size will be the
|
||||
** same for both.
|
||||
*/
|
||||
static int unixSectorSize(sqlite3_file *id){
|
||||
unixFile *pFd = (unixFile*)id;
|
||||
setDeviceCharacteristics(pFd);
|
||||
return pFd->sectorSize;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristics for the file.
|
||||
@@ -3970,16 +4035,9 @@ static int unixSectorSize(sqlite3_file *id){
|
||||
** available to turn it off and URI query parameter available to turn it off.
|
||||
*/
|
||||
static int unixDeviceCharacteristics(sqlite3_file *id){
|
||||
unixFile *p = (unixFile*)id;
|
||||
int rc = 0;
|
||||
#ifdef __QNXNTO__
|
||||
if( p->sectorSize==0 ) unixSectorSize(id);
|
||||
rc = p->deviceCharacteristics;
|
||||
#endif
|
||||
if( p->ctrlFlags & UNIXFILE_PSOW ){
|
||||
rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
||||
}
|
||||
return rc;
|
||||
unixFile *pFd = (unixFile*)id;
|
||||
setDeviceCharacteristics(pFd);
|
||||
return pFd->deviceCharacteristics;
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
||||
@@ -7598,7 +7656,7 @@ int sqlite3_os_init(void){
|
||||
|
||||
/* Double-check that the aSyscall[] array has been constructed
|
||||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||||
assert( ArraySize(aSyscall)==28 );
|
||||
assert( ArraySize(aSyscall)==29 );
|
||||
|
||||
/* Register all VFSes defined in the aVfs[] array */
|
||||
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
||||
|
||||
@@ -3559,6 +3559,14 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
if( newLimit>sqlite3GlobalConfig.mxMmap ){
|
||||
newLimit = sqlite3GlobalConfig.mxMmap;
|
||||
}
|
||||
|
||||
/* The value of newLimit may be eventually cast to (SIZE_T) and passed
|
||||
** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at
|
||||
** least a 64-bit type. */
|
||||
if( newLimit>0 && sizeof(SIZE_T)<8 ){
|
||||
newLimit = (newLimit & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
*(i64*)pArg = pFile->mmapSizeMax;
|
||||
if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
|
||||
pFile->mmapSizeMax = newLimit;
|
||||
|
||||
+106
-46
@@ -947,6 +947,7 @@ static int assert_pager_state(Pager *p){
|
||||
assert( isOpen(p->jfd)
|
||||
|| p->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| p->journalMode==PAGER_JOURNALMODE_WAL
|
||||
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
);
|
||||
assert( pPager->dbOrigSize<=pPager->dbHintSize );
|
||||
break;
|
||||
@@ -958,6 +959,7 @@ static int assert_pager_state(Pager *p){
|
||||
assert( isOpen(p->jfd)
|
||||
|| p->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| p->journalMode==PAGER_JOURNALMODE_WAL
|
||||
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -1168,34 +1170,45 @@ static int pagerLockDb(Pager *pPager, int eLock){
|
||||
}
|
||||
|
||||
/*
|
||||
** This function determines whether or not the atomic-write optimization
|
||||
** can be used with this pager. The optimization can be used if:
|
||||
** This function determines whether or not the atomic-write or
|
||||
** atomic-batch-write optimizations can be used with this pager. The
|
||||
** atomic-write optimization can be used if:
|
||||
**
|
||||
** (a) the value returned by OsDeviceCharacteristics() indicates that
|
||||
** a database page may be written atomically, and
|
||||
** (b) the value returned by OsSectorSize() is less than or equal
|
||||
** to the page size.
|
||||
**
|
||||
** The optimization is also always enabled for temporary files. It is
|
||||
** an error to call this function if pPager is opened on an in-memory
|
||||
** database.
|
||||
** If it can be used, then the value returned is the size of the journal
|
||||
** file when it contains rollback data for exactly one page.
|
||||
**
|
||||
** If the optimization cannot be used, 0 is returned. If it can be used,
|
||||
** then the value returned is the size of the journal file when it
|
||||
** contains rollback data for exactly one page.
|
||||
** The atomic-batch-write optimization can be used if OsDeviceCharacteristics()
|
||||
** returns a value with the SQLITE_IOCAP_BATCH_ATOMIC bit set. -1 is
|
||||
** returned in this case.
|
||||
**
|
||||
** If neither optimization can be used, 0 is returned.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
static int jrnlBufferSize(Pager *pPager){
|
||||
assert( !MEMDB );
|
||||
if( !pPager->tempFile ){
|
||||
int dc; /* Device characteristics */
|
||||
int nSector; /* Sector size */
|
||||
int szPage; /* Page size */
|
||||
|
||||
assert( isOpen(pPager->fd) );
|
||||
dc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
||||
nSector = pPager->sectorSize;
|
||||
szPage = pPager->pageSize;
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
int dc; /* Device characteristics */
|
||||
|
||||
assert( isOpen(pPager->fd) );
|
||||
dc = sqlite3OsDeviceCharacteristics(pPager->fd);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
if( dc&SQLITE_IOCAP_BATCH_ATOMIC ){
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
{
|
||||
int nSector = pPager->sectorSize;
|
||||
int szPage = pPager->pageSize;
|
||||
|
||||
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
|
||||
assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
|
||||
@@ -1205,11 +1218,11 @@ static int jrnlBufferSize(Pager *pPager){
|
||||
}
|
||||
|
||||
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
|
||||
}
|
||||
#else
|
||||
# define jrnlBufferSize(x) 0
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** If SQLITE_CHECK_PAGES is defined then we do some sanity checking
|
||||
** on the cache using a hash function. This is used for testing
|
||||
@@ -2012,7 +2025,9 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
}
|
||||
|
||||
releaseAllSavepoints(pPager);
|
||||
assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
|
||||
assert( isOpen(pPager->jfd) || pPager->pInJournal==0
|
||||
|| (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
);
|
||||
if( isOpen(pPager->jfd) ){
|
||||
assert( !pagerUseWal(pPager) );
|
||||
|
||||
@@ -4567,6 +4582,13 @@ static int pagerStress(void *p, PgHdr *pPg){
|
||||
rc = pagerWalFrames(pPager, pPg, 0, 0);
|
||||
}
|
||||
}else{
|
||||
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
if( pPager->tempFile==0 ){
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc!=SQLITE_OK ) return pager_error(pPager, rc);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Sync the journal file if required. */
|
||||
if( pPg->flags&PGHDR_NEED_SYNC
|
||||
@@ -6347,6 +6369,21 @@ int sqlite3PagerCommitPhaseOne(
|
||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||
}
|
||||
}else{
|
||||
/* The bBatch boolean is true if the batch-atomic-write commit method
|
||||
** should be used. No rollback journal is created if batch-atomic-write
|
||||
** is enabled.
|
||||
*/
|
||||
sqlite3_file *fd = pPager->fd;
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
const int bBatch = zMaster==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */
|
||||
&& (sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC)
|
||||
&& !pPager->noSync
|
||||
&& sqlite3JournalIsInMemory(pPager->jfd);
|
||||
#else
|
||||
# define bBatch 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
/* The following block updates the change-counter. Exactly how it
|
||||
** does this depends on whether or not the atomic-update optimization
|
||||
** was enabled at compile time, and if this transaction meets the
|
||||
@@ -6370,33 +6407,40 @@ int sqlite3PagerCommitPhaseOne(
|
||||
** in 'direct' mode. In this case the journal file will never be
|
||||
** created for this transaction.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
PgHdr *pPg;
|
||||
assert( isOpen(pPager->jfd)
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
|
||||
);
|
||||
if( !zMaster && isOpen(pPager->jfd)
|
||||
&& pPager->journalOff==jrnlBufferSize(pPager)
|
||||
&& pPager->dbSize>=pPager->dbOrigSize
|
||||
&& (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
|
||||
){
|
||||
/* Update the db file change counter via the direct-write method. The
|
||||
** following call will modify the in-memory representation of page 1
|
||||
** to include the updated change counter and then write page 1
|
||||
** directly to the database file. Because of the atomic-write
|
||||
** property of the host file-system, this is safe.
|
||||
*/
|
||||
rc = pager_incr_changecounter(pPager, 1);
|
||||
}else{
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_incr_changecounter(pPager, 0);
|
||||
if( bBatch==0 ){
|
||||
PgHdr *pPg;
|
||||
assert( isOpen(pPager->jfd)
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
|
||||
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
|
||||
);
|
||||
if( !zMaster && isOpen(pPager->jfd)
|
||||
&& pPager->journalOff==jrnlBufferSize(pPager)
|
||||
&& pPager->dbSize>=pPager->dbOrigSize
|
||||
&& (!(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
|
||||
){
|
||||
/* Update the db file change counter via the direct-write method. The
|
||||
** following call will modify the in-memory representation of page 1
|
||||
** to include the updated change counter and then write page 1
|
||||
** directly to the database file. Because of the atomic-write
|
||||
** property of the host file-system, this is safe.
|
||||
*/
|
||||
rc = pager_incr_changecounter(pPager, 1);
|
||||
}else{
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pager_incr_changecounter(pPager, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#else
|
||||
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
if( zMaster ){
|
||||
rc = sqlite3JournalCreate(pPager->jfd);
|
||||
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
||||
}
|
||||
#endif
|
||||
rc = pager_incr_changecounter(pPager, 0);
|
||||
#endif
|
||||
#endif
|
||||
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
||||
|
||||
/* Write the master journal name into the journal file. If a master
|
||||
@@ -6419,8 +6463,24 @@ int sqlite3PagerCommitPhaseOne(
|
||||
*/
|
||||
rc = syncJournal(pPager, 0);
|
||||
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
||||
|
||||
|
||||
if( bBatch ){
|
||||
/* The pager is now in DBMOD state. But regardless of what happens
|
||||
** next, attempting to play the journal back into the database would
|
||||
** be unsafe. Close it now to make sure that does not happen. */
|
||||
sqlite3OsClose(pPager->jfd);
|
||||
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
|
||||
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
|
||||
}
|
||||
rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
|
||||
if( bBatch ){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
|
||||
}else{
|
||||
sqlite3OsFileControl(fd, SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( rc!=SQLITE_IOERR_BLOCKED );
|
||||
goto commit_phase_one_exit;
|
||||
|
||||
+13
@@ -192,6 +192,19 @@ columnlist ::= columnlist COMMA columnname carglist.
|
||||
columnlist ::= columnname carglist.
|
||||
columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
|
||||
|
||||
// Declare some tokens early in order to influence their values, to
|
||||
// improve performance and reduce the executable size. The goal here is
|
||||
// to get the "jump" operations in ISNULL through ESCAPE to have numeric
|
||||
// values that are early enough so that all jump operations are clustered
|
||||
// at the beginning, but also so that the comparison tokens NE through GE
|
||||
// are as large as possible so that they are near to FUNCTION, which is a
|
||||
// token synthesized by addopcodes.tcl.
|
||||
//
|
||||
%token ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST.
|
||||
%token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL.
|
||||
%token OR AND NOT IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ.
|
||||
%token GT LE LT GE ESCAPE.
|
||||
|
||||
// The following directive causes tokens ABORT, AFTER, ASC, etc. to
|
||||
// fallback to ID if they will not parse as their original value.
|
||||
// This obviates the need for the "id" nonterminal.
|
||||
|
||||
+17
-18
@@ -96,7 +96,6 @@ typedef struct PGroup PGroup;
|
||||
struct PgHdr1 {
|
||||
sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
|
||||
unsigned int iKey; /* Key value (page number) */
|
||||
u8 isPinned; /* Page in use, not on the LRU list */
|
||||
u8 isBulkLocal; /* This page from bulk local storage */
|
||||
u8 isAnchor; /* This is the PGroup.lru element */
|
||||
PgHdr1 *pNext; /* Next in hash table chain */
|
||||
@@ -105,6 +104,12 @@ struct PgHdr1 {
|
||||
PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
|
||||
};
|
||||
|
||||
/*
|
||||
** A page is pinned if it is no on the LRU list
|
||||
*/
|
||||
#define PAGE_IS_PINNED(p) ((p)->pLruNext==0)
|
||||
#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0)
|
||||
|
||||
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
|
||||
** of one or more PCaches that are able to recycle each other's unpinned
|
||||
** pages when they are under memory pressure. A PGroup is an instance of
|
||||
@@ -557,22 +562,18 @@ static void pcache1ResizeHash(PCache1 *p){
|
||||
** The PGroup mutex must be held when this function is called.
|
||||
*/
|
||||
static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
|
||||
PCache1 *pCache;
|
||||
|
||||
assert( pPage!=0 );
|
||||
assert( pPage->isPinned==0 );
|
||||
pCache = pPage->pCache;
|
||||
assert( PAGE_IS_UNPINNED(pPage) );
|
||||
assert( pPage->pLruNext );
|
||||
assert( pPage->pLruPrev );
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
assert( sqlite3_mutex_held(pPage->pCache->pGroup->mutex) );
|
||||
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
||||
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
||||
pPage->pLruNext = 0;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->isPinned = 1;
|
||||
assert( pPage->isAnchor==0 );
|
||||
assert( pCache->pGroup->lru.isAnchor==1 );
|
||||
pCache->nRecyclable--;
|
||||
assert( pPage->pCache->pGroup->lru.isAnchor==1 );
|
||||
pPage->pCache->nRecyclable--;
|
||||
return pPage;
|
||||
}
|
||||
|
||||
@@ -610,7 +611,7 @@ static void pcache1EnforceMaxPage(PCache1 *pCache){
|
||||
&& (p=pGroup->lru.pLruPrev)->isAnchor==0
|
||||
){
|
||||
assert( p->pCache->pGroup==pGroup );
|
||||
assert( p->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(p) );
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p, 1);
|
||||
}
|
||||
@@ -659,7 +660,7 @@ static void pcache1TruncateUnsafe(
|
||||
if( pPage->iKey>=iLimit ){
|
||||
pCache->nPage--;
|
||||
*pp = pPage->pNext;
|
||||
if( !pPage->isPinned ) pcache1PinPage(pPage);
|
||||
if( PAGE_IS_UNPINNED(pPage) ) pcache1PinPage(pPage);
|
||||
pcache1FreePage(pPage);
|
||||
}else{
|
||||
pp = &pPage->pNext;
|
||||
@@ -878,7 +879,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
|
||||
){
|
||||
PCache1 *pOther;
|
||||
pPage = pGroup->lru.pLruPrev;
|
||||
assert( pPage->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(pPage) );
|
||||
pcache1RemoveFromHash(pPage, 0);
|
||||
pcache1PinPage(pPage);
|
||||
pOther = pPage->pCache;
|
||||
@@ -905,7 +906,6 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
|
||||
pPage->pCache = pCache;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->pLruNext = 0;
|
||||
pPage->isPinned = 1;
|
||||
*(void **)pPage->page.pExtra = 0;
|
||||
pCache->apHash[h] = pPage;
|
||||
if( iKey>pCache->iMaxKey ){
|
||||
@@ -991,7 +991,7 @@ static PgHdr1 *pcache1FetchNoMutex(
|
||||
** Otherwise (page not in hash and createFlag!=0) continue with
|
||||
** subsequent steps to try to create the page. */
|
||||
if( pPage ){
|
||||
if( !pPage->isPinned ){
|
||||
if( PAGE_IS_UNPINNED(pPage) ){
|
||||
return pcache1PinPage(pPage);
|
||||
}else{
|
||||
return pPage;
|
||||
@@ -1066,7 +1066,7 @@ static void pcache1Unpin(
|
||||
** part of the PGroup LRU list.
|
||||
*/
|
||||
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
|
||||
assert( pPage->isPinned==1 );
|
||||
assert( PAGE_IS_PINNED(pPage) );
|
||||
|
||||
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
|
||||
pcache1RemoveFromHash(pPage, 1);
|
||||
@@ -1077,7 +1077,6 @@ static void pcache1Unpin(
|
||||
(pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
|
||||
*ppFirst = pPage;
|
||||
pCache->nRecyclable++;
|
||||
pPage->isPinned = 0;
|
||||
}
|
||||
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
@@ -1221,7 +1220,7 @@ int sqlite3PcacheReleaseMemory(int nReq){
|
||||
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
nFree += sqlite3MemSize(p);
|
||||
#endif
|
||||
assert( p->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(p) );
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p, 1);
|
||||
}
|
||||
@@ -1245,7 +1244,7 @@ void sqlite3PcacheStats(
|
||||
PgHdr1 *p;
|
||||
int nRecyclable = 0;
|
||||
for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
|
||||
assert( p->isPinned==0 );
|
||||
assert( PAGE_IS_UNPINNED(p) );
|
||||
nRecyclable++;
|
||||
}
|
||||
*pnCurrent = pcache1.grp.nCurrentPage;
|
||||
|
||||
+9
-11
@@ -85,7 +85,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
rc = db->errCode;
|
||||
assert( (rc&0xFF)==(rcp&0xFF) );
|
||||
db->init.iDb = saved_iDb;
|
||||
assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
|
||||
assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 );
|
||||
if( SQLITE_OK!=rc ){
|
||||
if( db->init.orphanTrigger ){
|
||||
assert( iDb==1 );
|
||||
@@ -354,7 +354,7 @@ error_out:
|
||||
*/
|
||||
int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
int i, rc;
|
||||
int commit_internal = !(db->flags&SQLITE_InternChanges);
|
||||
int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
|
||||
@@ -494,16 +494,14 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
** Free all memory allocations in the pParse object
|
||||
*/
|
||||
void sqlite3ParserReset(Parse *pParse){
|
||||
if( pParse ){
|
||||
sqlite3 *db = pParse->db;
|
||||
sqlite3DbFree(db, pParse->aLabel);
|
||||
sqlite3ExprListDelete(db, pParse->pConstExpr);
|
||||
if( db ){
|
||||
assert( db->lookaside.bDisable >= pParse->disableLookaside );
|
||||
db->lookaside.bDisable -= pParse->disableLookaside;
|
||||
}
|
||||
pParse->disableLookaside = 0;
|
||||
sqlite3 *db = pParse->db;
|
||||
sqlite3DbFree(db, pParse->aLabel);
|
||||
sqlite3ExprListDelete(db, pParse->pConstExpr);
|
||||
if( db ){
|
||||
assert( db->lookaside.bDisable >= pParse->disableLookaside );
|
||||
db->lookaside.bDisable -= pParse->disableLookaside;
|
||||
}
|
||||
pParse->disableLookaside = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+5
-5
@@ -562,11 +562,11 @@ static void pushOntoSorter(
|
||||
if( pParse->db->mallocFailed ) return;
|
||||
pOp->p2 = nKey + nData;
|
||||
pKI = pOp->p4.pKeyInfo;
|
||||
memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
|
||||
memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
||||
testcase( pKI->nXField>2 );
|
||||
testcase( pKI->nAllField > pKI->nKeyField+2 );
|
||||
pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat,
|
||||
pKI->nXField-1);
|
||||
pKI->nAllField-pKI->nKeyField-1);
|
||||
addrJmp = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
|
||||
pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
|
||||
@@ -1035,8 +1035,8 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
|
||||
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
|
||||
if( p ){
|
||||
p->aSortOrder = (u8*)&p->aColl[N+X];
|
||||
p->nField = (u16)N;
|
||||
p->nXField = (u16)X;
|
||||
p->nKeyField = (u16)N;
|
||||
p->nAllField = (u16)(N+X);
|
||||
p->enc = ENC(db);
|
||||
p->db = db;
|
||||
p->nRef = 1;
|
||||
|
||||
+48
-1
@@ -494,6 +494,9 @@ int sqlite3_exec(
|
||||
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
|
||||
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
|
||||
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
|
||||
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
|
||||
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
|
||||
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
|
||||
@@ -580,6 +583,11 @@ int sqlite3_exec(
|
||||
** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
|
||||
** read-only media and cannot be changed even by processes with
|
||||
** elevated privileges.
|
||||
**
|
||||
** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying
|
||||
** filesystem supports doing multiple write operations atomically when those
|
||||
** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and
|
||||
** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE].
|
||||
*/
|
||||
#define SQLITE_IOCAP_ATOMIC 0x00000001
|
||||
#define SQLITE_IOCAP_ATOMIC512 0x00000002
|
||||
@@ -595,6 +603,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
|
||||
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
|
||||
#define SQLITE_IOCAP_IMMUTABLE 0x00002000
|
||||
#define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000
|
||||
|
||||
/*
|
||||
** CAPI3REF: File Locking Levels
|
||||
@@ -729,6 +738,7 @@ struct sqlite3_file {
|
||||
** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN]
|
||||
** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE]
|
||||
** <li> [SQLITE_IOCAP_IMMUTABLE]
|
||||
** <li> [SQLITE_IOCAP_BATCH_ATOMIC]
|
||||
** </ul>
|
||||
**
|
||||
** The SQLITE_IOCAP_ATOMIC property means that all writes of
|
||||
@@ -1012,6 +1022,40 @@ struct sqlite3_io_methods {
|
||||
** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
|
||||
** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
|
||||
** this opcode.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]]
|
||||
** If the [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] opcode returns SQLITE_OK, then
|
||||
** the file descriptor is placed in "batch write mode", which
|
||||
** means all subsequent write operations will be deferred and done
|
||||
** atomically at the next [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. Systems
|
||||
** that do not support batch atomic writes will return SQLITE_NOTFOUND.
|
||||
** ^Following a successful SQLITE_FCNTL_BEGIN_ATOMIC_WRITE and prior to
|
||||
** the closing [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] or
|
||||
** [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE], SQLite will make
|
||||
** no VFS interface calls on the same [sqlite3_file] file descriptor
|
||||
** except for calls to the xWrite method and the xFileControl method
|
||||
** with [SQLITE_FCNTL_SIZE_HINT].
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]]
|
||||
** The [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] opcode causes all write
|
||||
** operations since the previous successful call to
|
||||
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be performed atomically.
|
||||
** This file control returns [SQLITE_OK] if and only if the writes were
|
||||
** all performed successfully and have been committed to persistent storage.
|
||||
** ^Regardless of whether or not it is successful, this file control takes
|
||||
** the file descriptor out of batch write mode so that all subsequent
|
||||
** write operations are independent.
|
||||
** ^SQLite will never invoke SQLITE_FCNTL_COMMIT_ATOMIC_WRITE without
|
||||
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE]]
|
||||
** The [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE] opcode causes all write
|
||||
** operations since the previous successful call to
|
||||
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be rolled back.
|
||||
** ^This file control takes the file descriptor out of batch write mode
|
||||
** so that all subsequent write operations are independent.
|
||||
** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
|
||||
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
|
||||
** </ul>
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
@@ -1043,6 +1087,9 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_JOURNAL_POINTER 28
|
||||
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
|
||||
#define SQLITE_FCNTL_PDB 30
|
||||
#define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31
|
||||
#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32
|
||||
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
@@ -4199,7 +4246,7 @@ const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||
** sqlite3_step(). Failure to reset the prepared statement using
|
||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
||||
** sqlite3_step() began
|
||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||
|
||||
+3
-3
@@ -246,7 +246,7 @@ struct sqlite3_api_routines {
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*vsnprintf)(int,char*,const char*,va_list);
|
||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
@@ -442,7 +442,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
@@ -518,7 +518,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
|
||||
+30
-18
@@ -630,6 +630,15 @@
|
||||
# define SQLITE_DEFAULT_PCACHE_INITSZ 20
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The compile-time options SQLITE_MMAP_READWRITE and
|
||||
** SQLITE_ENABLE_BATCH_ATOMIC_WRITE are not compatible with one another.
|
||||
** You must choose one or the other (or neither) but not both.
|
||||
*/
|
||||
#if defined(SQLITE_MMAP_READWRITE) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
#error Cannot use both SQLITE_MMAP_READWRITE and SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
#endif
|
||||
|
||||
/*
|
||||
** GCC does not define the offsetof() macro so we'll have to do it
|
||||
** ourselves.
|
||||
@@ -1316,7 +1325,8 @@ struct sqlite3 {
|
||||
sqlite3_mutex *mutex; /* Connection mutex */
|
||||
Db *aDb; /* All backends */
|
||||
int nDb; /* Number of backends currently in use */
|
||||
int flags; /* Miscellaneous flags. See below */
|
||||
u32 mDbFlags; /* flags recording internal state */
|
||||
u32 flags; /* flags settable by pragmas. See below */
|
||||
i64 lastRowid; /* ROWID of most recent insert (see above) */
|
||||
i64 szMmap; /* Default mmap_size setting */
|
||||
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
|
||||
@@ -1470,18 +1480,13 @@ struct sqlite3 {
|
||||
#define SQLITE_ForeignKeys 0x00004000 /* Enforce foreign key constraints */
|
||||
#define SQLITE_AutoIndex 0x00008000 /* Enable automatic indexes */
|
||||
#define SQLITE_LoadExtension 0x00010000 /* Enable load_extension */
|
||||
#define SQLITE_EnableTrigger 0x00020000 /* True to enable triggers */
|
||||
#define SQLITE_DeferFKs 0x00040000 /* Defer all FK constraints */
|
||||
#define SQLITE_QueryOnly 0x00080000 /* Disable database changes */
|
||||
#define SQLITE_CellSizeCk 0x00100000 /* Check btree cell sizes on load */
|
||||
#define SQLITE_Fts3Tokenizer 0x00200000 /* Enable fts3_tokenizer(2) */
|
||||
#define SQLITE_EnableQPSG 0x00400000 /* Query Planner Stability Guarantee */
|
||||
/* The next four values are not used by PRAGMAs or by sqlite3_dbconfig() and
|
||||
** could be factored out into a separate bit vector of the sqlite3 object. */
|
||||
#define SQLITE_InternChanges 0x00800000 /* Uncommitted Hash table changes */
|
||||
#define SQLITE_LoadExtFunc 0x01000000 /* Enable load_extension() SQL func */
|
||||
#define SQLITE_PreferBuiltin 0x02000000 /* Preference to built-in funcs */
|
||||
#define SQLITE_Vacuum 0x04000000 /* Currently in a VACUUM */
|
||||
#define SQLITE_LoadExtFunc 0x00020000 /* Enable load_extension() SQL func */
|
||||
#define SQLITE_EnableTrigger 0x00040000 /* True to enable triggers */
|
||||
#define SQLITE_DeferFKs 0x00080000 /* Defer all FK constraints */
|
||||
#define SQLITE_QueryOnly 0x00100000 /* Disable database changes */
|
||||
#define SQLITE_CellSizeCk 0x00200000 /* Check btree cell sizes on load */
|
||||
#define SQLITE_Fts3Tokenizer 0x00400000 /* Enable fts3_tokenizer(2) */
|
||||
#define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee */
|
||||
/* Flags used only if debugging */
|
||||
#ifdef SQLITE_DEBUG
|
||||
#define SQLITE_SqlTrace 0x08000000 /* Debug print SQL as it executes */
|
||||
@@ -1491,6 +1496,12 @@ struct sqlite3 {
|
||||
#define SQLITE_VdbeEQP 0x80000000 /* Debug EXPLAIN QUERY PLAN */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Allowed values for sqlite3.mDbFlags
|
||||
*/
|
||||
#define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */
|
||||
#define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */
|
||||
#define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */
|
||||
|
||||
/*
|
||||
** Bits of the sqlite3.dbOptFlags field that are used by the
|
||||
@@ -2041,8 +2052,8 @@ struct FKey {
|
||||
struct KeyInfo {
|
||||
u32 nRef; /* Number of references to this KeyInfo object */
|
||||
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
|
||||
u16 nField; /* Number of key columns in the index */
|
||||
u16 nXField; /* Number of columns beyond the key columns */
|
||||
u16 nKeyField; /* Number of key columns in the index */
|
||||
u16 nAllField; /* Total columns, including key plus others */
|
||||
sqlite3 *db; /* The database connection */
|
||||
u8 *aSortOrder; /* Sort order for each column. */
|
||||
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
|
||||
@@ -2089,8 +2100,8 @@ struct UnpackedRecord {
|
||||
u16 nField; /* Number of entries in apMem[] */
|
||||
i8 default_rc; /* Comparison result if keys are equal */
|
||||
u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
|
||||
i8 r1; /* Value to return if (lhs > rhs) */
|
||||
i8 r2; /* Value to return if (rhs < lhs) */
|
||||
i8 r1; /* Value to return if (lhs < rhs) */
|
||||
i8 r2; /* Value to return if (lhs > rhs) */
|
||||
u8 eqSeen; /* True if an equality comparison has been seen */
|
||||
};
|
||||
|
||||
@@ -4285,7 +4296,8 @@ int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*);
|
||||
|
||||
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
|
||||
int sqlite3JournalSize(sqlite3_vfs *);
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|
||||
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
|
||||
int sqlite3JournalCreate(sqlite3_file *);
|
||||
#endif
|
||||
|
||||
|
||||
+15
-4
@@ -3882,28 +3882,39 @@ static int SQLITE_TCLAPI md5file_cmd(
|
||||
const char **argv
|
||||
){
|
||||
FILE *in;
|
||||
int ofst;
|
||||
int amt;
|
||||
MD5Context ctx;
|
||||
void (*converter)(unsigned char*, char*);
|
||||
unsigned char digest[16];
|
||||
char zBuf[10240];
|
||||
|
||||
if( argc!=2 ){
|
||||
if( argc!=2 && argc!=4 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" FILENAME\"", (char*)0);
|
||||
" FILENAME [OFFSET AMT]\"", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( argc==4 ){
|
||||
ofst = atoi(argv[2]);
|
||||
amt = atoi(argv[3]);
|
||||
}else{
|
||||
ofst = 0;
|
||||
amt = 2147483647;
|
||||
}
|
||||
in = fopen(argv[1],"rb");
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
||||
"\" for reading", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
fseek(in, ofst, SEEK_SET);
|
||||
MD5Init(&ctx);
|
||||
for(;;){
|
||||
while( amt>0 ){
|
||||
int n;
|
||||
n = (int)fread(zBuf, 1, sizeof(zBuf), in);
|
||||
n = (int)fread(zBuf, 1, sizeof(zBuf)<=amt ? sizeof(zBuf) : amt, in);
|
||||
if( n<=0 ) break;
|
||||
MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
|
||||
amt -= n;
|
||||
}
|
||||
fclose(in);
|
||||
MD5Final(digest, &ctx);
|
||||
|
||||
+41
@@ -2553,6 +2553,46 @@ static int SQLITE_TCLAPI test_delete_database(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: atomic_batch_write PATH
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_atomic_batch_write(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
char *zFile = 0; /* Path to file to test */
|
||||
sqlite3 *db = 0; /* Database handle */
|
||||
sqlite3_file *pFd = 0; /* SQLite fd open on zFile */
|
||||
int bRes = 0; /* Integer result of this command */
|
||||
int dc = 0; /* Device-characteristics mask */
|
||||
int rc; /* sqlite3_open() return code */
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATH");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zFile = Tcl_GetString(objv[1]);
|
||||
|
||||
rc = sqlite3_open(zFile, &db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(db), 0);
|
||||
sqlite3_close(db);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void*)&pFd);
|
||||
dc = pFd->pMethods->xDeviceCharacteristics(pFd);
|
||||
if( dc & SQLITE_IOCAP_BATCH_ATOMIC ){
|
||||
bRes = 1;
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(bRes));
|
||||
sqlite3_close(db);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_next_stmt DB STMT
|
||||
**
|
||||
@@ -7645,6 +7685,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
|
||||
#endif
|
||||
{ "sqlite3_delete_database", test_delete_database, 0 },
|
||||
{ "atomic_batch_write", test_atomic_batch_write, 0 },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
|
||||
|
||||
+25
@@ -736,6 +736,7 @@ static int processDevSymArgs(
|
||||
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
|
||||
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
|
||||
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
|
||||
{ "batch-atomic", SQLITE_IOCAP_BATCH_ATOMIC },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@@ -976,7 +977,30 @@ static int SQLITE_TCLAPI devSymObjCmd(
|
||||
devsym_register(iDc, iSectorSize);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_crash_on_write N
|
||||
*/
|
||||
static int SQLITE_TCLAPI writeCrashObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void devsym_crash_on_write(int);
|
||||
int nWrite = 0;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NWRITE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &nWrite) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
devsym_crash_on_write(nWrite);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1068,6 +1092,7 @@ int Sqlitetest6_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crash_on_write", writeCrashObjCmd,0,0);
|
||||
Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
|
||||
|
||||
+168
-50
@@ -28,6 +28,7 @@
|
||||
** Name used to identify this VFS.
|
||||
*/
|
||||
#define DEVSYM_VFS_NAME "devsym"
|
||||
#define WRITECRASH_NAME "writecrash"
|
||||
|
||||
typedef struct devsym_file devsym_file;
|
||||
struct devsym_file {
|
||||
@@ -72,61 +73,13 @@ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int devsymSleep(sqlite3_vfs*, int microseconds);
|
||||
static int devsymCurrentTime(sqlite3_vfs*, double*);
|
||||
|
||||
static sqlite3_vfs devsym_vfs = {
|
||||
2, /* iVersion */
|
||||
sizeof(devsym_file), /* szOsFile */
|
||||
DEVSYM_MAX_PATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
DEVSYM_VFS_NAME, /* zName */
|
||||
0, /* pAppData */
|
||||
devsymOpen, /* xOpen */
|
||||
devsymDelete, /* xDelete */
|
||||
devsymAccess, /* xAccess */
|
||||
devsymFullPathname, /* xFullPathname */
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
devsymDlOpen, /* xDlOpen */
|
||||
devsymDlError, /* xDlError */
|
||||
devsymDlSym, /* xDlSym */
|
||||
devsymDlClose, /* xDlClose */
|
||||
#else
|
||||
0, /* xDlOpen */
|
||||
0, /* xDlError */
|
||||
0, /* xDlSym */
|
||||
0, /* xDlClose */
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
devsymRandomness, /* xRandomness */
|
||||
devsymSleep, /* xSleep */
|
||||
devsymCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
0 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
static sqlite3_io_methods devsym_io_methods = {
|
||||
2, /* iVersion */
|
||||
devsymClose, /* xClose */
|
||||
devsymRead, /* xRead */
|
||||
devsymWrite, /* xWrite */
|
||||
devsymTruncate, /* xTruncate */
|
||||
devsymSync, /* xSync */
|
||||
devsymFileSize, /* xFileSize */
|
||||
devsymLock, /* xLock */
|
||||
devsymUnlock, /* xUnlock */
|
||||
devsymCheckReservedLock, /* xCheckReservedLock */
|
||||
devsymFileControl, /* xFileControl */
|
||||
devsymSectorSize, /* xSectorSize */
|
||||
devsymDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
devsymShmMap, /* xShmMap */
|
||||
devsymShmLock, /* xShmLock */
|
||||
devsymShmBarrier, /* xShmBarrier */
|
||||
devsymShmUnmap /* xShmUnmap */
|
||||
};
|
||||
|
||||
struct DevsymGlobal {
|
||||
sqlite3_vfs *pVfs;
|
||||
int iDeviceChar;
|
||||
int iSectorSize;
|
||||
int nWriteCrash;
|
||||
};
|
||||
struct DevsymGlobal g = {0, 0, 512};
|
||||
struct DevsymGlobal g = {0, 0, 512, 0};
|
||||
|
||||
/*
|
||||
** Close an devsym-file.
|
||||
@@ -271,6 +224,26 @@ static int devsymOpen(
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
static sqlite3_io_methods devsym_io_methods = {
|
||||
2, /* iVersion */
|
||||
devsymClose, /* xClose */
|
||||
devsymRead, /* xRead */
|
||||
devsymWrite, /* xWrite */
|
||||
devsymTruncate, /* xTruncate */
|
||||
devsymSync, /* xSync */
|
||||
devsymFileSize, /* xFileSize */
|
||||
devsymLock, /* xLock */
|
||||
devsymUnlock, /* xUnlock */
|
||||
devsymCheckReservedLock, /* xCheckReservedLock */
|
||||
devsymFileControl, /* xFileControl */
|
||||
devsymSectorSize, /* xSectorSize */
|
||||
devsymDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
devsymShmMap, /* xShmMap */
|
||||
devsymShmLock, /* xShmLock */
|
||||
devsymShmBarrier, /* xShmBarrier */
|
||||
devsymShmUnmap /* xShmUnmap */
|
||||
};
|
||||
|
||||
int rc;
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
p->pReal = (sqlite3_file *)&p[1];
|
||||
@@ -372,6 +345,137 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return g.pVfs->xCurrentTime(g.pVfs, pTimeOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector-size in bytes for an writecrash-file.
|
||||
*/
|
||||
static int writecrashSectorSize(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsSectorSize(p->pReal);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by an writecrash-file.
|
||||
*/
|
||||
static int writecrashDeviceCharacteristics(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsDeviceCharacteristics(p->pReal);
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an writecrash-file.
|
||||
*/
|
||||
static int writecrashWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
if( g.nWriteCrash>0 ){
|
||||
g.nWriteCrash--;
|
||||
if( g.nWriteCrash==0 ) abort();
|
||||
}
|
||||
return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
/*
|
||||
** Open an writecrash file handle.
|
||||
*/
|
||||
static int writecrashOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
static sqlite3_io_methods writecrash_io_methods = {
|
||||
2, /* iVersion */
|
||||
devsymClose, /* xClose */
|
||||
devsymRead, /* xRead */
|
||||
writecrashWrite, /* xWrite */
|
||||
devsymTruncate, /* xTruncate */
|
||||
devsymSync, /* xSync */
|
||||
devsymFileSize, /* xFileSize */
|
||||
devsymLock, /* xLock */
|
||||
devsymUnlock, /* xUnlock */
|
||||
devsymCheckReservedLock, /* xCheckReservedLock */
|
||||
devsymFileControl, /* xFileControl */
|
||||
writecrashSectorSize, /* xSectorSize */
|
||||
writecrashDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
devsymShmMap, /* xShmMap */
|
||||
devsymShmLock, /* xShmLock */
|
||||
devsymShmBarrier, /* xShmBarrier */
|
||||
devsymShmUnmap /* xShmUnmap */
|
||||
};
|
||||
|
||||
int rc;
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
p->pReal = (sqlite3_file *)&p[1];
|
||||
rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
|
||||
if( p->pReal->pMethods ){
|
||||
pFile->pMethods = &writecrash_io_methods;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static sqlite3_vfs devsym_vfs = {
|
||||
2, /* iVersion */
|
||||
sizeof(devsym_file), /* szOsFile */
|
||||
DEVSYM_MAX_PATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
DEVSYM_VFS_NAME, /* zName */
|
||||
0, /* pAppData */
|
||||
devsymOpen, /* xOpen */
|
||||
devsymDelete, /* xDelete */
|
||||
devsymAccess, /* xAccess */
|
||||
devsymFullPathname, /* xFullPathname */
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
devsymDlOpen, /* xDlOpen */
|
||||
devsymDlError, /* xDlError */
|
||||
devsymDlSym, /* xDlSym */
|
||||
devsymDlClose, /* xDlClose */
|
||||
#else
|
||||
0, /* xDlOpen */
|
||||
0, /* xDlError */
|
||||
0, /* xDlSym */
|
||||
0, /* xDlClose */
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
devsymRandomness, /* xRandomness */
|
||||
devsymSleep, /* xSleep */
|
||||
devsymCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
0 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
static sqlite3_vfs writecrash_vfs = {
|
||||
2, /* iVersion */
|
||||
sizeof(devsym_file), /* szOsFile */
|
||||
DEVSYM_MAX_PATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
WRITECRASH_NAME, /* zName */
|
||||
0, /* pAppData */
|
||||
writecrashOpen, /* xOpen */
|
||||
devsymDelete, /* xDelete */
|
||||
devsymAccess, /* xAccess */
|
||||
devsymFullPathname, /* xFullPathname */
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
devsymDlOpen, /* xDlOpen */
|
||||
devsymDlError, /* xDlError */
|
||||
devsymDlSym, /* xDlSym */
|
||||
devsymDlClose, /* xDlClose */
|
||||
#else
|
||||
0, /* xDlOpen */
|
||||
0, /* xDlError */
|
||||
0, /* xDlSym */
|
||||
0, /* xDlClose */
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
devsymRandomness, /* xRandomness */
|
||||
devsymSleep, /* xSleep */
|
||||
devsymCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
0 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** This procedure registers the devsym vfs with SQLite. If the argument is
|
||||
@@ -379,10 +483,13 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
** available function in this file.
|
||||
*/
|
||||
void devsym_register(int iDeviceChar, int iSectorSize){
|
||||
|
||||
if( g.pVfs==0 ){
|
||||
g.pVfs = sqlite3_vfs_find(0);
|
||||
devsym_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
writecrash_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
sqlite3_vfs_register(&devsym_vfs, 0);
|
||||
sqlite3_vfs_register(&writecrash_vfs, 0);
|
||||
}
|
||||
if( iDeviceChar>=0 ){
|
||||
g.iDeviceChar = iDeviceChar;
|
||||
@@ -403,4 +510,15 @@ void devsym_unregister(){
|
||||
g.iSectorSize = 0;
|
||||
}
|
||||
|
||||
void devsym_crash_on_write(int nWrite){
|
||||
if( g.pVfs==0 ){
|
||||
g.pVfs = sqlite3_vfs_find(0);
|
||||
devsym_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
writecrash_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
sqlite3_vfs_register(&devsym_vfs, 0);
|
||||
sqlite3_vfs_register(&writecrash_vfs, 0);
|
||||
}
|
||||
g.nWriteCrash = nWrite;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+88
-2
@@ -15,6 +15,25 @@
|
||||
**
|
||||
** The emphasis of this file is a virtual table that provides
|
||||
** access to TCL variables.
|
||||
**
|
||||
** The TCLVAR eponymous virtual table has a schema like this:
|
||||
**
|
||||
** CREATE TABLE tclvar(
|
||||
** name TEXT, -- base name of the variable: "x" in "$x(y)"
|
||||
** arrayname TEXT, -- array index name: "y" in "$x(y)"
|
||||
** value TEXT, -- the value of the variable
|
||||
** fullname TEXT, -- the full name of the variable
|
||||
** PRIMARY KEY(fullname)
|
||||
** ) WITHOUT ROWID;
|
||||
**
|
||||
** DELETE, INSERT, and UPDATE operations use the "fullname" field to
|
||||
** determine the variable to be modified. Changing "value" to NULL
|
||||
** deletes the variable.
|
||||
**
|
||||
** For SELECT operations, the "name" and "arrayname" fields will always
|
||||
** match the "fullname" field. For DELETE, INSERT, and UPDATE, the
|
||||
** "name" and "arrayname" fields are ignored and the variable is modified
|
||||
** according to "fullname" and "value" only.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
@@ -67,7 +86,12 @@ static int tclvarConnect(
|
||||
){
|
||||
tclvar_vtab *pVtab;
|
||||
static const char zSchema[] =
|
||||
"CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)";
|
||||
"CREATE TABLE x("
|
||||
" name TEXT," /* Base name */
|
||||
" arrayname TEXT," /* Array index */
|
||||
" value TEXT," /* Value */
|
||||
" fullname TEXT PRIMARY KEY" /* base(index) name */
|
||||
") WITHOUT ROWID";
|
||||
pVtab = sqlite3MallocZero( sizeof(*pVtab) );
|
||||
if( pVtab==0 ) return SQLITE_NOMEM;
|
||||
*ppVtab = &pVtab->base;
|
||||
@@ -251,6 +275,16 @@ static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
char *z3;
|
||||
if( p2 ){
|
||||
z3 = sqlite3_mprintf("%s(%s)", z1, z2);
|
||||
sqlite3_result_text(ctx, z3, -1, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -376,6 +410,58 @@ static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoked for any UPDATE, INSERT, or DELETE against a tclvar table
|
||||
*/
|
||||
static int tclvarUpdate(
|
||||
sqlite3_vtab *tab,
|
||||
int argc,
|
||||
sqlite3_value **argv,
|
||||
sqlite_int64 *pRowid
|
||||
){
|
||||
tclvar_vtab *pTab = (tclvar_vtab*)tab;
|
||||
if( argc==1 ){
|
||||
/* A DELETE operation. The variable to be deleted is stored in argv[0] */
|
||||
const char *zVar = (const char*)sqlite3_value_text(argv[0]);
|
||||
Tcl_UnsetVar(pTab->interp, zVar, TCL_GLOBAL_ONLY);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
||||
/* An INSERT operation */
|
||||
const char *zValue = (const char*)sqlite3_value_text(argv[4]);
|
||||
const char *zName;
|
||||
if( sqlite3_value_type(argv[5])!=SQLITE_TEXT ){
|
||||
tab->zErrMsg = sqlite3_mprintf("the 'fullname' column must be TEXT");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
zName = (const char*)sqlite3_value_text(argv[5]);
|
||||
if( zValue ){
|
||||
Tcl_SetVar(pTab->interp, zName, zValue, TCL_GLOBAL_ONLY);
|
||||
}else{
|
||||
Tcl_UnsetVar(pTab->interp, zName, TCL_GLOBAL_ONLY);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_TEXT
|
||||
&& sqlite3_value_type(argv[1])==SQLITE_TEXT
|
||||
){
|
||||
/* An UPDATE operation */
|
||||
const char *zOldName = (const char*)sqlite3_value_text(argv[0]);
|
||||
const char *zNewName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zValue = (const char*)sqlite3_value_text(argv[4]);
|
||||
|
||||
if( strcmp(zOldName, zNewName)!=0 || zValue==0 ){
|
||||
Tcl_UnsetVar(pTab->interp, zOldName, TCL_GLOBAL_ONLY);
|
||||
}
|
||||
if( zValue!=0 ){
|
||||
Tcl_SetVar(pTab->interp, zNewName, zValue, TCL_GLOBAL_ONLY);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
tab->zErrMsg = sqlite3_mprintf("prohibited TCL variable change");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
@@ -394,7 +480,7 @@ static sqlite3_module tclvarModule = {
|
||||
tclvarEof, /* xEof - check for end of scan */
|
||||
tclvarColumn, /* xColumn - read data */
|
||||
tclvarRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
tclvarUpdate, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
|
||||
+1
-1
@@ -585,7 +585,7 @@ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
|
||||
*pp = (*pp)->pNext;
|
||||
}
|
||||
sqlite3DeleteTrigger(db, pTrigger);
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-6
@@ -803,12 +803,6 @@ static void updateVirtualTable(
|
||||
if( pWInfo==0 ) return;
|
||||
|
||||
/* Populate the argument registers. */
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
||||
if( pRowid ){
|
||||
sqlite3ExprCode(pParse, pRowid, regArg+1);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
||||
}
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]>=0 ){
|
||||
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
|
||||
@@ -816,6 +810,23 @@ static void updateVirtualTable(
|
||||
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
|
||||
}
|
||||
}
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
||||
if( pRowid ){
|
||||
sqlite3ExprCode(pParse, pRowid, regArg+1);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
||||
}
|
||||
}else{
|
||||
Index *pPk; /* PRIMARY KEY index */
|
||||
i16 iPk; /* PRIMARY KEY column */
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
assert( pPk->nKeyCol==1 );
|
||||
iPk = pPk->aiColumn[0];
|
||||
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
|
||||
}
|
||||
|
||||
bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
||||
|
||||
|
||||
+8
-5
@@ -130,7 +130,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
int rc = SQLITE_OK; /* Return code from service routines */
|
||||
Btree *pMain; /* The database being vacuumed */
|
||||
Btree *pTemp; /* The temporary database we vacuum into */
|
||||
int saved_flags; /* Saved value of the db->flags */
|
||||
u16 saved_mDbFlags; /* Saved value of db->mDbFlags */
|
||||
u32 saved_flags; /* Saved value of db->flags */
|
||||
int saved_nChange; /* Saved value of db->nChange */
|
||||
int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
||||
u8 saved_mTrace; /* Saved trace settings */
|
||||
@@ -153,11 +154,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
** restored before returning. Then set the writable-schema flag, and
|
||||
** disable CHECK and foreign key constraints. */
|
||||
saved_flags = db->flags;
|
||||
saved_mDbFlags = db->mDbFlags;
|
||||
saved_nChange = db->nChange;
|
||||
saved_nTotalChange = db->nTotalChange;
|
||||
saved_mTrace = db->mTrace;
|
||||
db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
|
||||
| SQLITE_PreferBuiltin | SQLITE_Vacuum);
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
|
||||
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
|
||||
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
|
||||
db->mTrace = 0;
|
||||
|
||||
@@ -268,8 +270,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
"WHERE type='table'AND coalesce(rootpage,1)>0",
|
||||
zDbMain
|
||||
);
|
||||
assert( (db->flags & SQLITE_Vacuum)!=0 );
|
||||
db->flags &= ~SQLITE_Vacuum;
|
||||
assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 );
|
||||
db->mDbFlags &= ~DBFLAG_Vacuum;
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
|
||||
/* Copy the triggers, views, and virtual tables from the main database
|
||||
@@ -337,6 +339,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
|
||||
end_of_vacuum:
|
||||
/* Restore the original value of db->flags */
|
||||
db->init.iDb = 0;
|
||||
db->mDbFlags = saved_mDbFlags;
|
||||
db->flags = saved_flags;
|
||||
db->nChange = saved_nChange;
|
||||
db->nTotalChange = saved_nTotalChange;
|
||||
|
||||
+49
-34
@@ -1955,13 +1955,23 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
|
||||
}
|
||||
compare_op:
|
||||
switch( pOp->opcode ){
|
||||
case OP_Eq: res2 = res==0; break;
|
||||
case OP_Ne: res2 = res; break;
|
||||
case OP_Lt: res2 = res<0; break;
|
||||
case OP_Le: res2 = res<=0; break;
|
||||
case OP_Gt: res2 = res>0; break;
|
||||
default: res2 = res>=0; break;
|
||||
/* At this point, res is negative, zero, or positive if reg[P1] is
|
||||
** less than, equal to, or greater than reg[P3], respectively. Compute
|
||||
** the answer to this operator in res2, depending on what the comparison
|
||||
** operator actually is. The next block of code depends on the fact
|
||||
** that the 6 comparison operators are consecutive integers in this
|
||||
** order: NE, EQ, GT, LE, LT, GE */
|
||||
assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 );
|
||||
assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 );
|
||||
if( res<0 ){ /* ne, eq, gt, le, lt, ge */
|
||||
static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 };
|
||||
res2 = aLTb[pOp->opcode - OP_Ne];
|
||||
}else if( res==0 ){
|
||||
static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 };
|
||||
res2 = aEQb[pOp->opcode - OP_Ne];
|
||||
}else{
|
||||
static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 };
|
||||
res2 = aGTb[pOp->opcode - OP_Ne];
|
||||
}
|
||||
|
||||
/* Undo any changes made by applyAffinity() to the input registers. */
|
||||
@@ -1973,7 +1983,6 @@ compare_op:
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
pOut = &aMem[pOp->p2];
|
||||
iCompare = res;
|
||||
res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */
|
||||
if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
|
||||
/* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
|
||||
** and prevents OP_Ne from overwriting NULL with 0. This flag
|
||||
@@ -2104,7 +2113,7 @@ case OP_Compare: {
|
||||
assert( memIsValid(&aMem[p2+idx]) );
|
||||
REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
|
||||
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
|
||||
assert( i<pKeyInfo->nField );
|
||||
assert( i<pKeyInfo->nKeyField );
|
||||
pColl = pKeyInfo->aColl[i];
|
||||
bRev = pKeyInfo->aSortOrder[i];
|
||||
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
|
||||
@@ -2950,7 +2959,7 @@ case OP_Savepoint: {
|
||||
int isSchemaChange;
|
||||
iSavepoint = db->nSavepoint - iSavepoint - 1;
|
||||
if( p1==SAVEPOINT_ROLLBACK ){
|
||||
isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
|
||||
isSchemaChange = (db->mDbFlags & DBFLAG_SchemaChange)!=0;
|
||||
for(ii=0; ii<db->nDb; ii++){
|
||||
rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt,
|
||||
SQLITE_ABORT_ROLLBACK,
|
||||
@@ -2969,7 +2978,7 @@ case OP_Savepoint: {
|
||||
if( isSchemaChange ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
db->flags = (db->flags | SQLITE_InternChanges);
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3249,7 +3258,7 @@ case OP_SetCookie: {
|
||||
if( pOp->p2==BTREE_SCHEMA_VERSION ){
|
||||
/* When the schema cookie changes, record the new cookie internally */
|
||||
pDb->pSchema->schema_cookie = pOp->p3;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
}else if( pOp->p2==BTREE_FILE_FORMAT ){
|
||||
/* Record changes in the file format */
|
||||
pDb->pSchema->file_format = pOp->p3;
|
||||
@@ -3398,7 +3407,7 @@ case OP_OpenWrite:
|
||||
pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( pKeyInfo->enc==ENC(db) );
|
||||
assert( pKeyInfo->db==db );
|
||||
nField = pKeyInfo->nField+pKeyInfo->nXField;
|
||||
nField = pKeyInfo->nAllField;
|
||||
}else if( pOp->p4type==P4_INT32 ){
|
||||
nField = pOp->p4.i;
|
||||
}
|
||||
@@ -4776,7 +4785,17 @@ case OP_NullRow: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Last P1 P2 P3 * *
|
||||
/* Opcode: SeekEnd P1 * * * *
|
||||
**
|
||||
** Position cursor P1 at the end of the btree for the purpose of
|
||||
** appending a new entry onto the btree.
|
||||
**
|
||||
** It is assumed that the cursor is used only for appending and so
|
||||
** if the cursor is valid, then the cursor must already be pointing
|
||||
** at the end of the btree and so no changes are made to
|
||||
** the cursor.
|
||||
*/
|
||||
/* Opcode: Last P1 P2 * * *
|
||||
**
|
||||
** The next use of the Rowid or Column or Prev instruction for P1
|
||||
** will refer to the last entry in the database table or index.
|
||||
@@ -4787,14 +4806,8 @@ case OP_NullRow: {
|
||||
** This opcode leaves the cursor configured to move in reverse order,
|
||||
** from the end toward the beginning. In other words, the cursor is
|
||||
** configured to use Prev, not Next.
|
||||
**
|
||||
** If P3 is -1, then the cursor is positioned at the end of the btree
|
||||
** for the purpose of appending a new entry onto the btree. In that
|
||||
** case P2 must be 0. It is assumed that the cursor is used only for
|
||||
** appending and so if the cursor is valid, then the cursor must already
|
||||
** be pointing at the end of the btree and so no changes are made to
|
||||
** the cursor.
|
||||
*/
|
||||
case OP_SeekEnd:
|
||||
case OP_Last: { /* jump */
|
||||
VdbeCursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
@@ -4807,22 +4820,24 @@ case OP_Last: { /* jump */
|
||||
pCrsr = pC->uc.pCursor;
|
||||
res = 0;
|
||||
assert( pCrsr!=0 );
|
||||
pC->seekResult = pOp->p3;
|
||||
#ifdef SQLITE_DEBUG
|
||||
pC->seekOp = OP_Last;
|
||||
pC->seekOp = pOp->opcode;
|
||||
#endif
|
||||
if( pOp->p3==0 || !sqlite3BtreeCursorIsValidNN(pCrsr) ){
|
||||
rc = sqlite3BtreeLast(pCrsr, &res);
|
||||
pC->nullRow = (u8)res;
|
||||
pC->deferredMoveto = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( pOp->p2>0 ){
|
||||
VdbeBranchTaken(res!=0,2);
|
||||
if( res ) goto jump_to_p2;
|
||||
}
|
||||
}else{
|
||||
if( pOp->opcode==OP_SeekEnd ){
|
||||
assert( pOp->p2==0 );
|
||||
pC->seekResult = -1;
|
||||
if( sqlite3BtreeCursorIsValidNN(pCrsr) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
rc = sqlite3BtreeLast(pCrsr, &res);
|
||||
pC->nullRow = (u8)res;
|
||||
pC->deferredMoveto = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( pOp->p2>0 ){
|
||||
VdbeBranchTaken(res!=0,2);
|
||||
if( res ) goto jump_to_p2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+26
-29
@@ -398,8 +398,7 @@ void sqlite3_result_pointer(
|
||||
){
|
||||
Mem *pOut = pCtx->pOut;
|
||||
assert( sqlite3_mutex_held(pOut->db->mutex) );
|
||||
sqlite3VdbeMemRelease(pOut);
|
||||
pOut->flags = MEM_Null;
|
||||
sqlite3VdbeMemSetNull(pOut);
|
||||
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
|
||||
}
|
||||
void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
|
||||
@@ -524,7 +523,7 @@ static int doWalCallbacks(sqlite3 *db){
|
||||
sqlite3BtreeEnter(pBt);
|
||||
nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
|
||||
if( nEntry>0 && db->xWalCallback && rc==SQLITE_OK ){
|
||||
rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry);
|
||||
}
|
||||
}
|
||||
@@ -634,7 +633,7 @@ static int sqlite3Step(Vdbe *p){
|
||||
if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
|
||||
#endif
|
||||
|
||||
if( rc==SQLITE_DONE ){
|
||||
if( rc==SQLITE_DONE && db->autoCommit ){
|
||||
assert( p->rc==SQLITE_OK );
|
||||
p->rc = doWalCallbacks(db);
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
@@ -678,7 +677,6 @@ end_of_step:
|
||||
*/
|
||||
int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
int rc = SQLITE_OK; /* Result from sqlite3Step() */
|
||||
int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */
|
||||
Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */
|
||||
int cnt = 0; /* Counter to prevent infinite loop of reprepares */
|
||||
sqlite3 *db; /* The database connection */
|
||||
@@ -692,32 +690,31 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
||||
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
|
||||
&& cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
|
||||
int savedPc = v->pc;
|
||||
rc2 = rc = sqlite3Reprepare(v);
|
||||
if( rc!=SQLITE_OK) break;
|
||||
rc = sqlite3Reprepare(v);
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* This case occurs after failing to recompile an sql statement.
|
||||
** The error message from the SQL compiler has already been loaded
|
||||
** into the database handle. This block copies the error message
|
||||
** from the database handle into the statement and sets the statement
|
||||
** program counter to 0 to ensure that when the statement is
|
||||
** finalized or reset the parser error message is available via
|
||||
** sqlite3_errmsg() and sqlite3_errcode().
|
||||
*/
|
||||
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
|
||||
sqlite3DbFree(db, v->zErrMsg);
|
||||
if( !db->mallocFailed ){
|
||||
v->zErrMsg = sqlite3DbStrDup(db, zErr);
|
||||
v->rc = rc = sqlite3ApiExit(db, rc);
|
||||
} else {
|
||||
v->zErrMsg = 0;
|
||||
v->rc = rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
sqlite3_reset(pStmt);
|
||||
if( savedPc>=0 ) v->doingRerun = 1;
|
||||
assert( v->expired==0 );
|
||||
}
|
||||
if( rc2!=SQLITE_OK ){
|
||||
/* This case occurs after failing to recompile an sql statement.
|
||||
** The error message from the SQL compiler has already been loaded
|
||||
** into the database handle. This block copies the error message
|
||||
** from the database handle into the statement and sets the statement
|
||||
** program counter to 0 to ensure that when the statement is
|
||||
** finalized or reset the parser error message is available via
|
||||
** sqlite3_errmsg() and sqlite3_errcode().
|
||||
*/
|
||||
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
|
||||
sqlite3DbFree(db, v->zErrMsg);
|
||||
if( !db->mallocFailed ){
|
||||
v->zErrMsg = sqlite3DbStrDup(db, zErr);
|
||||
v->rc = rc2;
|
||||
} else {
|
||||
v->zErrMsg = 0;
|
||||
v->rc = rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
}
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
@@ -1723,7 +1720,7 @@ static UnpackedRecord *vdbeUnpackRecord(
|
||||
|
||||
pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
|
||||
if( pRet ){
|
||||
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1));
|
||||
memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
|
||||
sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
|
||||
}
|
||||
return pRet;
|
||||
@@ -1796,7 +1793,7 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
*/
|
||||
int sqlite3_preupdate_count(sqlite3 *db){
|
||||
PreUpdate *p = db->pPreUpdate;
|
||||
return (p ? p->keyinfo.nField : 0);
|
||||
return (p ? p->keyinfo.nKeyField : 0);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
|
||||
|
||||
+51
-32
@@ -597,6 +597,27 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
p->bIsReader = 1;
|
||||
break;
|
||||
}
|
||||
case OP_Next:
|
||||
case OP_NextIfOpen:
|
||||
case OP_SorterNext: {
|
||||
pOp->p4.xAdvance = sqlite3BtreeNext;
|
||||
pOp->p4type = P4_ADVANCE;
|
||||
/* The code generator never codes any of these opcodes as a jump
|
||||
** to a label. They are always coded as a jump backwards to a
|
||||
** known address */
|
||||
assert( pOp->p2>=0 );
|
||||
break;
|
||||
}
|
||||
case OP_Prev:
|
||||
case OP_PrevIfOpen: {
|
||||
pOp->p4.xAdvance = sqlite3BtreePrevious;
|
||||
pOp->p4type = P4_ADVANCE;
|
||||
/* The code generator never codes any of these opcodes as a jump
|
||||
** to a label. They are always coded as a jump backwards to a
|
||||
** known address */
|
||||
assert( pOp->p2>=0 );
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
case OP_VUpdate: {
|
||||
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
|
||||
@@ -608,27 +629,25 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
assert( pOp[-1].opcode==OP_Integer );
|
||||
n = pOp[-1].p1;
|
||||
if( n>nMaxArgs ) nMaxArgs = n;
|
||||
break;
|
||||
/* Fall through into the default case */
|
||||
}
|
||||
#endif
|
||||
case OP_Next:
|
||||
case OP_NextIfOpen:
|
||||
case OP_SorterNext: {
|
||||
pOp->p4.xAdvance = sqlite3BtreeNext;
|
||||
pOp->p4type = P4_ADVANCE;
|
||||
break;
|
||||
}
|
||||
case OP_Prev:
|
||||
case OP_PrevIfOpen: {
|
||||
pOp->p4.xAdvance = sqlite3BtreePrevious;
|
||||
pOp->p4type = P4_ADVANCE;
|
||||
default: {
|
||||
if( pOp->p2<0 ){
|
||||
/* The mkopcodeh.tcl script has so arranged things that the only
|
||||
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
|
||||
** have non-negative values for P2. */
|
||||
assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 );
|
||||
assert( ADDR(pOp->p2)<pParse->nLabel );
|
||||
pOp->p2 = aLabel[ADDR(pOp->p2)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 && pOp->p2<0 ){
|
||||
assert( ADDR(pOp->p2)<pParse->nLabel );
|
||||
pOp->p2 = aLabel[ADDR(pOp->p2)];
|
||||
}
|
||||
/* The mkopcodeh.tcl script has so arranged things that the only
|
||||
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
|
||||
** have non-negative values for P2. */
|
||||
assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0);
|
||||
}
|
||||
if( pOp==p->aOp ) break;
|
||||
pOp--;
|
||||
@@ -1301,8 +1320,8 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
int j;
|
||||
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( pKeyInfo->aSortOrder!=0 );
|
||||
sqlite3XPrintf(&x, "k(%d", pKeyInfo->nField);
|
||||
for(j=0; j<pKeyInfo->nField; j++){
|
||||
sqlite3XPrintf(&x, "k(%d", pKeyInfo->nKeyField);
|
||||
for(j=0; j<pKeyInfo->nKeyField; j++){
|
||||
CollSeq *pColl = pKeyInfo->aColl[j];
|
||||
const char *zColl = pColl ? pColl->zName : "";
|
||||
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
|
||||
@@ -3528,13 +3547,13 @@ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
|
||||
){
|
||||
UnpackedRecord *p; /* Unpacked record to return */
|
||||
int nByte; /* Number of bytes required for *p */
|
||||
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
|
||||
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
|
||||
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
|
||||
if( !p ) return 0;
|
||||
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
|
||||
assert( pKeyInfo->aSortOrder!=0 );
|
||||
p->pKeyInfo = pKeyInfo;
|
||||
p->nField = pKeyInfo->nField + 1;
|
||||
p->nField = pKeyInfo->nKeyField + 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -3574,7 +3593,7 @@ void sqlite3VdbeRecordUnpack(
|
||||
pMem++;
|
||||
if( (++u)>=p->nField ) break;
|
||||
}
|
||||
assert( u<=pKeyInfo->nField + 1 );
|
||||
assert( u<=pKeyInfo->nKeyField + 1 );
|
||||
p->nField = u;
|
||||
}
|
||||
|
||||
@@ -3623,9 +3642,9 @@ static int vdbeRecordCompareDebug(
|
||||
idx1 = getVarint32(aKey1, szHdr1);
|
||||
if( szHdr1>98307 ) return SQLITE_CORRUPT;
|
||||
d1 = szHdr1;
|
||||
assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
|
||||
assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB );
|
||||
assert( pKeyInfo->aSortOrder!=0 );
|
||||
assert( pKeyInfo->nField>0 );
|
||||
assert( pKeyInfo->nKeyField>0 );
|
||||
assert( idx1<=szHdr1 || CORRUPT_DB );
|
||||
do{
|
||||
u32 serial_type1;
|
||||
@@ -3687,12 +3706,12 @@ debugCompareEnd:
|
||||
/*
|
||||
** Count the number of fields (a.k.a. columns) in the record given by
|
||||
** pKey,nKey. The verify that this count is less than or equal to the
|
||||
** limit given by pKeyInfo->nField + pKeyInfo->nXField.
|
||||
** limit given by pKeyInfo->nAllField.
|
||||
**
|
||||
** If this constraint is not satisfied, it means that the high-speed
|
||||
** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will
|
||||
** not work correctly. If this assert() ever fires, it probably means
|
||||
** that the KeyInfo.nField or KeyInfo.nXField values were computed
|
||||
** that the KeyInfo.nKeyField or KeyInfo.nAllField values were computed
|
||||
** incorrectly.
|
||||
*/
|
||||
static void vdbeAssertFieldCountWithinLimits(
|
||||
@@ -3713,7 +3732,7 @@ static void vdbeAssertFieldCountWithinLimits(
|
||||
idx += getVarint32(aKey+idx, notUsed);
|
||||
nField++;
|
||||
}
|
||||
assert( nField <= pKeyInfo->nField+pKeyInfo->nXField );
|
||||
assert( nField <= pKeyInfo->nAllField );
|
||||
}
|
||||
#else
|
||||
# define vdbeAssertFieldCountWithinLimits(A,B,C)
|
||||
@@ -4018,10 +4037,10 @@ int sqlite3VdbeRecordCompareWithSkip(
|
||||
}
|
||||
|
||||
VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
|
||||
assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField
|
||||
assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
|
||||
|| CORRUPT_DB );
|
||||
assert( pPKey2->pKeyInfo->aSortOrder!=0 );
|
||||
assert( pPKey2->pKeyInfo->nField>0 );
|
||||
assert( pPKey2->pKeyInfo->nKeyField>0 );
|
||||
assert( idx1<=szHdr1 || CORRUPT_DB );
|
||||
do{
|
||||
u32 serial_type;
|
||||
@@ -4354,7 +4373,7 @@ RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
|
||||
** The easiest way to enforce this limit is to consider only records with
|
||||
** 13 fields or less. If the first field is an integer, the maximum legal
|
||||
** header size is (12*5 + 1 + 1) bytes. */
|
||||
if( (p->pKeyInfo->nField + p->pKeyInfo->nXField)<=13 ){
|
||||
if( p->pKeyInfo->nAllField<=13 ){
|
||||
int flags = p->aMem[0].flags;
|
||||
if( p->pKeyInfo->aSortOrder[0] ){
|
||||
p->r1 = 1;
|
||||
@@ -4689,7 +4708,7 @@ void sqlite3VdbePreUpdateHook(
|
||||
preupdate.iNewReg = iReg;
|
||||
preupdate.keyinfo.db = db;
|
||||
preupdate.keyinfo.enc = ENC(db);
|
||||
preupdate.keyinfo.nField = pTab->nCol;
|
||||
preupdate.keyinfo.nKeyField = pTab->nCol;
|
||||
preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
|
||||
preupdate.iKey1 = iKey1;
|
||||
preupdate.iKey2 = iKey2;
|
||||
@@ -4699,8 +4718,8 @@ void sqlite3VdbePreUpdateHook(
|
||||
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
|
||||
db->pPreUpdate = 0;
|
||||
sqlite3DbFree(db, preupdate.aRecord);
|
||||
vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pUnpacked);
|
||||
vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pNewUnpacked);
|
||||
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
|
||||
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
|
||||
if( preupdate.aNew ){
|
||||
int i;
|
||||
for(i=0; i<pCsr->nField; i++){
|
||||
|
||||
+17
-21
@@ -129,8 +129,8 @@ int sqlite3_blob_open(
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
Table *pTab;
|
||||
Parse *pParse = 0;
|
||||
Incrblob *pBlob = 0;
|
||||
Parse sParse;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppBlob==0 ){
|
||||
@@ -148,37 +148,34 @@ int sqlite3_blob_open(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
|
||||
if( !pBlob ) goto blob_open_out;
|
||||
pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
|
||||
if( !pParse ) goto blob_open_out;
|
||||
|
||||
do {
|
||||
memset(pParse, 0, sizeof(Parse));
|
||||
pParse->db = db;
|
||||
memset(&sParse, 0, sizeof(Parse));
|
||||
if( !pBlob ) goto blob_open_out;
|
||||
sParse.db = db;
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = 0;
|
||||
|
||||
sqlite3BtreeEnterAll(db);
|
||||
pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
|
||||
pTab = sqlite3LocateTable(&sParse, 0, zTable, zDb);
|
||||
if( pTab && IsVirtual(pTab) ){
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
|
||||
sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable);
|
||||
}
|
||||
if( pTab && !HasRowid(pTab) ){
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable);
|
||||
sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIEW
|
||||
if( pTab && pTab->pSelect ){
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open view: %s", zTable);
|
||||
sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
|
||||
}
|
||||
#endif
|
||||
if( !pTab ){
|
||||
if( pParse->zErrMsg ){
|
||||
if( sParse.zErrMsg ){
|
||||
sqlite3DbFree(db, zErr);
|
||||
zErr = pParse->zErrMsg;
|
||||
pParse->zErrMsg = 0;
|
||||
zErr = sParse.zErrMsg;
|
||||
sParse.zErrMsg = 0;
|
||||
}
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
@@ -242,7 +239,7 @@ int sqlite3_blob_open(
|
||||
}
|
||||
}
|
||||
|
||||
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
|
||||
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);
|
||||
assert( pBlob->pStmt || db->mallocFailed );
|
||||
if( pBlob->pStmt ){
|
||||
|
||||
@@ -315,10 +312,10 @@ int sqlite3_blob_open(
|
||||
aOp[1].p4.i = pTab->nCol+1;
|
||||
aOp[3].p2 = pTab->nCol;
|
||||
|
||||
pParse->nVar = 0;
|
||||
pParse->nMem = 1;
|
||||
pParse->nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, pParse);
|
||||
sParse.nVar = 0;
|
||||
sParse.nMem = 1;
|
||||
sParse.nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, &sParse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,8 +337,7 @@ blob_open_out:
|
||||
}
|
||||
sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(db, pParse);
|
||||
sqlite3ParserReset(&sParse);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
|
||||
+2
-2
@@ -1163,7 +1163,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
|
||||
if( pRec ){
|
||||
pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
|
||||
if( pRec->pKeyInfo ){
|
||||
assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
|
||||
assert( pRec->pKeyInfo->nAllField==nCol );
|
||||
assert( pRec->pKeyInfo->enc==ENC(db) );
|
||||
pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord)));
|
||||
for(i=0; i<nCol; i++){
|
||||
@@ -1699,7 +1699,7 @@ int sqlite3Stat4Column(
|
||||
void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
|
||||
if( pRec ){
|
||||
int i;
|
||||
int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
|
||||
int nCol = pRec->pKeyInfo->nAllField;
|
||||
Mem *aMem = pRec->aMem;
|
||||
sqlite3 *db = aMem[0].db;
|
||||
for(i=0; i<nCol; i++){
|
||||
|
||||
+7
-8
@@ -823,7 +823,7 @@ static int vdbeSorterCompareText(
|
||||
}
|
||||
|
||||
if( res==0 ){
|
||||
if( pTask->pSorter->pKeyInfo->nField>1 ){
|
||||
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
|
||||
res = vdbeSorterCompareTail(
|
||||
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
|
||||
);
|
||||
@@ -892,7 +892,7 @@ static int vdbeSorterCompareInt(
|
||||
}
|
||||
|
||||
if( res==0 ){
|
||||
if( pTask->pSorter->pKeyInfo->nField>1 ){
|
||||
if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
|
||||
res = vdbeSorterCompareTail(
|
||||
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
|
||||
);
|
||||
@@ -907,7 +907,7 @@ static int vdbeSorterCompareInt(
|
||||
/*
|
||||
** Initialize the temporary index cursor just opened as a sorter cursor.
|
||||
**
|
||||
** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nField)
|
||||
** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nKeyField)
|
||||
** to determine the number of fields that should be compared from the
|
||||
** records being sorted. However, if the value passed as argument nField
|
||||
** is non-zero and the sorter is able to guarantee a stable sort, nField
|
||||
@@ -960,7 +960,7 @@ int sqlite3VdbeSorterInit(
|
||||
|
||||
assert( pCsr->pKeyInfo && pCsr->pBtx==0 );
|
||||
assert( pCsr->eCurType==CURTYPE_SORTER );
|
||||
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*);
|
||||
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
|
||||
sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
|
||||
|
||||
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
|
||||
@@ -972,8 +972,7 @@ int sqlite3VdbeSorterInit(
|
||||
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
|
||||
pKeyInfo->db = 0;
|
||||
if( nField && nWorker==0 ){
|
||||
pKeyInfo->nXField += (pKeyInfo->nField - nField);
|
||||
pKeyInfo->nField = nField;
|
||||
pKeyInfo->nKeyField = nField;
|
||||
}
|
||||
pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
|
||||
pSorter->nTask = nWorker + 1;
|
||||
@@ -1013,7 +1012,7 @@ int sqlite3VdbeSorterInit(
|
||||
}
|
||||
}
|
||||
|
||||
if( (pKeyInfo->nField+pKeyInfo->nXField)<13
|
||||
if( pKeyInfo->nAllField<13
|
||||
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
|
||||
){
|
||||
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
|
||||
@@ -1328,7 +1327,7 @@ static int vdbeSortAllocUnpacked(SortSubtask *pTask){
|
||||
if( pTask->pUnpacked==0 ){
|
||||
pTask->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pTask->pSorter->pKeyInfo);
|
||||
if( pTask->pUnpacked==0 ) return SQLITE_NOMEM_BKPT;
|
||||
pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField;
|
||||
pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nKeyField;
|
||||
pTask->pUnpacked->errCode = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
|
||||
+48
-47
@@ -644,6 +644,7 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
|
||||
rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3ErrorMsg(pParse, "%s", zErr);
|
||||
pParse->rc = rc;
|
||||
}
|
||||
sqlite3DbFree(db, zErr);
|
||||
}
|
||||
@@ -733,10 +734,10 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
|
||||
*/
|
||||
int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
VtabCtx *pCtx;
|
||||
Parse *pParse;
|
||||
int rc = SQLITE_OK;
|
||||
Table *pTab;
|
||||
char *zErr = 0;
|
||||
Parse sParse;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
|
||||
@@ -753,55 +754,55 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
pTab = pCtx->pTab;
|
||||
assert( IsVirtual(pTab) );
|
||||
|
||||
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
|
||||
if( pParse==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
pParse->declareVtab = 1;
|
||||
pParse->db = db;
|
||||
pParse->nQueryLoop = 1;
|
||||
|
||||
if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr)
|
||||
&& pParse->pNewTable
|
||||
&& !db->mallocFailed
|
||||
&& !pParse->pNewTable->pSelect
|
||||
&& !IsVirtual(pParse->pNewTable)
|
||||
){
|
||||
if( !pTab->aCol ){
|
||||
Table *pNew = pParse->pNewTable;
|
||||
Index *pIdx;
|
||||
pTab->aCol = pNew->aCol;
|
||||
pTab->nCol = pNew->nCol;
|
||||
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
|
||||
pNew->nCol = 0;
|
||||
pNew->aCol = 0;
|
||||
assert( pTab->pIndex==0 );
|
||||
if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pIdx = pNew->pIndex;
|
||||
if( pIdx ){
|
||||
assert( pIdx->pNext==0 );
|
||||
pTab->pIndex = pIdx;
|
||||
pNew->pIndex = 0;
|
||||
pIdx->pTable = pTab;
|
||||
}
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.declareVtab = 1;
|
||||
sParse.db = db;
|
||||
sParse.nQueryLoop = 1;
|
||||
if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr)
|
||||
&& sParse.pNewTable
|
||||
&& !db->mallocFailed
|
||||
&& !sParse.pNewTable->pSelect
|
||||
&& !IsVirtual(sParse.pNewTable)
|
||||
){
|
||||
if( !pTab->aCol ){
|
||||
Table *pNew = sParse.pNewTable;
|
||||
Index *pIdx;
|
||||
pTab->aCol = pNew->aCol;
|
||||
pTab->nCol = pNew->nCol;
|
||||
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
|
||||
pNew->nCol = 0;
|
||||
pNew->aCol = 0;
|
||||
assert( pTab->pIndex==0 );
|
||||
assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
|
||||
if( !HasRowid(pNew)
|
||||
&& pCtx->pVTable->pMod->pModule->xUpdate!=0
|
||||
&& sqlite3PrimaryKeyIndex(pNew)->nKeyCol!=1
|
||||
){
|
||||
/* WITHOUT ROWID virtual tables must either be read-only (xUpdate==0)
|
||||
** or else must have a single-column PRIMARY KEY */
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pIdx = pNew->pIndex;
|
||||
if( pIdx ){
|
||||
assert( pIdx->pNext==0 );
|
||||
pTab->pIndex = pIdx;
|
||||
pNew->pIndex = 0;
|
||||
pIdx->pTable = pTab;
|
||||
}
|
||||
pCtx->bDeclared = 1;
|
||||
}else{
|
||||
sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
pParse->declareVtab = 0;
|
||||
|
||||
if( pParse->pVdbe ){
|
||||
sqlite3VdbeFinalize(pParse->pVdbe);
|
||||
}
|
||||
sqlite3DeleteTable(db, pParse->pNewTable);
|
||||
sqlite3ParserReset(pParse);
|
||||
sqlite3StackFree(db, pParse);
|
||||
pCtx->bDeclared = 1;
|
||||
}else{
|
||||
sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
|
||||
sqlite3DbFree(db, zErr);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sParse.declareVtab = 0;
|
||||
|
||||
if( sParse.pVdbe ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
}
|
||||
sqlite3DeleteTable(db, sParse.pNewTable);
|
||||
sqlite3ParserReset(&sParse);
|
||||
|
||||
assert( (rc&0xff)==rc );
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
|
||||
+1
-1
@@ -794,7 +794,7 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
|
||||
pWalker->eCode = 1;
|
||||
}else if( pExpr->op==TK_FUNCTION ){
|
||||
int d1;
|
||||
char d2[3];
|
||||
char d2[4];
|
||||
if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){
|
||||
pWalker->eCode = 1;
|
||||
}
|
||||
|
||||
+34
-6
@@ -194,12 +194,12 @@ static int isLikeOrGlob(
|
||||
int *pisComplete, /* True if the only wildcard is % in the last character */
|
||||
int *pnoCase /* True if uppercase is equivalent to lowercase */
|
||||
){
|
||||
const char *z = 0; /* String on RHS of LIKE operator */
|
||||
const u8 *z = 0; /* String on RHS of LIKE operator */
|
||||
Expr *pRight, *pLeft; /* Right and left size of LIKE operator */
|
||||
ExprList *pList; /* List of operands to the LIKE operator */
|
||||
int c; /* One character in z[] */
|
||||
int cnt; /* Number of non-wildcard prefix characters */
|
||||
char wc[3]; /* Wildcard characters */
|
||||
char wc[4]; /* Wildcard characters */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
sqlite3_value *pVal = 0;
|
||||
int op; /* Opcode of pRight */
|
||||
@@ -221,12 +221,12 @@ static int isLikeOrGlob(
|
||||
int iCol = pRight->iColumn;
|
||||
pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
|
||||
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
|
||||
z = (char *)sqlite3_value_text(pVal);
|
||||
z = sqlite3_value_text(pVal);
|
||||
}
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
|
||||
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
|
||||
}else if( op==TK_STRING ){
|
||||
z = pRight->u.zToken;
|
||||
z = (u8*)pRight->u.zToken;
|
||||
}
|
||||
if( z ){
|
||||
|
||||
@@ -246,16 +246,44 @@ static int isLikeOrGlob(
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Count the number of prefix characters prior to the first wildcard */
|
||||
cnt = 0;
|
||||
while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
|
||||
cnt++;
|
||||
if( c==wc[3] && z[cnt]!=0 ){
|
||||
if( z[cnt++]>0xc0 ) while( (z[cnt]&0xc0)==0x80 ){ cnt++; }
|
||||
}
|
||||
}
|
||||
|
||||
/* The optimization is possible only if (1) the pattern does not begin
|
||||
** with a wildcard and if (2) the non-wildcard prefix does not end with
|
||||
** an (illegal 0xff) character. The second condition is necessary so
|
||||
** that we can increment the prefix key to find an upper bound for the
|
||||
** range search.
|
||||
*/
|
||||
if( cnt!=0 && 255!=(u8)z[cnt-1] ){
|
||||
Expr *pPrefix;
|
||||
|
||||
/* A "complete" match if the pattern ends with "*" or "%" */
|
||||
*pisComplete = c==wc[0] && z[cnt+1]==0;
|
||||
pPrefix = sqlite3Expr(db, TK_STRING, z);
|
||||
if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
|
||||
|
||||
/* Get the pattern prefix. Remove all escapes from the prefix. */
|
||||
pPrefix = sqlite3Expr(db, TK_STRING, (char*)z);
|
||||
if( pPrefix ){
|
||||
int iFrom, iTo;
|
||||
char *zNew = pPrefix->u.zToken;
|
||||
zNew[cnt] = 0;
|
||||
for(iFrom=iTo=0; iFrom<cnt; iFrom++){
|
||||
if( zNew[iFrom]==wc[3] ) iFrom++;
|
||||
zNew[iTo++] = zNew[iFrom];
|
||||
}
|
||||
zNew[iTo] = 0;
|
||||
}
|
||||
*ppPrefix = pPrefix;
|
||||
|
||||
/* If the RHS pattern is a bound parameter, make arrangements to
|
||||
** reprepare the statement when that parameter is rebound */
|
||||
if( op==TK_VARIABLE ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
sqlite3VdbeSetVarmask(v, pRight->iColumn);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# 2015-11-07
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the WITH clause.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix atomic
|
||||
|
||||
db close
|
||||
if {[atomic_batch_write test.db]==0} {
|
||||
puts "No f2fs atomic-batch-write support. Skipping tests..."
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
reset_db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
}
|
||||
|
||||
do_test 1.1 { file exists test.db-journal } {0}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
+3
-8
@@ -374,8 +374,9 @@ do_test attach2-6.1 {
|
||||
do_test attach2-6.2 {
|
||||
catchsql {
|
||||
ATTACH 'test3.db' as aux2;
|
||||
DETACH aux2;
|
||||
}
|
||||
} {1 {cannot ATTACH database within transaction}}
|
||||
} {0 {}}
|
||||
|
||||
# EVIDENCE-OF: R-59740-55581 This statement will fail if SQLite is in
|
||||
# the middle of a transaction.
|
||||
@@ -384,13 +385,7 @@ do_test attach2-6.3 {
|
||||
catchsql {
|
||||
DETACH aux;
|
||||
}
|
||||
} {1 {cannot DETACH database within transaction}}
|
||||
do_test attach2-6.4 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
DETACH aux;
|
||||
}
|
||||
} {}
|
||||
} {0 {}}
|
||||
|
||||
db close
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# 2017 August 07
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script testing the ability of SQLite to use mmap
|
||||
# to access files larger than 4GiB.
|
||||
#
|
||||
|
||||
if {[file exists skip-big-file]} return
|
||||
if {$tcl_platform(os)=="Darwin"} return
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix bigmmap
|
||||
|
||||
ifcapable !mmap {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set mmap_limit 0
|
||||
db eval {
|
||||
SELECT compile_options AS x FROM pragma_compile_options
|
||||
WHERE x LIKE 'max_mmap_size=%'
|
||||
} {
|
||||
regexp {MAX_MMAP_SIZE=([0-9]*)} $x -> mmap_limit
|
||||
}
|
||||
if {$mmap_limit < [expr 8 * 1<<30]} {
|
||||
puts "Skipping bigmmap.test - requires SQLITE_MAX_MMAP_SIZE >= 8G"
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Create the database file roughly 8GiB in size. Most pages are unused,
|
||||
# except that there is a table and index clustered around each 1GiB
|
||||
# boundary.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA page_size = 4096;
|
||||
CREATE TABLE t0(a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c));
|
||||
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 )
|
||||
INSERT INTO t0 SELECT i, 't0', randomblob(800) FROM s;
|
||||
}
|
||||
|
||||
for {set i 1} {$i < 8} {incr i} {
|
||||
fake_big_file [expr $i*1024] [get_pwd]/test.db
|
||||
hexio_write test.db 28 [format %.8x [expr ($i*1024*1024*1024/4096) - 5]]
|
||||
|
||||
do_execsql_test 1.$i "
|
||||
CREATE TABLE t$i (a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c));
|
||||
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 )
|
||||
INSERT INTO t$i SELECT i, 't$i', randomblob(800) FROM s;
|
||||
"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that data can be retrieved from the db with a variety of
|
||||
# configured mmap size limits.
|
||||
#
|
||||
for {set i 0} {$i < 9} {incr i} {
|
||||
|
||||
# Configure a memory mapping $i GB in size.
|
||||
#
|
||||
set val [expr $i*1024*1024*1024]
|
||||
execsql "PRAGMA main.mmap_size = $val"
|
||||
do_execsql_test 2.$i.0 {
|
||||
PRAGMA main.mmap_size
|
||||
} $val
|
||||
|
||||
for {set t 0} {$t < 8} {incr t} {
|
||||
do_execsql_test 2.$i.$t.1 "
|
||||
SELECT count(*) FROM t$t;
|
||||
SELECT count(b || c) FROM t$t GROUP BY b;
|
||||
" {100 100}
|
||||
|
||||
do_execsql_test 2.$i.$t.2 "
|
||||
SELECT * FROM t$t AS o WHERE
|
||||
NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c )
|
||||
ORDER BY b, c;
|
||||
" {}
|
||||
|
||||
do_eqp_test 2.$i.$t.3 "
|
||||
SELECT * FROM t$t AS o WHERE
|
||||
NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c )
|
||||
ORDER BY b, c;
|
||||
" "
|
||||
0 0 0 {SCAN TABLE t$t AS o USING COVERING INDEX sqlite_autoindex_t${t}_1}
|
||||
0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1}
|
||||
1 0 0 {SEARCH TABLE t$t AS i USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
"
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+35
-3
@@ -93,6 +93,7 @@ do_catchsql_test 3.2 {
|
||||
SELECT rowid, a FROM t3;
|
||||
} {1 {no such column: rowid}}
|
||||
|
||||
# Multi-column WITHOUT ROWID virtual tables may not be writable.
|
||||
do_catchsql_test 4.0 {
|
||||
DROP TABLE t3;
|
||||
CREATE VIRTUAL TABLE temp.t4 USING csv_wr(
|
||||
@@ -100,13 +101,44 @@ do_catchsql_test 4.0 {
|
||||
'1,2,3,4
|
||||
5,6,7,8
|
||||
9,10,11,12
|
||||
13,14,15,16
|
||||
',
|
||||
13,14,15,16',
|
||||
columns=4,
|
||||
schema=
|
||||
'CREATE TABLE t3(a PRIMARY KEY,b TEXT,c TEXT,d TEXT) WITHOUT ROWID',
|
||||
'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(a,b)) WITHOUT ROWID',
|
||||
testflags=1
|
||||
);
|
||||
} {1 {vtable constructor failed: t4}}
|
||||
|
||||
# WITHOUT ROWID tables with a single-column PRIMARY KEY may be writable.
|
||||
do_catchsql_test 4.1 {
|
||||
DROP TABLE IF EXISTS t4;
|
||||
CREATE VIRTUAL TABLE temp.t4 USING csv_wr(
|
||||
data=
|
||||
'1,2,3,4
|
||||
5,6,7,8
|
||||
9,10,11,12
|
||||
13,14,15,16',
|
||||
columns=4,
|
||||
schema=
|
||||
'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(b)) WITHOUT ROWID',
|
||||
testflags=1
|
||||
);
|
||||
} {0 {}}
|
||||
|
||||
do_catchsql_test 4.2 {
|
||||
DROP TABLE IF EXISTS t5;
|
||||
CREATE VIRTUAL TABLE temp.t5 USING csv_wr(
|
||||
data=
|
||||
'1,2,3,4
|
||||
5,6,7,8
|
||||
9,10,11,12
|
||||
13,14,15,16',
|
||||
columns=4,
|
||||
schema=
|
||||
'CREATE TABLE t3(a,b,c,d) WITHOUT ROWID',
|
||||
testflags=1
|
||||
);
|
||||
} {1 {vtable constructor failed: t5}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+3
-1
@@ -59,7 +59,9 @@ do_test fallocate-1.6 {
|
||||
#
|
||||
do_test fallocate-1.7 {
|
||||
execsql { BEGIN; INSERT INTO t1 VALUES(1, 2); }
|
||||
if {[permutation] != "inmemory_journal"} {
|
||||
if {[permutation] != "inmemory_journal"
|
||||
&& [permutation] != "atomic-batch-write"
|
||||
} {
|
||||
hexio_get_int [hexio_read test.db-journal 16 4]
|
||||
} else {
|
||||
set {} 1024
|
||||
|
||||
+49
-1
@@ -207,7 +207,7 @@ do_test like-3.3.100 {
|
||||
SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
|
||||
}
|
||||
} {abc abcd nosort {} i1}
|
||||
do_test like-3.3.101 {
|
||||
do_test like-3.3.100.cnt {
|
||||
set sqlite_like_count
|
||||
} 0
|
||||
|
||||
@@ -1048,4 +1048,52 @@ ifcapable !icu {
|
||||
} {1}
|
||||
}
|
||||
|
||||
# As of 2017-07-27 (3.21.0) the LIKE optimization works with ESCAPE as
|
||||
# long as the ESCAPE is a single-byte literal.
|
||||
#
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
do_execsql_test like-15.100 {
|
||||
CREATE TABLE t15(x TEXT COLLATE nocase, y, PRIMARY KEY(x));
|
||||
INSERT INTO t15(x,y) VALUES
|
||||
('abcde',1), ('ab%de',2), ('a_cde',3),
|
||||
('uvwxy',11),('uvwx%',12),('uvwx_',13),
|
||||
('_bcde',21),('%bcde',22),
|
||||
('abcd_',31),('abcd%',32),
|
||||
('ab%xy',41);
|
||||
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/';
|
||||
} {2}
|
||||
do_execsql_test like-15.101 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '/';
|
||||
} {/SEARCH/}
|
||||
do_execsql_test like-15.102 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '//';
|
||||
} {/SCAN/}
|
||||
do_execsql_test like-15.103 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT y FROM t15 WHERE x LIKE 'ab/%d%' ESCAPE '';
|
||||
} {/SCAN/}
|
||||
do_execsql_test like-15.110 {
|
||||
SELECT y FROM t15 WHERE x LIKE 'abcdx%%' ESCAPE 'x';
|
||||
} {32}
|
||||
do_execsql_test like-15.111 {
|
||||
SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y
|
||||
} {2 41}
|
||||
do_execsql_test like-15.112 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT y FROM t15 WHERE x LIKE 'abx%%' ESCAPE 'x' ORDER BY +y
|
||||
} {/SEARCH/}
|
||||
do_execsql_test like-15.120 {
|
||||
SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/';
|
||||
} {22}
|
||||
do_execsql_test like-15.121 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT y FROM t15 WHERE x LIKE '/%bc%' ESCAPE '/';
|
||||
} {/SEARCH/}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+22
-20
@@ -479,26 +479,28 @@ ifcapable curdir {
|
||||
# Make sure a database connection still works after changing the
|
||||
# working directory.
|
||||
#
|
||||
do_test misc1-14.1 {
|
||||
file mkdir tempdir
|
||||
cd tempdir
|
||||
execsql {BEGIN}
|
||||
file exists ./test.db-journal
|
||||
} {0}
|
||||
do_test misc1-14.2a {
|
||||
execsql {UPDATE t1 SET a=a||'x' WHERE 0}
|
||||
file exists ../test.db-journal
|
||||
} {0}
|
||||
do_test misc1-14.2b {
|
||||
execsql {UPDATE t1 SET a=a||'y' WHERE 1}
|
||||
file exists ../test.db-journal
|
||||
} {1}
|
||||
do_test misc1-14.3 {
|
||||
cd ..
|
||||
forcedelete tempdir
|
||||
execsql {COMMIT}
|
||||
file exists ./test.db-journal
|
||||
} {0}
|
||||
if {[atomic_batch_write test.db]==0} {
|
||||
do_test misc1-14.1 {
|
||||
file mkdir tempdir
|
||||
cd tempdir
|
||||
execsql {BEGIN}
|
||||
file exists ./test.db-journal
|
||||
} {0}
|
||||
do_test misc1-14.2a {
|
||||
execsql {UPDATE t1 SET a=a||'x' WHERE 0}
|
||||
file exists ../test.db-journal
|
||||
} {0}
|
||||
do_test misc1-14.2b {
|
||||
execsql {UPDATE t1 SET a=a||'y' WHERE 1}
|
||||
file exists ../test.db-journal
|
||||
} {1}
|
||||
do_test misc1-14.3 {
|
||||
cd ..
|
||||
forcedelete tempdir
|
||||
execsql {COMMIT}
|
||||
file exists ./test.db-journal
|
||||
} {0}
|
||||
}
|
||||
}
|
||||
|
||||
# A failed create table should not leave the table in the internal
|
||||
|
||||
@@ -70,6 +70,28 @@ static int progress_handler(void *pClientData) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and
|
||||
** "PRAGMA parser_trace" since they can dramatically increase the
|
||||
** amount of output without actually testing anything useful.
|
||||
*/
|
||||
static int block_debug_pragmas(
|
||||
void *Notused,
|
||||
int eCode,
|
||||
const char *zArg1,
|
||||
const char *zArg2,
|
||||
const char *zArg3,
|
||||
const char *zArg4
|
||||
){
|
||||
if( eCode==SQLITE_PRAGMA
|
||||
&& (sqlite3_strnicmp("vdbe_", zArg1, 5)==0
|
||||
|| sqlite3_stricmp("parser_trace", zArg1)==0)
|
||||
){
|
||||
return SQLITE_DENY;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Callback for sqlite3_exec().
|
||||
*/
|
||||
@@ -128,6 +150,9 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
sqlite3_db_config(cx.db, SQLITE_DBCONFIG_ENABLE_FKEY, uSelector&1, &rc);
|
||||
uSelector >>= 1;
|
||||
|
||||
/* Do not allow debugging pragma statements that might cause excess output */
|
||||
sqlite3_set_authorizer(cx.db, block_debug_pragmas, 0);
|
||||
|
||||
/* Remaining bits of the selector determine a limit on the number of
|
||||
** output rows */
|
||||
execCnt = uSelector + 1;
|
||||
|
||||
@@ -389,6 +389,30 @@ test_suite "vfslog" -prefix "" -description {
|
||||
wal* mmap*
|
||||
]
|
||||
|
||||
test_suite "atomic-batch-write" -prefix "" -description {
|
||||
Like veryquick.test, but must be run on a file-system that supports
|
||||
atomic-batch-writes. Tests that depend on the journal file being present
|
||||
are omitted.
|
||||
} -files [
|
||||
test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* \
|
||||
*fts5corrupt* *fts5big* *fts5aj* \
|
||||
crash8.test delete_db.test \
|
||||
exclusive.test journal3.test \
|
||||
journal1.test \
|
||||
jrnlmode.test jrnlmode2.test \
|
||||
lock4.test pager1.test \
|
||||
pager3.test sharedA.test \
|
||||
symlink.test stmt.test \
|
||||
sync.test sync2.test \
|
||||
tempdb.test tkt3457.test \
|
||||
vacuum5.test wal2.test \
|
||||
walmode.test zerodamage.test
|
||||
] -initialize {
|
||||
if {[atomic_batch_write test.db]==0} {
|
||||
error "File system does NOT support atomic-batch-write"
|
||||
}
|
||||
}
|
||||
|
||||
lappend ::testsuitelist xxx
|
||||
#-------------------------------------------------------------------------
|
||||
# Define the coverage related test suites:
|
||||
|
||||
@@ -82,6 +82,7 @@ do_test rollback-1.9 {
|
||||
if {$tcl_platform(platform) == "unix"
|
||||
&& [permutation] ne "onefile"
|
||||
&& [permutation] ne "inmemory_journal"
|
||||
&& [permutation] ne "atomic-batch-write"
|
||||
} {
|
||||
do_test rollback-2.1 {
|
||||
execsql {
|
||||
|
||||
+7
-2
@@ -616,12 +616,16 @@ ifcapable auth {
|
||||
# First make sure it is not possible to attach or detach a database while
|
||||
# a savepoint is open (it is not possible if any transaction is open).
|
||||
#
|
||||
# UPDATE 2017-07-26: It is not possible to ATTACH and DETACH within a
|
||||
# a transaction.
|
||||
#
|
||||
do_test savepoint-10.1.1 {
|
||||
catchsql {
|
||||
SAVEPOINT one;
|
||||
ATTACH 'test2.db' AS aux;
|
||||
DETACH aux;
|
||||
}
|
||||
} {1 {cannot ATTACH database within transaction}}
|
||||
} {0 {}}
|
||||
do_test savepoint-10.1.2 {
|
||||
execsql {
|
||||
RELEASE one;
|
||||
@@ -630,8 +634,9 @@ do_test savepoint-10.1.2 {
|
||||
catchsql {
|
||||
SAVEPOINT one;
|
||||
DETACH aux;
|
||||
ATTACH 'test2.db' AS aux;
|
||||
}
|
||||
} {1 {cannot DETACH database within transaction}}
|
||||
} {0 {}}
|
||||
do_test savepoint-10.1.3 {
|
||||
execsql {
|
||||
RELEASE one;
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
# 2017-07-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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements tests to show that certain CREATE TABLE statements
|
||||
# generate identical database files. For example, changes in identifier
|
||||
# names, white-space, and formatting of the CREATE TABLE statement should
|
||||
# produce identical table content.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix schema6
|
||||
|
||||
# Command: check_same_database_content TESTNAME SQL1 SQL2 SQL3 ...
|
||||
#
|
||||
# This command creates fresh databases using SQL1 and subsequent arguments
|
||||
# and checks to make sure the content of all database files is byte-for-byte
|
||||
# identical. Page 1 of the database files is allowed to be different, since
|
||||
# page 1 contains the sqlite_master table which is expected to vary.
|
||||
#
|
||||
proc check_same_database_content {basename args} {
|
||||
set i 0
|
||||
set hash {}
|
||||
foreach sql $args {
|
||||
catch {db close}
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval $sql
|
||||
set pgsz [db one {PRAGMA page_size}]
|
||||
db close
|
||||
set sz [file size test.db]
|
||||
set thishash [md5file test.db $pgsz [expr {$sz-$pgsz}]]
|
||||
if {$i==0} {
|
||||
set hash $thishash
|
||||
} else {
|
||||
do_test $basename-$i "set x $thishash" $hash
|
||||
}
|
||||
incr i
|
||||
}
|
||||
}
|
||||
|
||||
# Command: check_different_database_content TESTNAME SQL1 SQL2 SQL3 ...
|
||||
#
|
||||
# This command creates fresh databases using SQL1 and subsequent arguments
|
||||
# and checks to make sure the content of all database files is different
|
||||
# in ways other than on page 1.
|
||||
#
|
||||
proc check_different_database_content {basename args} {
|
||||
set i 0
|
||||
set hashes {}
|
||||
foreach sql $args {
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db eval $sql
|
||||
set pgsz [db one {PRAGMA page_size}]
|
||||
db close
|
||||
set sz [file size test.db]
|
||||
set thishash [md5file test.db $pgsz [expr {$sz-$pgsz}]]
|
||||
set j [lsearch $hashes $thishash]
|
||||
if {$j>=0} {
|
||||
do_test $basename-$i "set x {$i is the same as $j}" "All are different"
|
||||
} else {
|
||||
do_test $basename-$i "set x {All are different}" "All are different"
|
||||
}
|
||||
lappend hashes $thishash
|
||||
incr i
|
||||
}
|
||||
}
|
||||
|
||||
check_same_database_content 100 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(xyz INTEGER, abc, PRIMARY KEY(xyz), UNIQUE(abc));
|
||||
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(xyz INTEGER, abc, UNIQUE(abc), PRIMARY KEY(xyz));
|
||||
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ASC, b UNIQUE);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE UNIQUE INDEX t1b ON t1(b);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
CREATE UNIQUE INDEX t1b ON t1(b);
|
||||
}
|
||||
|
||||
check_same_database_content 110 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE, UNIQUE(a));
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b);
|
||||
CREATE UNIQUE INDEX t1b ON t1(b);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
CREATE UNIQUE INDEX t1b ON t1(b);
|
||||
}
|
||||
|
||||
check_same_database_content 120 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(xyz INTEGER, abc, PRIMARY KEY(xyz), UNIQUE(abc))WITHOUT ROWID;
|
||||
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(xyz INTEGER, abc, UNIQUE(abc), PRIMARY KEY(xyz))WITHOUT ROWID;
|
||||
INSERT INTO t1(xyz,abc) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ASC, b UNIQUE) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER UNIQUE PRIMARY KEY, b UNIQUE, UNIQUE(a))
|
||||
WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
|
||||
CREATE UNIQUE INDEX t1b ON t1(b);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
CREATE UNIQUE INDEX t1b ON t1(b);
|
||||
}
|
||||
|
||||
check_different_database_content 130 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY UNIQUE, b UNIQUE);
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
} {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b) VALUES(123,'Four score and seven years ago...');
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,246 @@
|
||||
# 2017-07-15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is the "swarmvtab" extension
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix swarmvtab
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
load_static_extension db unionvtab
|
||||
|
||||
set nFile $sqlite_open_file_count
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t0(a INTEGER PRIMARY KEY, b TEXT);
|
||||
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<400)
|
||||
INSERT INTO t0 SELECT i, hex(randomblob(50)) FROM s;
|
||||
|
||||
CREATE TABLE dir(f, t, imin, imax);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
for {set i 0} {$i < 40} {incr i} {
|
||||
set iMin [expr $i*10 + 1]
|
||||
set iMax [expr $iMin+9]
|
||||
|
||||
forcedelete "test.db$i"
|
||||
execsql [subst {
|
||||
ATTACH 'test.db$i' AS aux;
|
||||
CREATE TABLE aux.t$i (a INTEGER PRIMARY KEY, b TEXT);
|
||||
INSERT INTO aux.t$i SELECT * FROM t0 WHERE a BETWEEN $iMin AND $iMax;
|
||||
DETACH aux;
|
||||
INSERT INTO dir VALUES('test.db$i', 't$i', $iMin, $iMax);
|
||||
}]
|
||||
}
|
||||
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir');
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
DROP TABLE s1;
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir');
|
||||
SELECT count(*) FROM s1 WHERE rowid<50;
|
||||
} {49}
|
||||
|
||||
proc do_compare_test {tn where} {
|
||||
set sql [subst {
|
||||
SELECT (SELECT group_concat(a || ',' || b, ',') FROM t0 WHERE $where)
|
||||
IS
|
||||
(SELECT group_concat(a || ',' || b, ',') FROM s1 WHERE $where)
|
||||
}]
|
||||
|
||||
uplevel [list do_execsql_test $tn $sql 1]
|
||||
}
|
||||
|
||||
do_compare_test 1.4.1 "rowid = 700"
|
||||
do_compare_test 1.4.2 "rowid = -1"
|
||||
do_compare_test 1.4.3 "rowid = 0"
|
||||
do_compare_test 1.4.4 "rowid = 55"
|
||||
do_compare_test 1.4.5 "rowid BETWEEN 20 AND 100"
|
||||
do_compare_test 1.4.6 "rowid > 350"
|
||||
do_compare_test 1.4.7 "rowid >= 350"
|
||||
do_compare_test 1.4.8 "rowid >= 200"
|
||||
do_compare_test 1.4.9 "1"
|
||||
|
||||
# Multiple simultaneous cursors.
|
||||
#
|
||||
do_execsql_test 1.5.1.(5-seconds-or-so) {
|
||||
SELECT count(*) FROM s1 a, s1 b WHERE b.rowid<=200;
|
||||
} {80000}
|
||||
do_execsql_test 1.5.2 {
|
||||
SELECT count(*) FROM s1 a, s1 b, s1 c
|
||||
WHERE a.rowid=b.rowid AND b.rowid=c.rowid;
|
||||
} {400}
|
||||
|
||||
# Empty source tables.
|
||||
#
|
||||
do_test 1.6.0 {
|
||||
for {set i 0} {$i < 20} {incr i} {
|
||||
sqlite3 db2 test.db$i
|
||||
db2 eval " DELETE FROM t$i "
|
||||
db2 close
|
||||
}
|
||||
db eval { DELETE FROM t0 WHERE rowid<=200 }
|
||||
} {}
|
||||
|
||||
do_compare_test 1.6.1 "rowid = 700"
|
||||
do_compare_test 1.6.2 "rowid = -1"
|
||||
do_compare_test 1.6.3 "rowid = 0"
|
||||
do_compare_test 1.6.4 "rowid = 55"
|
||||
do_compare_test 1.6.5 "rowid BETWEEN 20 AND 100"
|
||||
do_compare_test 1.6.6 "rowid > 350"
|
||||
do_compare_test 1.6.7 "rowid >= 350"
|
||||
do_compare_test 1.6.8 "rowid >= 200"
|
||||
do_compare_test 1.6.9 "1"
|
||||
do_compare_test 1.6.10 "rowid >= 5"
|
||||
|
||||
do_test 1.x {
|
||||
set sqlite_open_file_count
|
||||
} [expr $nFile+9]
|
||||
|
||||
do_test 1.y { db close } {}
|
||||
|
||||
# Delete all the database files created above.
|
||||
#
|
||||
for {set i 0} {$i < 40} {incr i} { forcedelete "test.db$i" }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test some error conditions:
|
||||
#
|
||||
# 2.1: Database file does not exist.
|
||||
# 2.2: Table does not exist.
|
||||
# 2.3: Table schema does not match.
|
||||
# 2.4: Syntax error in SELECT statement.
|
||||
#
|
||||
reset_db
|
||||
load_static_extension db unionvtab
|
||||
do_test 2.0.1 {
|
||||
db eval {
|
||||
CREATE TABLE t0(a INTEGER PRIMARY KEY, b TEXT);
|
||||
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<400)
|
||||
INSERT INTO t0 SELECT i, hex(randomblob(50)) FROM s;
|
||||
CREATE TABLE dir(f, t, imin, imax);
|
||||
}
|
||||
|
||||
for {set i 0} {$i < 40} {incr i} {
|
||||
set iMin [expr $i*10 + 1]
|
||||
set iMax [expr $iMin+9]
|
||||
|
||||
forcedelete "test.db$i"
|
||||
db eval [subst {
|
||||
ATTACH 'test.db$i' AS aux;
|
||||
CREATE TABLE aux.t$i (a INTEGER PRIMARY KEY, b TEXT);
|
||||
INSERT INTO aux.t$i SELECT * FROM t0 WHERE a BETWEEN $iMin AND $iMax;
|
||||
DETACH aux;
|
||||
INSERT INTO dir VALUES('test.db$i', 't$i', $iMin, $iMax);
|
||||
}]
|
||||
}
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE temp.s1 USING swarmvtab('SELECT * FROM dir');
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 2.0.2 {
|
||||
forcedelete test.db5
|
||||
|
||||
sqlite3 db2 test.db15
|
||||
db2 eval { DROP TABLE t15 }
|
||||
db2 close
|
||||
|
||||
sqlite3 db2 test.db25
|
||||
db2 eval {
|
||||
DROP TABLE t25;
|
||||
CREATE TABLE t25(x, y, z PRIMARY KEY);
|
||||
}
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
SELECT * FROM s1 WHERE rowid BETWEEN 1 AND 100;
|
||||
} {1 {unable to open database file}}
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT * FROM s1 WHERE rowid BETWEEN 101 AND 200;
|
||||
} {1 {no such rowid table: t15}}
|
||||
do_catchsql_test 2.3 {
|
||||
SELECT * FROM s1 WHERE rowid BETWEEN 201 AND 300;
|
||||
} {1 {source table schema mismatch}}
|
||||
|
||||
do_catchsql_test 2.4 {
|
||||
CREATE VIRTUAL TABLE temp.x1 USING swarmvtab('SELECT * FROMdir');
|
||||
} {1 {sql error: near "FROMdir": syntax error}}
|
||||
do_catchsql_test 2.5 {
|
||||
CREATE VIRTUAL TABLE temp.x1 USING swarmvtab('SELECT * FROMdir', 'fetchdb');
|
||||
} {1 {sql error: near "FROMdir": syntax error}}
|
||||
|
||||
for {set i 0} {$i < 40} {incr i} {
|
||||
forcedelete "test.db$i"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the outcome of the fetch function throwing an exception.
|
||||
#
|
||||
proc fetch_db {file} {
|
||||
error "fetch_db error!"
|
||||
}
|
||||
|
||||
db func fetch_db fetch_db
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
|
||||
'VALUES
|
||||
("test.db1", "t1", 1, 10),
|
||||
("test.db2", "t1", 11, 20)
|
||||
', 'fetch_db_no_such_function'
|
||||
);
|
||||
} {1 {no such function: fetch_db_no_such_function}}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
|
||||
'VALUES
|
||||
("test.db1", "t1", 1, 10),
|
||||
("test.db2", "t1", 11, 20)
|
||||
', 'fetch_db'
|
||||
);
|
||||
} {1 {fetch_db error!}}
|
||||
|
||||
do_execsql_test 3.3.1 {
|
||||
ATTACH 'test.db1' AS aux;
|
||||
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO aux.t1 VALUES(1, NULL);
|
||||
INSERT INTO aux.t1 VALUES(2, NULL);
|
||||
INSERT INTO aux.t1 VALUES(9, NULL);
|
||||
DETACH aux;
|
||||
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
|
||||
'VALUES
|
||||
("test.db1", "t1", 1, 10),
|
||||
("test.db2", "t1", 11, 20)
|
||||
', 'fetch_db'
|
||||
);
|
||||
} {}
|
||||
|
||||
do_catchsql_test 3.3.2 { SELECT * FROM xyz } {1 {fetch_db error!}}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# 2017-07-15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is the "swarmvtab" extension
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix swarmvtab
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
db close
|
||||
foreach name [glob -nocomplain test*.db] {
|
||||
forcedelete $name
|
||||
}
|
||||
sqlite3 db test.db
|
||||
load_static_extension db unionvtab
|
||||
proc create_database {filename} {
|
||||
sqlite3 dbx $filename
|
||||
set num [regsub -all {[^0-9]+} $filename {}]
|
||||
set num [string trimleft $num 0]
|
||||
set start [expr {$num*1000}]
|
||||
set end [expr {$start+999}]
|
||||
dbx eval {
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY,b);
|
||||
WITH RECURSIVE c(x) AS (
|
||||
VALUES($start) UNION ALL SELECT x+1 FROM c WHERE x<$end
|
||||
)
|
||||
INSERT INTO t2(a,b) SELECT x, printf('**%05d**',x) FROM c;
|
||||
}
|
||||
dbx close
|
||||
}
|
||||
db func create_database create_database
|
||||
do_execsql_test 100 {
|
||||
CREATE TABLE t1(filename, tablename, istart, iend);
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<99)
|
||||
INSERT INTO t1 SELECT printf('test%03d.db',x),'t2',x*1000,x*1000+999 FROM c;
|
||||
CREATE VIRTUAL TABLE temp.v1 USING swarmvtab(
|
||||
'SELECT * FROM t1', 'create_database'
|
||||
);
|
||||
} {}
|
||||
do_execsql_test 110 {
|
||||
SELECT b FROM v1 WHERE a=3875;
|
||||
} {**03875**}
|
||||
do_test 120 {
|
||||
lsort [glob -nocomplain test?*.db]
|
||||
} {test001.db test003.db}
|
||||
do_execsql_test 130 {
|
||||
SELECT b FROM v1 WHERE a BETWEEN 3999 AND 4000 ORDER BY a;
|
||||
} {**03999** **04000**}
|
||||
do_test 140 {
|
||||
lsort [glob -nocomplain test?*.db]
|
||||
} {test001.db test003.db test004.db}
|
||||
do_execsql_test 150 {
|
||||
SELECT b FROM v1 WHERE a>=99998;
|
||||
} {**99998** **99999**}
|
||||
do_test 160 {
|
||||
lsort -dictionary [glob -nocomplain test?*.db]
|
||||
} {test001.db test003.db test004.db test099.db}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,61 @@
|
||||
# 2017-07-15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is error handling in the swarmvtab extension.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix swarmvtabfault
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc fetch_db {file} {
|
||||
forcedelete $file
|
||||
sqlite3 dbX $file
|
||||
dbX eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b) }
|
||||
dbX close
|
||||
}
|
||||
|
||||
forcedelete test.db1
|
||||
do_execsql_test 1.0 {
|
||||
ATTACH 'test.db1' AS aux;
|
||||
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO aux.t1 VALUES(1, NULL);
|
||||
INSERT INTO aux.t1 VALUES(2, NULL);
|
||||
INSERT INTO aux.t1 VALUES(9, NULL);
|
||||
DETACH aux;
|
||||
} {}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 1.1 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db func fetch_db fetch_db
|
||||
load_static_extension db unionvtab
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE temp.xyz USING swarmvtab(
|
||||
'VALUES
|
||||
("test.db1", "t1", 1, 10),
|
||||
("test.db2", "t1", 11, 20)
|
||||
', 'fetch_db'
|
||||
);
|
||||
}
|
||||
} -body {
|
||||
execsql { SELECT a FROM xyz }
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 2 9}} {1 {sql error: out of memory}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ foreach s {
|
||||
fcntl read pread write pwrite fchmod fallocate
|
||||
pread64 pwrite64 unlink openDirectory mkdir rmdir
|
||||
statvfs fchown geteuid umask mmap munmap mremap
|
||||
getpagesize readlink lstat
|
||||
getpagesize readlink lstat ioctl
|
||||
} {
|
||||
if {[test_syscall exists $s]} {lappend syscall_list $s}
|
||||
}
|
||||
|
||||
@@ -1608,6 +1608,54 @@ proc crashsql {args} {
|
||||
lappend r $msg
|
||||
}
|
||||
|
||||
# crash_on_write ?-devchar DEVCHAR? CRASHDELAY SQL
|
||||
#
|
||||
proc crash_on_write {args} {
|
||||
|
||||
set nArg [llength $args]
|
||||
if {$nArg<2 || $nArg%2} {
|
||||
error "bad args: $args"
|
||||
}
|
||||
set zSql [lindex $args end]
|
||||
set nDelay [lindex $args end-1]
|
||||
|
||||
set devchar {}
|
||||
for {set ii 0} {$ii < $nArg-2} {incr ii 2} {
|
||||
set opt [lindex $args $ii]
|
||||
switch -- [lindex $args $ii] {
|
||||
-devchar {
|
||||
set devchar [lindex $args [expr $ii+1]]
|
||||
}
|
||||
|
||||
default { error "unrecognized option: $opt" }
|
||||
}
|
||||
}
|
||||
|
||||
set f [open crash.tcl w]
|
||||
puts $f "sqlite3_crash_on_write $nDelay"
|
||||
puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
|
||||
puts $f "sqlite3 db test.db -vfs writecrash"
|
||||
puts $f "db eval {$zSql}"
|
||||
puts $f "set {} {}"
|
||||
|
||||
close $f
|
||||
set r [catch {
|
||||
exec [info nameofexec] crash.tcl >@stdout
|
||||
} msg]
|
||||
|
||||
# Windows/ActiveState TCL returns a slightly different
|
||||
# error message. We map that to the expected message
|
||||
# so that we don't have to change all of the test
|
||||
# cases.
|
||||
if {$::tcl_platform(platform)=="windows"} {
|
||||
if {$msg=="child killed: unknown signal"} {
|
||||
set msg "child process exited abnormally"
|
||||
}
|
||||
}
|
||||
|
||||
lappend r $msg
|
||||
}
|
||||
|
||||
proc run_ioerr_prep {} {
|
||||
set ::sqlite_io_error_pending 0
|
||||
catch {db close}
|
||||
|
||||
@@ -67,6 +67,18 @@ do_faultsim_test 1.2 -faults oom* -prep {
|
||||
faultsim_test_result {0 {1 one 2 two 3 three 10 ten 11 eleven 12 twelve 20 twenty 21 twenty-one 22 twenty-two}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Error while registering the two vtab modules.
|
||||
do_faultsim_test 2.0 -faults * -prep {
|
||||
catch { db close }
|
||||
sqlite3 db :memory:
|
||||
} -body {
|
||||
load_static_extension db unionvtab
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 {initialization of unionvtab failed: }}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+2
-2
@@ -60,7 +60,7 @@ do_test vtab2-2.1 {
|
||||
set ::abc 123
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE vars USING tclvar;
|
||||
SELECT * FROM vars WHERE name='abc';
|
||||
SELECT name, arrayname, value FROM vars WHERE name='abc';
|
||||
}
|
||||
} [list abc "" 123]
|
||||
do_test vtab2-2.2 {
|
||||
@@ -68,7 +68,7 @@ do_test vtab2-2.2 {
|
||||
set A(2) 4
|
||||
set A(3) 9
|
||||
execsql {
|
||||
SELECT * FROM vars WHERE name='A';
|
||||
SELECT name, arrayname, value FROM vars WHERE name='A';
|
||||
}
|
||||
} [list A 1 1 A 2 4 A 3 9]
|
||||
unset -nocomplain result
|
||||
|
||||
+3
-1
@@ -39,7 +39,9 @@ do_test vtabE-1 {
|
||||
CREATE VIRTUAL TABLE t1 USING tclvar;
|
||||
CREATE VIRTUAL TABLE t2 USING tclvar;
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
|
||||
SELECT t1.*, t2.*, abs(t3.b + abs(t2.value + abs(t1.value)))
|
||||
SELECT t1.name, t1.arrayname, t1.value,
|
||||
t2.name, t2.arrayname, t2.value,
|
||||
abs(t3.b + abs(t2.value + abs(t1.value)))
|
||||
FROM t1 LEFT JOIN t2 ON t2.name = t1.arrayname
|
||||
LEFT JOIN t3 ON t3.a=t2.value
|
||||
WHERE t1.name = 'vtabE'
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ register_tclvar_module db
|
||||
set ::xyz 10
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE vars USING tclvar;
|
||||
SELECT * FROM vars WHERE name = 'xyz';
|
||||
SELECT name, arrayname, value FROM vars WHERE name = 'xyz';
|
||||
} {xyz {} 10}
|
||||
|
||||
set x1 aback
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
# 2017-08-10
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements tests of writing to WITHOUT ROWID virtual tables
|
||||
# using the tclvar eponymous virtual table.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix vtabJ
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
register_tclvar_module db
|
||||
|
||||
unset -nocomplain vtabJ
|
||||
do_test 100 {
|
||||
set vtabJ(1) this
|
||||
set vtabJ(two) is
|
||||
set vtabJ(3) {a test}
|
||||
db eval {
|
||||
SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
|
||||
}
|
||||
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(two) is}
|
||||
|
||||
do_execsql_test 110 {
|
||||
INSERT INTO tclvar(fullname, value)
|
||||
VALUES('vtabJ(4)',4),('vtabJ(five)',555);
|
||||
SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
|
||||
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is}
|
||||
do_test 111 {
|
||||
set res {}
|
||||
foreach vname [lsort [array names vtabJ]] {
|
||||
lappend res vtabJ($vname) $vtabJ($vname)
|
||||
}
|
||||
set res
|
||||
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is}
|
||||
|
||||
do_test 120 {
|
||||
db eval {
|
||||
INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',444);
|
||||
}
|
||||
set vtabJ(4)
|
||||
} {444}
|
||||
|
||||
do_test 130 {
|
||||
db eval {
|
||||
INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',NULL);
|
||||
}
|
||||
info exists vtabJ(4)
|
||||
} {0}
|
||||
|
||||
do_test 140 {
|
||||
db eval {
|
||||
UPDATE tclvar SET value=55 WHERE fullname='vtabJ(five)';
|
||||
}
|
||||
set vtabJ(five)
|
||||
} {55}
|
||||
|
||||
do_test 150 {
|
||||
db eval {
|
||||
UPDATE tclvar SET fullname='vtabJ(5)' WHERE fullname='vtabJ(five)';
|
||||
}
|
||||
set vtabJ(5)
|
||||
} {55}
|
||||
do_test 151 {
|
||||
info exists vtabJ(five)
|
||||
} {0}
|
||||
do_test 152 {
|
||||
set res {}
|
||||
foreach vname [lsort [array names vtabJ]] {
|
||||
lappend res vtabJ($vname) $vtabJ($vname)
|
||||
}
|
||||
set res
|
||||
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55 vtabJ(two) is}
|
||||
|
||||
do_execsql_test 160 {
|
||||
SELECT fullname FROM tclvar WHERE arrayname='two'
|
||||
} {vtabJ(two)}
|
||||
do_execsql_test 161 {
|
||||
DELETE FROM tclvar WHERE arrayname='two';
|
||||
SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname;
|
||||
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55}
|
||||
do_test 162 {
|
||||
set res {}
|
||||
foreach vname [lsort [array names vtabJ]] {
|
||||
lappend res vtabJ($vname) $vtabJ($vname)
|
||||
}
|
||||
set res
|
||||
} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55}
|
||||
|
||||
# Try to trick the module into updating the same variable twice for a
|
||||
# single UPDATE statement.
|
||||
#
|
||||
do_execsql_test 171 {
|
||||
INSERT INTO tclvar(fullname, value) VALUES('xx', 'a');
|
||||
SELECT name, value FROM tclvar where name = 'xx';
|
||||
} {xx a}
|
||||
do_execsql_test 172 {
|
||||
UPDATE tclvar SET value = value || 't'
|
||||
WHERE name = 'xx' OR name = 'x'||'x';
|
||||
SELECT name, value FROM tclvar where name = 'xx';
|
||||
} {xx at}
|
||||
do_execsql_test 173 {
|
||||
UPDATE tclvar SET value = value || 't'
|
||||
WHERE name = 'xx' OR name BETWEEN 'xx' AND 'xx';
|
||||
SELECT name, value FROM tclvar where name = 'xx';
|
||||
} {xx att}
|
||||
|
||||
do_execsql_test 181 {
|
||||
DELETE FROM tclvar WHERE name BETWEEN 'xx' AND 'xx' OR name='xx';
|
||||
SELECT name, value FROM tclvar where name = 'xx';
|
||||
} {}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -158,5 +158,17 @@ do_test whereA-4.6 {
|
||||
}
|
||||
} {2 1 1}
|
||||
|
||||
# Ticket https://sqlite.org/src/tktview/cb91bf4290c211 2017-08-01
|
||||
# Assertion fault following PRAGMA reverse_unordered_selects=ON.
|
||||
#
|
||||
do_execsql_test whereA-5.1 {
|
||||
PRAGMA reverse_unordered_selects=on;
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
SELECT a FROM t1 WHERE b=-99 OR b>1;
|
||||
} {1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# 2009 January 8
|
||||
#
|
||||
# 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 the outcome of a writer crashing within a call to the VFS
|
||||
# xWrite function.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix writecrash
|
||||
|
||||
do_not_use_codec
|
||||
|
||||
|
||||
if {$tcl_platform(platform)=="windows"} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB UNIQUE);
|
||||
WITH s(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
)
|
||||
INSERT INTO t1 SELECT NULL, randomblob(900) FROM s;
|
||||
} {}
|
||||
|
||||
set bGo 1
|
||||
for {set tn 1} {$bGo} {incr tn} {
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_test 1.$tn.1 {
|
||||
set res [crash_on_write $tn {
|
||||
UPDATE t1 SET b = randomblob(899) WHERE (a%3)==0
|
||||
}]
|
||||
set bGo 0
|
||||
if {[string match {1 {child killed:*}} $res]} {
|
||||
set res {0 {}}
|
||||
set bGo 1
|
||||
}
|
||||
set res
|
||||
} {0 {}}
|
||||
|
||||
#db close
|
||||
#sqlite3 db test.db
|
||||
|
||||
do_execsql_test 1.$tn.2 { PRAGMA integrity_check } {ok}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 1.$tn.3 { PRAGMA integrity_check } {ok}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
+2
-7
@@ -22,14 +22,7 @@ close $in
|
||||
# ILLEGAL *must* be the last two token codes and they must be in that order.
|
||||
#
|
||||
set extras {
|
||||
TO_TEXT
|
||||
TO_BLOB
|
||||
TO_NUMERIC
|
||||
TO_INT
|
||||
TO_REAL
|
||||
ISNOT
|
||||
END_OF_FILE
|
||||
UNCLOSED_STRING
|
||||
FUNCTION
|
||||
COLUMN
|
||||
AGG_FUNCTION
|
||||
@@ -42,6 +35,8 @@ set extras {
|
||||
IF_NULL_ROW
|
||||
ASTERISK
|
||||
SPAN
|
||||
END_OF_FILE
|
||||
UNCLOSED_STRING
|
||||
SPACE
|
||||
ILLEGAL
|
||||
}
|
||||
|
||||
+24
-1
@@ -2155,7 +2155,8 @@ enum e_state {
|
||||
WAITING_FOR_FALLBACK_ID,
|
||||
WAITING_FOR_WILDCARD_ID,
|
||||
WAITING_FOR_CLASS_ID,
|
||||
WAITING_FOR_CLASS_TOKEN
|
||||
WAITING_FOR_CLASS_TOKEN,
|
||||
WAITING_FOR_TOKEN_NAME
|
||||
};
|
||||
struct pstate {
|
||||
char *filename; /* Name of the input file */
|
||||
@@ -2470,6 +2471,8 @@ to follow the previous rule.");
|
||||
}else if( strcmp(x,"fallback")==0 ){
|
||||
psp->fallback = 0;
|
||||
psp->state = WAITING_FOR_FALLBACK_ID;
|
||||
}else if( strcmp(x,"token")==0 ){
|
||||
psp->state = WAITING_FOR_TOKEN_NAME;
|
||||
}else if( strcmp(x,"wildcard")==0 ){
|
||||
psp->state = WAITING_FOR_WILDCARD_ID;
|
||||
}else if( strcmp(x,"token_class")==0 ){
|
||||
@@ -2624,6 +2627,26 @@ to follow the previous rule.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WAITING_FOR_TOKEN_NAME:
|
||||
/* Tokens do not have to be declared before use. But they can be
|
||||
** in order to control their assigned integer number. The number for
|
||||
** each token is assigned when it is first seen. So by including
|
||||
**
|
||||
** %token ONE TWO THREE
|
||||
**
|
||||
** early in the grammar file, that assigns small consecutive values
|
||||
** to each of the tokens ONE TWO and THREE.
|
||||
*/
|
||||
if( x[0]=='.' ){
|
||||
psp->state = WAITING_FOR_DECL_OR_RULE;
|
||||
}else if( !ISUPPER(x[0]) ){
|
||||
ErrorMsg(psp->filename, psp->tokenlineno,
|
||||
"%%token argument \"%s\" should be a token", x);
|
||||
psp->errorcnt++;
|
||||
}else{
|
||||
(void)Symbol_new(x);
|
||||
}
|
||||
break;
|
||||
case WAITING_FOR_WILDCARD_ID:
|
||||
if( x[0]=='.' ){
|
||||
psp->state = WAITING_FOR_DECL_OR_RULE;
|
||||
|
||||
+7
-9
@@ -202,19 +202,17 @@ for {set i 0} {$i<=$max} {incr i} {
|
||||
set name $def($i)
|
||||
puts -nonewline [format {#define %-16s %3d} $name $i]
|
||||
set com {}
|
||||
if {$jump($name)} {
|
||||
lappend com "jump"
|
||||
}
|
||||
if {[info exists sameas($i)]} {
|
||||
set com "same as $sameas($i)"
|
||||
lappend com "same as $sameas($i)"
|
||||
}
|
||||
if {[info exists synopsis($name)]} {
|
||||
set x $synopsis($name)
|
||||
if {$com==""} {
|
||||
set com "synopsis: $x"
|
||||
} else {
|
||||
append com ", synopsis: $x"
|
||||
}
|
||||
lappend com "synopsis: $synopsis($name)"
|
||||
}
|
||||
if {$com!=""} {
|
||||
puts -nonewline [format " /* %-42s */" $com]
|
||||
if {[llength $com]} {
|
||||
puts -nonewline [format " /* %-42s */" [join $com {, }]]
|
||||
}
|
||||
puts ""
|
||||
}
|
||||
|
||||
@@ -224,6 +224,8 @@ proc copy_file {filename} {
|
||||
if {![info exists varonly_hdr($tail)]
|
||||
&& [regexp $declpattern $line all rettype funcname rest]} {
|
||||
regsub {^SQLITE_API } $line {} line
|
||||
regsub {^SQLITE_API } $rettype {} rettype
|
||||
|
||||
# Add the SQLITE_PRIVATE or SQLITE_API keyword before functions.
|
||||
# so that linkage can be modified at compile-time.
|
||||
if {[regexp {^sqlite3[a-z]*_} $funcname]} {
|
||||
|
||||
Reference in New Issue
Block a user