Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 464a2dd1f5 | |||
| 351064b452 | |||
| a895a4d22c | |||
| ffc8f3e201 | |||
| 2c3abeb8c3 | |||
| 5de7d966bd | |||
| dea7d70d1b | |||
| 1d32488037 | |||
| 72673a24e2 | |||
| d8922052b2 | |||
| 063970a15f | |||
| a25165fa68 | |||
| dd90d7ee81 | |||
| 86e166a778 | |||
| bb9a378df3 | |||
| 5b875311a3 | |||
| 2d2e7bfeff | |||
| 2f89546001 | |||
| 0fe8c1b967 | |||
| f26a1549ac | |||
| 1fd2d7d471 | |||
| 9b5444af6c | |||
| ac5649a985 | |||
| cb7fe0ab9c | |||
| d89b834f54 | |||
| 3f2d22e1a3 | |||
| 5ab63775dd | |||
| 0a79238b39 | |||
| 0da10d3289 | |||
| ab4624d005 | |||
| 7a1bca7e74 | |||
| a3898250d4 | |||
| 643091f071 | |||
| 2d8233157d | |||
| 341eca7f68 | |||
| 5e3b49bc42 | |||
| fdab02635c | |||
| 1b40e63f9b | |||
| 654858d7f4 | |||
| 113762a284 | |||
| 42a7b4bac9 | |||
| 664f85dd59 | |||
| fead4ed8f3 | |||
| 36ff0bf684 | |||
| 64b600ff13 | |||
| bea3b976a9 | |||
| 58f95c43ad | |||
| bb8f925294 | |||
| 4429c20b86 | |||
| c5352b996a | |||
| 756d3b3e9d | |||
| 2b8669a9da | |||
| d816e00d13 | |||
| 77b1deec40 | |||
| 13835c41e8 | |||
| 302f583aa4 | |||
| f9df2fbdcd | |||
| 70273d0bab | |||
| 0a9d9d566e | |||
| dd715f7c57 | |||
| 8ac1a67eff | |||
| fad01993b7 | |||
| 6f9c5669e2 | |||
| d7b06909ad | |||
| 8023104252 | |||
| 5a2c8c885c | |||
| 51a205410c | |||
| 85fabf1444 | |||
| bfa395d085 | |||
| 2d8e3caa2e | |||
| 949d43a5fd | |||
| 43f4066e10 | |||
| 47b7fc7843 | |||
| de58f4fe7f | |||
| 923c4b35be | |||
| 1728bcb07f | |||
| 4ace5362c2 | |||
| b391b94431 | |||
| 97d3898335 | |||
| 9a06d30bb5 | |||
| 86a11b8a6a | |||
| 6137138ded | |||
| 8790b6e860 | |||
| b1a6eada1d | |||
| 15f23c2cf0 | |||
| 179bac3a14 | |||
| 42f30bce11 | |||
| c6652b1efe | |||
| 518140ed04 | |||
| e1db0db899 | |||
| fefa094921 | |||
| 6d3f91d040 | |||
| 6b5631e02f | |||
| 937994aa65 | |||
| e0de876e27 | |||
| 8d1edb92c4 | |||
| 547fb61807 | |||
| fdece7bad1 | |||
| 73767829b9 | |||
| 4d9f188f00 | |||
| cbd55b0362 | |||
| 5279d3433c | |||
| def6889d21 | |||
| d84bf205c3 | |||
| 0cb6948f6b | |||
| d72219da43 | |||
| d1a1c23423 | |||
| e2f771b047 | |||
| 7b4d780b54 | |||
| 7f5a7ecd21 | |||
| 037b5324bd | |||
| 43764a8efb | |||
| 6f9702ed4d | |||
| d1dcb23465 | |||
| 89e71646df | |||
| 04489b6dce | |||
| 0fb5daed34 | |||
| d836d42383 | |||
| bec021b9fc | |||
| 9c0153457a | |||
| 768f29002e | |||
| 00fe08af82 | |||
| 2a0df92542 | |||
| ba0f999418 | |||
| af8f513f9d | |||
| 7abfe9d0c6 | |||
| 35404253f6 | |||
| a26b9a7727 | |||
| bba8206be9 | |||
| 739383d25d | |||
| 62ecc28caf | |||
| 22e8d833f0 | |||
| aa55563d7e | |||
| 4f41b7dec5 | |||
| c3da667b25 | |||
| dd8c460081 | |||
| 635e57fc48 | |||
| c3ef4fa88a | |||
| 916cd23b2c | |||
| eefaf448db | |||
| 2d8ad51c5b | |||
| d61a18a9f1 | |||
| df9c093e2c | |||
| a95d8ca1fa | |||
| 98d94211ce | |||
| a4b8fa2360 | |||
| 8a42d1bba1 | |||
| 5a5d120bcc | |||
| 9ca95730e3 |
@@ -359,6 +359,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_backup.c \
|
||||
$(TOP)/src/test_blob.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_demovfs.c \
|
||||
@@ -394,6 +395,7 @@ TESTSRC = \
|
||||
TESTSRC += \
|
||||
$(TOP)/ext/misc/amatch.c \
|
||||
$(TOP)/ext/misc/closure.c \
|
||||
$(TOP)/ext/misc/eval.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/fuzzer.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
|
||||
@@ -828,6 +828,7 @@ TESTSRC = \
|
||||
$(TOP)\src\test_autoext.c \
|
||||
$(TOP)\src\test_async.c \
|
||||
$(TOP)\src\test_backup.c \
|
||||
$(TOP)\src\test_blob.c \
|
||||
$(TOP)\src\test_btree.c \
|
||||
$(TOP)\src\test_config.c \
|
||||
$(TOP)\src\test_demovfs.c \
|
||||
@@ -863,6 +864,7 @@ TESTSRC = \
|
||||
TESTEXT = \
|
||||
$(TOP)\ext\misc\amatch.c \
|
||||
$(TOP)\ext\misc\closure.c \
|
||||
$(TOP)\ext\misc\eval.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\fuzzer.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.62 for sqlite 3.8.7.
|
||||
# Generated by GNU Autoconf 2.62 for sqlite 3.8.8.
|
||||
#
|
||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
@@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.8.7'
|
||||
PACKAGE_STRING='sqlite 3.8.7'
|
||||
PACKAGE_VERSION='3.8.8'
|
||||
PACKAGE_STRING='sqlite 3.8.8'
|
||||
PACKAGE_BUGREPORT=''
|
||||
|
||||
# Factoring default headers for most tests.
|
||||
@@ -1483,7 +1483,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.8.7 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.8.8 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1548,7 +1548,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.8.7:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.8.8:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1664,7 +1664,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.8.7
|
||||
sqlite configure 3.8.8
|
||||
generated by GNU Autoconf 2.62
|
||||
|
||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
@@ -1678,7 +1678,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.8.7, which was
|
||||
It was created by sqlite $as_me 3.8.8, which was
|
||||
generated by GNU Autoconf 2.62. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -14021,7 +14021,7 @@ exec 6>&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.8.7, which was
|
||||
This file was extended by sqlite $as_me 3.8.8, which was
|
||||
generated by GNU Autoconf 2.62. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -14074,7 +14074,7 @@ Report bugs to <bug-autoconf@gnu.org>."
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.8.7
|
||||
sqlite config.status 3.8.8
|
||||
configured by $0, generated by GNU Autoconf 2.62,
|
||||
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ static int isVowel(const char *z){
|
||||
** by a consonant.
|
||||
**
|
||||
** In this routine z[] is in reverse order. So we are really looking
|
||||
** for an instance of of a consonant followed by a vowel.
|
||||
** for an instance of a consonant followed by a vowel.
|
||||
*/
|
||||
static int m_gt_0(const char *z){
|
||||
while( isVowel(z) ){ z++; }
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
** 2014-11-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 SQLite extension implements SQL function eval() which runs
|
||||
** SQL statements recursively.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Structure used to accumulate the output
|
||||
*/
|
||||
struct EvalResult {
|
||||
char *z; /* Accumulated output */
|
||||
const char *zSep; /* Separator */
|
||||
int szSep; /* Size of the separator string */
|
||||
sqlite3_int64 nAlloc; /* Number of bytes allocated for z[] */
|
||||
sqlite3_int64 nUsed; /* Number of bytes of z[] actually used */
|
||||
};
|
||||
|
||||
/*
|
||||
** Callback from sqlite_exec() for the eval() function.
|
||||
*/
|
||||
static int callback(void *pCtx, int argc, char **argv, char **colnames){
|
||||
struct EvalResult *p = (struct EvalResult*)pCtx;
|
||||
int i;
|
||||
for(i=0; i<argc; i++){
|
||||
const char *z = argv[i] ? argv[i] : "";
|
||||
size_t sz = strlen(z);
|
||||
if( (sqlite3_int64)sz+p->nUsed+p->szSep+1 > p->nAlloc ){
|
||||
char *zNew;
|
||||
p->nAlloc = p->nAlloc*2 + sz + p->szSep + 1;
|
||||
/* Using sqlite3_realloc64() would be better, but it is a recent
|
||||
** addition and will cause a segfault if loaded by an older version
|
||||
** of SQLite. */
|
||||
zNew = p->nAlloc<=0x7fffffff ? sqlite3_realloc(p->z, (int)p->nAlloc) : 0;
|
||||
if( zNew==0 ){
|
||||
sqlite3_free(p->z);
|
||||
memset(p, 0, sizeof(*p));
|
||||
return 1;
|
||||
}
|
||||
p->z = zNew;
|
||||
}
|
||||
if( p->nUsed>0 ){
|
||||
memcpy(&p->z[p->nUsed], p->zSep, p->szSep);
|
||||
p->nUsed += p->szSep;
|
||||
}
|
||||
memcpy(&p->z[p->nUsed], z, sz);
|
||||
p->nUsed += sz;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the eval(X) and eval(X,Y) SQL functions.
|
||||
**
|
||||
** Evaluate the SQL text in X. Return the results, using string
|
||||
** Y as the separator. If Y is omitted, use a single space character.
|
||||
*/
|
||||
static void sqlEvalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
char *zErr = 0;
|
||||
int rc;
|
||||
struct EvalResult x;
|
||||
|
||||
memset(&x, 0, sizeof(x));
|
||||
x.zSep = " ";
|
||||
zSql = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zSql==0 ) return;
|
||||
if( argc>1 ){
|
||||
x.zSep = (const char*)sqlite3_value_text(argv[1]);
|
||||
if( x.zSep==0 ) return;
|
||||
}
|
||||
x.szSep = (int)strlen(x.zSep);
|
||||
db = sqlite3_context_db_handle(context);
|
||||
rc = sqlite3_exec(db, zSql, callback, &x, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}else if( x.zSep==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
sqlite3_free(x.z);
|
||||
}else{
|
||||
sqlite3_result_text(context, x.z, (int)x.nUsed, sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_eval_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -246,6 +246,7 @@ TESTSRC = \
|
||||
$(TOP)/src/test_autoext.c \
|
||||
$(TOP)/src/test_async.c \
|
||||
$(TOP)/src/test_backup.c \
|
||||
$(TOP)/src/test_blob.c \
|
||||
$(TOP)/src/test_btree.c \
|
||||
$(TOP)/src/test_config.c \
|
||||
$(TOP)/src/test_demovfs.c \
|
||||
@@ -280,6 +281,7 @@ TESTSRC = \
|
||||
TESTSRC += \
|
||||
$(TOP)/ext/misc/amatch.c \
|
||||
$(TOP)/ext/misc/closure.c \
|
||||
$(TOP)/ext/misc/eval.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/fuzzer.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
C Optimizations\saimed\sat\sreducing\sthe\snumber\sof\smemcpy()\soperations\srequired\sby\sbalance_nonroot().
|
||||
D 2014-10-27T14:26:54.508
|
||||
C Add\scode\sto\scheck\sthe\svalidity\sof\sCollSeq\sobjects\sduring\sruntime.\s\sThis\scode\nwas\snot\sable\sto\sdetect\sanomalies\ssuch\sas\scame\sup\sas\sa\sresult\sof\nticket\s[e4a18565a36884b00edf66541f38c693827968ab]\sso\sit\sis\sput\sinto\sa\sbranch\nfor\shistorical\sreference,\swith\sthe\sintent\sof\sleaving\sit\sout\sof\strunk.
|
||||
D 2014-12-05T14:36:15.903
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
||||
F Makefile.in 6c4f961fa91d0b4fa121946a19f9e5eac2f2f809
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc e31dee24038965fb6269d6d61073fd6b7e331dec
|
||||
F Makefile.msc 10720782f88648bf2b5dcedf4c1524b067d43e47
|
||||
F Makefile.vxworks 034289efa9d591b04b1a73598623119c306cbba0
|
||||
F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8
|
||||
F VERSION 53a0b870e7f16d3b06623c31d233a304c163a6af
|
||||
F VERSION d846487aff892625eb8e75960234e7285f0462fe
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
@@ -38,7 +38,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure ad59a5f48b3c59a92b5506040a22fbe3f733a9d8 x
|
||||
F configure 4343c810cc772571210af75d1a8f7c2eb711d75a x
|
||||
F configure.ac 4cf9f60785143fa141b10962ccc885d973792e9a
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1
|
||||
@@ -86,7 +86,7 @@ F ext/fts3/fts3_expr.c 40123785eaa3ebd4c45c9b23407cc44ac0c49905
|
||||
F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
|
||||
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
|
||||
F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5
|
||||
F ext/fts3/fts3_porter.c 7f8b4bf5af7c0f20f73b8e87e14fa9298f52e290
|
||||
F ext/fts3/fts3_porter.c 3565faf04b626cddf85f03825e86056a4562c009
|
||||
F ext/fts3/fts3_snippet.c 51beb5c1498176fd9caccaf1c75b55cb803a985a
|
||||
F ext/fts3/fts3_term.c a521f75132f9a495bdca1bdd45949b3191c52763
|
||||
F ext/fts3/fts3_test.c 8a3a78c4458b2d7c631fcf4b152a5cd656fa7038
|
||||
@@ -109,6 +109,7 @@ F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
F ext/misc/amatch.c 678056a4bfcd83c4e82dea81d37543cd1d6dbee1
|
||||
F ext/misc/closure.c 636024302cde41b2bf0c542f81c40c624cfb7012
|
||||
F ext/misc/compress.c 76e45655f4046e756064ab10c62e18f2eb846b9f
|
||||
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
|
||||
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
|
||||
F ext/misc/fuzzer.c 136533c53cfce0957f0b48fa11dba27e21c5c01d
|
||||
F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
|
||||
@@ -151,7 +152,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk bbc8b6000ed143a1a8d31d3b4995c359a3188fa1
|
||||
F main.mk 084976077a4aa3bd985154b5423e7aed88e4a2e9
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@@ -163,29 +164,29 @@ F mptest/mptest.c 499a74af4be293b7c1c7c3d40f332b67227dd739
|
||||
F mptest/multiwrite01.test 499ad0310da8dff8e8f98d2e272fc2a8aa741b2e
|
||||
F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c ba266a779bc7ce10e52e59e7d3dc79fa342e8fdb
|
||||
F src/analyze.c afbcca663c3f3625340b8e30d440cd7a97ded6bc
|
||||
F src/analyze.c f7d774356ba5a14e7ad4fb637681af16875ad88f
|
||||
F src/attach.c f4e94df2d1826feda65eb0939f7f6f5f923a0ad9
|
||||
F src/auth.c d8abcde53426275dab6243b441256fcd8ccbebb2
|
||||
F src/backup.c a31809c65623cc41849b94d368917f8bb66e6a7e
|
||||
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c 7ddee9c7d505e07e959a575b18498f17c71e53ea
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5
|
||||
F src/btree.c 9790fb4df51d36861bcbb8cd0a9b41586cbae699
|
||||
F src/btree.h a79aa6a71e7f1055f01052b7f821bd1c2dce95c8
|
||||
F src/btreeInt.h 026d0129724e8f265fdc60d44ec240cf5a4e6179
|
||||
F src/build.c 9dc2bd94347b878c89627000c92b0c8d97ec2919
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
F src/complete.c 535183afb3c75628b78ce82612931ac7cdf26f14
|
||||
F src/ctime.c bb434068b5308a857b181c2d204a320ff0d6c638
|
||||
F src/date.c 57a7f9ba9f6b4d5268f5e411739066a611f99036
|
||||
F src/delete.c fae81cc2eb14b75267d4f47d3cfc9ae02aae726f
|
||||
F src/expr.c 0391a657df4959eaf2a2fd7d77de5ebe750686ee
|
||||
F src/btree.c 44b58cd798a32579816ce274e415de262df9843d
|
||||
F src/btree.h e31a3a3ebdedb1caf9bda3ad5dbab3db9b780f6e
|
||||
F src/btreeInt.h 3363e18fd76f69a27a870b25221b2345b3fd4d21
|
||||
F src/build.c 67bb05b1077e0cdaccb2e36bfcbe7a5df9ed31e8
|
||||
F src/callback.c 759c9aff944f72fd7fb810594d95af1092a8433a
|
||||
F src/complete.c c4ba6e0626bb94bc77a0861735f3382fcf7cc818
|
||||
F src/ctime.c df19848891c8a553c80e6f5a035e768280952d1a
|
||||
F src/date.c 93594514aae68de117ca4a2a0d6cc63eddf26744
|
||||
F src/delete.c 0750b1eb4d96cd3fb2c798599a3a7c85e92f1417
|
||||
F src/expr.c 00da3072f362b06f39ce4052baa1d4ce2bb36d1c
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c da985ae673efef2c712caef825a5d2edb087ead7
|
||||
F src/func.c ba47c1671ab3cfdafa6e9d6ee490939ea578adee
|
||||
F src/global.c 01c1f36ecfcf10770db648422a8852c222308bb9
|
||||
F src/func.c 6d3c4ebd72aa7923ce9b110a7dc15f9b8c548430
|
||||
F src/global.c 6ded36dda9466fc1c9a3c5492ded81d79bf3977d
|
||||
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
|
||||
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
@@ -194,50 +195,50 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
||||
F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
|
||||
F src/loadext.c de741e66e5ddc1598d904d7289239696e40ed994
|
||||
F src/main.c 8207ece1b4005b1efab55d1505d8ffb45bfced05
|
||||
F src/malloc.c 3c3ac67969612493d435e14b6832793209afd2ec
|
||||
F src/main.c fa9f0e511a64caf66b8e7bb835c55172e7d61527
|
||||
F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c faf615aafd8be74a71494dfa027c113ea5c6615f
|
||||
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
|
||||
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
|
||||
F src/mem5.c 61eeb90134f9a5be6c2e68d8daae7628b25953fb
|
||||
F src/memjournal.c 3eb2c0b51adbd869cb6a44780323f05fa904dc85
|
||||
F src/mutex.c 84a073c9a23a8d7bdd2ea832522d1730df18812c
|
||||
F src/mutex.c 19bf9acba69ca2f367c3761080f8a9f0cf4670a8
|
||||
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
|
||||
F src/mutex_noop.c f3f09fd7a2eb4287cfc799753ffc30380e7b71a1
|
||||
F src/mutex_unix.c 1b10d5413dfc794364a8adf3eb3a192926b43fa3
|
||||
F src/mutex_unix.c 551e2f25f0fa0ee8fd7a43f50fc3d8be00e95dde
|
||||
F src/mutex_w32.c 06bfff9a3a83b53389a51a967643db3967032e1e
|
||||
F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7
|
||||
F src/os.c 1b147e4cf7cc39e618115c14a086aed44bc91ace
|
||||
F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
|
||||
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
|
||||
F src/os_unix.c fb587121840f690101336879adfa6d0b2cd0e8c7
|
||||
F src/os_win.c a019caaae2bcbbc0cc4c39af6e7d7e43d8426053
|
||||
F src/os_win.c a9e500dd963fb1f67d7860e58b5772abe6123862
|
||||
F src/os_win.h 09e751b20bbc107ffbd46e13555dc73576d88e21
|
||||
F src/pager.c 8d97b3633f098fef817656dcbf167ca904511d78
|
||||
F src/pager.c 7a5c5bc0e29b9b16834f5558a9d5d22bbae59a08
|
||||
F src/pager.h d1eee3c3f741be247ce6d82752a178515fc8578b
|
||||
F src/parse.y 5dfead8aed90cb0c7c1115898ee2266804daff45
|
||||
F src/pcache.c 4121a0571c18581ee9f82f086d5e2030051ebd6a
|
||||
F src/pcache.h 9b559127b83f84ff76d735c8262f04853be0c59a
|
||||
F src/pcache1.c e412cb585f777c840ddce0500eddc5c6043c2bb5
|
||||
F src/pragma.c 3f3e959390a10c0131676f0e307acce372777e0f
|
||||
F src/prepare.c 6ef0cf2f9274982988ed6b7cab1be23147e94196
|
||||
F src/printf.c 090fac0f779c93c8a95089a125339686648835e4
|
||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||
F src/resolve.c 4965007d6497b6a4d7a6d98751cc39712885f952
|
||||
F src/pcache.c ace1b67632deeaa84859b4c16c27711dfb7db3d4
|
||||
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
|
||||
F src/pcache1.c facbdd3ecc09c8f750089d941305694301328e98
|
||||
F src/pragma.c d54cdd40b63d608f2d95b7482c710690e3593a73
|
||||
F src/prepare.c b7b7bf020bd4c962f7c8aed5a3c542c7dfe9f9c7
|
||||
F src/printf.c 9e75a6a0b55bf61cfff7d7e19d89834a1b938236
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c f6c46d3434439ab2084618d603e6d6dbeb0d6ada
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c 428165951748151e87a15295b7357221433e311b
|
||||
F src/shell.c 282f8f5278e0c78eb442217531172ec9e1538796
|
||||
F src/sqlite.h.in 4a5e5158c189d2bcd45c7c4607c2c0eb6d25c153
|
||||
F src/select.c f377fb8a5c73c10678ea74f3400f7913943e3d75
|
||||
F src/shell.c 45d9c9bd7cde07845af957f2d849933b990773cf
|
||||
F src/sqlite.h.in 6ec654324cb490ea3d8a7be28b8c7d37fe4ad282
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
|
||||
F src/sqliteInt.h 90519c3b3e8ee90adfce013234c4bd07275d77b5
|
||||
F src/sqliteInt.h ef42a0515a846ab2a670bda705be494d7acc2535
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 961d5926e5a8fda611d385ec22c226b8635cd1cb
|
||||
F src/table.c 2e99ef7ef16187e17033d9398dc962ce22dab5cb
|
||||
F src/tclsqlite.c c67d310c833046cccc192125d64ad422ab882684
|
||||
F src/test1.c 63d4b1707c4052cf9c05c1cbb4a62666d70a0b48
|
||||
F src/status.c 81712116e826b0089bb221b018929536b2b5406f
|
||||
F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc
|
||||
F src/tclsqlite.c 0a874655dd39a9875e39c5d3c464db662171d228
|
||||
F src/test1.c c24d7f67252348d756773a6dbe23a61b4552f709
|
||||
F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
|
||||
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
|
||||
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
|
||||
@@ -249,8 +250,9 @@ F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
|
||||
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
|
||||
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
|
||||
F src/test_blob.c 1f2e3e25255b731c4fcf15ee7990d06347cb6c09
|
||||
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
|
||||
F src/test_config.c a4cdebe093474c02eecc5e4008b1a22198edf975
|
||||
F src/test_config.c 035c17a173937d019b8dfc1d524f9d3fc8123504
|
||||
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
|
||||
@@ -285,25 +287,25 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 6de09362b657f19ba83e5fa521ee715787ce9fee
|
||||
F src/tokenize.c cc9016e5007fc5e76789079616d2f26741bcc689
|
||||
F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
|
||||
F src/update.c 729f6f18fc27740591d085e1172cebe311144bf0
|
||||
F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c 4006c01772bd8d8ac4306d523bbcee41d3e392d8
|
||||
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
|
||||
F src/vdbe.c 5ee15a66ce07e0482b92aa29e4dd0c5827a22d79
|
||||
F src/vdbe.h 09f5b4e3719fa454f252322b1cdab5cf1f361327
|
||||
F src/vdbeInt.h e2a060a55ee18a6ab973353a5e2ec7ee569bf787
|
||||
F src/vdbeapi.c 37a6c6ae284a97bcace365f2f0a225680c0499d9
|
||||
F src/vdbeaux.c edbb7a9c8b2a8f7a68ac75c2475edd4040266b76
|
||||
F src/vdbeblob.c 848238dc73e93e48432991bb5651bf87d865eca4
|
||||
F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
|
||||
F src/vacuum.c 9b30ec729337dd012ed88d4c292922c8ef9cf00c
|
||||
F src/vdbe.c 1a9e671c9cfc259e4d2affc71f7df4a4c00a842c
|
||||
F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
|
||||
F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78
|
||||
F src/vdbeapi.c 07acb615d1e4170e71fc1b0d087f3c53a1ad8e83
|
||||
F src/vdbeaux.c 7e0a097ac7cf716300128f18147ef9af7c9d1130
|
||||
F src/vdbeblob.c 4af4bfb71f6df7778397b4a0ebc1879793276778
|
||||
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
|
||||
F src/vdbesort.c 975aeffa99acb0991b2f288d30294756bff41438
|
||||
F src/vdbesort.c 42c166f7ca78cb643c7f4e4bdfa83c59d363d1a6
|
||||
F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010
|
||||
F src/vtab.c cb0c194303fea276b48d7d4b6d970b5a96bde8de
|
||||
F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983
|
||||
F src/vtab.c c08ec66f45919eaa726bf88aa53eb08379d607f9
|
||||
F src/wal.c 847692349eb6e1fb8543dbc97e69ddbfa4cc7ea7
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
|
||||
F src/where.c 4ce8c4826b7f86d080f0ed4e7a9045bb5014be77
|
||||
F src/whereInt.h 19279cd0664ce1d90b9ad3ef0108cb494acfe455
|
||||
F src/where.c d67fe69dd1bb32ad3d488a8c5bc803a29814f357
|
||||
F src/whereInt.h d3633e9b592103241b74b0ec76185f3e5b8b62e0
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7
|
||||
@@ -321,7 +323,7 @@ F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
|
||||
F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
|
||||
F test/analyze6.test f1c552ce39cca4ec922a7e4e0e5d0203d6b3281f
|
||||
F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
|
||||
F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88
|
||||
F test/analyze8.test c05a461d0a6b05991106467d0c47480f2e709c82
|
||||
F test/analyze9.test 72795c8113604b5dcd47a1498a61d6d7fb5d041a
|
||||
F test/analyzeA.test 3335697f6700c7052295cfd0067fc5b2aacddf9a
|
||||
F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d
|
||||
@@ -344,16 +346,17 @@ F test/auth2.test 264c6af53cad9aba5218c68bbe18036e39007bfa
|
||||
F test/auth3.test 5cfa94ed90c6617c42b7ba4b133fd79678b251c7
|
||||
F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7
|
||||
F test/autoindex1.test 6ff78b94f43a59616c06c11c55b12935173506d7
|
||||
F test/autoindex2.test 60d2fc6f38364308ce73a9beb01b47ded38697de
|
||||
F test/autoindex3.test 8254f689c3241081fad52b7bea18ba53e07e14a2
|
||||
F test/autoindex2.test af7e595c6864cc6ef5fc38d5db579a3e34940cb8
|
||||
F test/autoindex3.test a3be0d1a53a7d2edff208a5e442312957047e972
|
||||
F test/autoindex4.test fc807f9efd158bec60f5dfdf34ebe46fb274612d
|
||||
F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
F test/backcompat.test 19a1f337c68419b020a7481dd272a472c4ad8ef4
|
||||
F test/backup.test c9cdd23a495864b9edf75a9fa66f5cb7e10fcf62
|
||||
F test/backup.test b79299a536a4c6d919094786595b95be56d02014
|
||||
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
|
||||
F test/backup4.test 2a2e4a64388090b152de753fd9e123f28f6a3bd4
|
||||
F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4
|
||||
F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
|
||||
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
|
||||
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
@@ -363,6 +366,7 @@ F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
|
||||
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
|
||||
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
|
||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||
F test/bigsort.test 835478d0ce83bd1e5b05c90571dedd9871a09196
|
||||
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
|
||||
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
|
||||
F test/bitvec.test 75894a880520164d73b1305c1c3f96882615e142
|
||||
@@ -375,14 +379,15 @@ F test/boundary3.tcl 23361e108a125dca9c4080c2feb884fe54d69243
|
||||
F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45
|
||||
F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983
|
||||
F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
|
||||
F test/btree01.test e08b3613540145b353f20c81cb18ead54ff12e0f
|
||||
F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3
|
||||
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
|
||||
F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de
|
||||
F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738
|
||||
F test/capi3.test 71bcf2fbd36a9732f617766dfd752552c8e491b5
|
||||
F test/capi3.test f0718f4f90d0efdc980119bfbdf1d7f1541ee5ef
|
||||
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
|
||||
F test/capi3c.test a21869e4d50d5dbb7e566e328fc0bc7c2efa6a32
|
||||
F test/capi3d.test c84af0c49267f9c3fbf4c1c46aa647646023811e
|
||||
F test/capi3c.test fdc0d67a2cb8e8fc400d5b7735e330161ea057a2
|
||||
F test/capi3d.test a82b6321c50a1cfc848e386fa2c851893606f68c
|
||||
F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
|
||||
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
|
||||
F test/check.test 5831ddb6f2c687782eaf2e1a07b6e17f24c4f763
|
||||
@@ -421,7 +426,7 @@ F test/corruptD.test b3c205fac7952b1de645ce44bb02335cd9e3e040
|
||||
F test/corruptE.test 193b4ca4e927e77c1d5f4f56203ddc998432a7ee
|
||||
F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
|
||||
F test/corruptG.test 1ab3bf97ee7bdba70e0ff3ba2320657df55d1804
|
||||
F test/corruptH.test 88ed71a086e13591c917aac6de32750e7c7281cb
|
||||
F test/corruptH.test 5dd4fa98c6c1ed33b178f9e8a48c4fdd3cfc9067
|
||||
F test/corruptI.test 221ad8b7f0a9ac6b80fc577e73b5ad8cdea31243
|
||||
F test/cost.test 19d314526616ce4473eb4e4e450fcb94499ce318
|
||||
F test/count.test 42a251178e32f617eda33f76236a7f79825a50b5
|
||||
@@ -449,8 +454,13 @@ F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77
|
||||
F test/distinct.test 175d49ee783febaf368192dfe7f5afbc68910230
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_blobbytes.test 9bea1d3e2b20f3010b04abba58f6ba172301f49f
|
||||
F test/e_blobclose.test df756753f571bc30e42e3a6cba2807576e49e716
|
||||
F test/e_blobopen.test 234f960d90235a9b51ec3ca1e062e8541dd558d8
|
||||
F test/e_blobwrite.test 615b405f29feb2cfb5a1f03dab7933258294fa26
|
||||
F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
|
||||
F test/e_createtable.test c7e67b49e6cf92473c8fb30ab26143e9e2128cf7
|
||||
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
|
||||
F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
|
||||
@@ -463,6 +473,7 @@ F test/e_reindex.test 396b7b4f0a66863b4e95116a67d93b227193e589
|
||||
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
|
||||
F test/e_select.test 52692ff3849541e828ad4661fe3773a9b8711763
|
||||
F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f
|
||||
F test/e_totalchanges.test b12ee5809d3e63aeb83238dd501a7bca7fd72c10
|
||||
F test/e_update.test 312cb8f5ccfe41515a6bb092f8ea562a9bd54d52
|
||||
F test/e_uri.test 5ae33760fb2039c61aa2d90886f1664664173585
|
||||
F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9
|
||||
@@ -489,7 +500,7 @@ F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
|
||||
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
|
||||
F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d
|
||||
F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
|
||||
F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc
|
||||
F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13
|
||||
F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
|
||||
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
|
||||
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
|
||||
@@ -618,7 +629,7 @@ F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
|
||||
F test/in5.test 99f9a40af01711b06d2d614ecfe96129f334fba3
|
||||
F test/in5.test 1de657472fa9ac2924be25c2c959ac5ca1aae554
|
||||
F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328
|
||||
F test/incrblob2.test bf4d549aa4a466d7fbe3e3a3693d3861263d5600
|
||||
F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
|
||||
@@ -650,7 +661,7 @@ F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc
|
||||
F test/intpkey.test 7506090fc08e028712a8bf47e5f54111947e3844
|
||||
F test/io.test 3a7abcef18727cc0f2399e04b0e8903eccae50f8
|
||||
F test/ioerr.test 2a24bd6ed5a8b062e64bfe1f6cf94fb25e92210d
|
||||
F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d
|
||||
F test/ioerr2.test 2593563599e2cc6b6b4fcf5878b177bdd5d8df26
|
||||
F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
|
||||
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
|
||||
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
|
||||
@@ -659,7 +670,7 @@ F test/join.test 52d4d49f86d0cf46926672878c4eaf0da399104a
|
||||
F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
|
||||
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
|
||||
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
||||
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
|
||||
F test/join5.test 5df23eba184f159ed9705a954957e765a10c141d
|
||||
F test/join6.test cfe6503791ceb0cbb509966740286ec423cbf10b
|
||||
F test/journal1.test 69abc726c51b4a0409189f9a85191205297c0577
|
||||
F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1
|
||||
@@ -725,8 +736,9 @@ F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
|
||||
F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
|
||||
F test/misc8.test fc2754d38892f7dac30c22db3616c2764f117d66
|
||||
F test/misuse.test 3c34719944ba045cc6c188a4852ba04680728912
|
||||
F test/mmap1.test 93d167b328255cbe6679fe1e1a23be1b1197d07b
|
||||
F test/mmap1.test 1bfd611b9841eafb44f7d83c0788e146d84a33c9
|
||||
F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022
|
||||
F test/mmap3.test c92273e16eb8d23c1d55c9815b446bb72ef0512e
|
||||
F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
|
||||
@@ -766,11 +778,11 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
|
||||
F test/permutations.test cef25f5e8499a15846eccd06785f17f4180407ab
|
||||
F test/pragma.test 19d0241a007bcdd77fc2606ec60fc60357e7fc8b
|
||||
F test/permutations.test 4e12d43f4639ea8a0e366d9c64e0009afe2eb544
|
||||
F test/pragma.test 49ac8a73c0daa574824538fed28727d1259fe735
|
||||
F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
F test/printf2.test bed79b4c3e5da08ba88ad637c0bf62586843cfb1
|
||||
F test/printf2.test b4acd4bf8734243257f01ddefa17c4fb090acc8a
|
||||
F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
|
||||
@@ -787,18 +799,21 @@ F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
|
||||
F test/releasetest.tcl a4279c890698584feb2ffc86735857a4e4474180
|
||||
F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
|
||||
F test/rollback.test e9504a009a202c3ed711da2e6879ff60c5a4669c
|
||||
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
|
||||
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
|
||||
F test/rollbackfault.test 6a004f71087cc399296cffbb5429ea6da655ae65
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test 742b5741584a8a44fd83e856cc2896688401d645
|
||||
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
||||
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
|
||||
F test/savepoint.test 51d3900dc071a7c2ad4248578a5925631b476313
|
||||
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
|
||||
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
|
||||
F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
|
||||
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
|
||||
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
|
||||
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
|
||||
F test/savepoint7.test fbf319a7b2dda089ec5be30a424a0e95f121d423
|
||||
F test/savepoint7.test db3db281486c925095f305aad09fe806e5188ff3
|
||||
F test/scanstatus.test 5253c219e331318a437f436268e0e82345700285
|
||||
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
|
||||
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
|
||||
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
|
||||
@@ -831,10 +846,11 @@ F test/shared7.test a81e99f83e6c51b02ac99c96fb3a2a7b5978c956
|
||||
F test/shared8.test 00a07bf5e1337ecf72e94542bdefdc330d7a2538
|
||||
F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21
|
||||
F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
|
||||
F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa
|
||||
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
|
||||
F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test d60946b5fde4d85fe06db7331dfe89011f564350
|
||||
F test/shell2.test c57da3a381c099b02c813ba156298d5c2f5c93a3
|
||||
F test/shell1.test ab6025d941f9c84c5b83412c6b4d8b57f78dfa3a
|
||||
F test/shell2.test 12b8bf901b0e3a8ac58cf5c0c63a0a388d4d1862
|
||||
F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29
|
||||
F test/shell4.test 8a9c08976291e6c6c808b4d718f4a8b299f339f5
|
||||
F test/shell5.test 15a419cc1df21c892ed64f5596ae7a501f2816f2
|
||||
@@ -842,15 +858,15 @@ F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
|
||||
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
|
||||
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
|
||||
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
|
||||
F test/skipscan1.test 7e15e1cc524524e7b2c4595ec85c75501d22f4ff
|
||||
F test/skipscan1.test 2ddfe5d168462170c4487f534e2a99fb006b2076
|
||||
F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
|
||||
F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
|
||||
F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
|
||||
F test/skipscan6.test 3a891b45d6df266ced861a2ad9d03fca2bc7fcc5
|
||||
F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b
|
||||
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
|
||||
F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
|
||||
F test/sort.test c4400e7533748f6bd7413851ff148645e82b9e2d
|
||||
F test/sort2.test 269f4f50c6e468cc32b302ae7ff0add8338ec6de
|
||||
F test/sort2.test 84a92eebf697feee9c6697746918c7d67373eea1
|
||||
F test/sort3.test 6178ade30810ac9166fcdf14b7065e49c0f534e2
|
||||
F test/sort4.test 6c37d85f7cd28d50cce222fcab84ccd771e105cb
|
||||
F test/sort5.test a448240a42b49239edc00f85d6d7ac7a1b261e1f
|
||||
@@ -876,7 +892,7 @@ F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
|
||||
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
|
||||
F test/syscall.test d2fdaad713f103ac611fe7ef9b724c7b69f8149c
|
||||
F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6
|
||||
F test/table.test 2a1d2fa52c531de5915f28023747d9a8c27b6f31
|
||||
F test/table.test 06271d61eb13871490d38168433c1ef3dd82bb2a
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
|
||||
F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
|
||||
@@ -946,7 +962,7 @@ F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09
|
||||
F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30
|
||||
F test/tkt-f3e5abed55.test d5a0126118142d13e27f6ce9f4c47096e9321c00
|
||||
F test/tkt-f67b41381a.test a23bc124c981662db712167bacd0ed8ad11abac9
|
||||
F test/tkt-f777251dc7a.test af6531446c64bfd268416f07b4df7be7f9c749d2
|
||||
F test/tkt-f777251dc7a.test d1a8fc3eefb7a9e64d19ff24d5c8c94c34a632fb
|
||||
F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7
|
||||
F test/tkt-f973c7ac31.test 28ef85c7f015477916795246d8286aeda39d4ead
|
||||
F test/tkt-fa7bf5ec.test 9102dfea58aa371d78969da735f9392c57e2e035
|
||||
@@ -1041,7 +1057,7 @@ F test/trace.test 73a5508100f7fccfbc3f8018d5f6963ed478eea0
|
||||
F test/trace2.test 93b47ca6996c66b47f57224cfb146f34e07df382
|
||||
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
|
||||
F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
|
||||
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
|
||||
F test/transitive1.test 03f532954f46cdf5608f7766bff0b0c52bf2a7cd
|
||||
F test/trigger1.test dc47573ac79ffe0ee3eecaa517d70d8dacbccd03
|
||||
F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6
|
||||
@@ -1051,7 +1067,7 @@ F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
|
||||
F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9
|
||||
F test/trigger7.test b39e6dee1debe0ff9c2ef66326668f149f07c9c4
|
||||
F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
|
||||
F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31
|
||||
F test/trigger9.test 2226ec795a33b0460ab5cf8891e9054cc7edef41
|
||||
F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
|
||||
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
||||
F test/triggerC.test a68980c5955d62ee24be6f97129d824f199f9a4c
|
||||
@@ -1065,7 +1081,7 @@ F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
|
||||
F test/unique2.test 41e7f83c6827605991160a31380148a9fc5f1339
|
||||
F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825
|
||||
F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
|
||||
F test/update.test 1b6c488a8f993d090b7ee9ad0e234faa161b3aeb
|
||||
F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
|
||||
F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b
|
||||
F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
@@ -1076,7 +1092,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
|
||||
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
||||
F test/view.test f311691d696a5cc27e3c1b875cec1b0866b4ccd9
|
||||
F test/vtab1.test b631d147b198cfd7903ab5fed028eb2a3d321dc6
|
||||
F test/vtab1.test 1cef14310144718812351a61c5cfb4ba8494a171
|
||||
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
|
||||
F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e
|
||||
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
|
||||
@@ -1098,7 +1114,7 @@ F test/wal.test 885f32b2b390b30b4aa3dbb0e568f8f78d40f5cc
|
||||
F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada
|
||||
F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0
|
||||
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
|
||||
F test/wal5.test 8f888b50f66b78821e61ed0e233ded5de378224b
|
||||
F test/wal5.test 174cc1512e304a7dfa28ac30527e28ea02fc37df
|
||||
F test/wal6.test 527581f5527bf9c24394991e2be83000aace5f9e
|
||||
F test/wal64k.test 163655ecd2cb8afef4737cac2a40fdd2eeaf20b8
|
||||
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
|
||||
@@ -1139,7 +1155,7 @@ F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
|
||||
F test/whereG.test 69f5ec4b15760a8c860f80e2d55525669390aab3
|
||||
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
|
||||
F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
|
||||
F test/whereJ.test 63599653dfefe4e74ebb358db753417fe0aa8a49
|
||||
F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
||||
@@ -1152,7 +1168,7 @@ F test/without_rowid1.test 7862e605753c8d25329f665fa09072e842183151
|
||||
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
||||
F test/without_rowid3.test 1081aabf60a1e1123b7f9a8f6ae19954351843b0
|
||||
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
|
||||
F test/without_rowid5.test b4a639a367f04d382d20e8f44fc1be4f2d57d107
|
||||
F test/without_rowid5.test 61256715b686359df48ca1742db50cc7e3e7b862
|
||||
F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
|
||||
@@ -1187,7 +1203,7 @@ F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
|
||||
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
|
||||
F tool/showdb.c bd073a78bce714a0e42d92ea474b3eb8cb53be5d
|
||||
F tool/showjournal.c 053eb1cc774710c6890b7dd6293300cc297b16a5
|
||||
F tool/showstat4.c c39279d6bd37cb999b634f0064f6f86ad7af008f
|
||||
F tool/showstat4.c 9515faa8ec176599d4a8288293ba8ec61f7b728a
|
||||
F tool/showwal.c 85cb36d4fe3e93e2fbd63e786e0d1ce42d0c4fad
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
|
||||
@@ -1202,12 +1218,16 @@ F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
|
||||
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
|
||||
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
|
||||
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
|
||||
F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003
|
||||
F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c
|
||||
F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P fc6920b5483eeeb06a474ff399a21afa51dc4859 1f80f8c136ac970dcc7fb2337263dc5922e348c3
|
||||
R ed2304a6a2e587056109284e9717f063
|
||||
U dan
|
||||
Z 986c15e36469a97fa56839a4b2a55b25
|
||||
P 71f589e3f82a07513425e212072f32748c0732d4
|
||||
R 02b8ea23886487ef9860b5ee40d238ab
|
||||
T *branch * collseq-checking
|
||||
T *sym-collseq-checking *
|
||||
T -sym-fix-stale-keyinfo-cache *
|
||||
U drh
|
||||
Z 74adfe1c6f95aa3cd0aa6e2d3a771e07
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
face33bea1ba3a6d57780655fa827226b4d2baa9
|
||||
68b23c3d41ce9303f42251d0c199181312e148f9
|
||||
@@ -2,7 +2,7 @@
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH SQLITE3 1 "Mon Jan 31 11:14:00 2014"
|
||||
.TH SQLITE3 1 "Fri Oct 31 10:41:31 EDT 2014"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
@@ -49,7 +49,7 @@ a table named "memos" and insert a couple of records into that table:
|
||||
$
|
||||
.B sqlite3 mydata.db
|
||||
.br
|
||||
SQLite version 3.8.3
|
||||
SQLite version 3.8.8
|
||||
.br
|
||||
Enter ".help" for instructions
|
||||
.br
|
||||
@@ -107,26 +107,29 @@ the '.help' command. For example:
|
||||
sqlite>
|
||||
.B .help
|
||||
.nf
|
||||
.cc |
|
||||
.backup ?DB? FILE Backup DB (default "main") to FILE
|
||||
.bail ON|OFF Stop after hitting an error. Default OFF
|
||||
.databases List names and files of attached databases
|
||||
.dump ?TABLE? ... Dump the database in an SQL text format
|
||||
.tr %.
|
||||
%backup ?DB? FILE Backup DB (default "main") to FILE
|
||||
%bail on|off Stop after hitting an error. Default OFF
|
||||
%clone NEWDB Clone data into NEWDB from the existing database
|
||||
%databases List names and files of attached databases
|
||||
%dump ?TABLE? ... Dump the database in an SQL text format
|
||||
If TABLE specified, only dump tables matching
|
||||
LIKE pattern TABLE.
|
||||
.echo ON|OFF Turn command echo on or off
|
||||
.exit Exit this program
|
||||
.explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.
|
||||
%echo on|off Turn command echo on or off
|
||||
%eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN
|
||||
%exit Exit this program
|
||||
%explain ?on|off? Turn output mode suitable for EXPLAIN on or off.
|
||||
With no args, it turns EXPLAIN on.
|
||||
.header(s) ON|OFF Turn display of headers on or off
|
||||
.help Show this message
|
||||
.import FILE TABLE Import data from FILE into TABLE
|
||||
.indices ?TABLE? Show names of all indices
|
||||
%fullschema Show schema and the content of sqlite_stat tables
|
||||
%headers on|off Turn display of headers on or off
|
||||
%help Show this message
|
||||
%import FILE TABLE Import data from FILE into TABLE
|
||||
%indices ?TABLE? Show names of all indices
|
||||
If TABLE specified, only show indices for tables
|
||||
matching LIKE pattern TABLE.
|
||||
.load FILE ?ENTRY? Load an extension library
|
||||
.log FILE|off Turn logging on or off. FILE can be stderr/stdout
|
||||
.mode MODE ?TABLE? Set output mode where MODE is one of:
|
||||
%load FILE ?ENTRY? Load an extension library
|
||||
%log FILE|off Turn logging on or off. FILE can be stderr/stdout
|
||||
%mode MODE ?TABLE? Set output mode where MODE is one of:
|
||||
csv Comma-separated values
|
||||
column Left-aligned columns. (See .width)
|
||||
html HTML <table> code
|
||||
@@ -135,31 +138,35 @@ sqlite>
|
||||
list Values delimited by .separator string
|
||||
tabs Tab-separated values
|
||||
tcl TCL list elements
|
||||
.nullvalue STRING Use STRING in place of NULL values
|
||||
.open ?FILENAME? Close existing database and reopen FILENAME
|
||||
.output FILENAME Send output to FILENAME
|
||||
.output stdout Send output to the screen
|
||||
.print STRING... Print literal STRING
|
||||
.prompt MAIN CONTINUE Replace the standard prompts
|
||||
.quit Exit this program
|
||||
.read FILENAME Execute SQL in FILENAME
|
||||
.restore ?DB? FILE Restore content of DB (default "main") from FILE
|
||||
.schema ?TABLE? Show the CREATE statements
|
||||
%nullvalue STRING Use STRING in place of NULL values
|
||||
%once FILENAME Output for the next SQL command only to FILENAME
|
||||
%open ?FILENAME? Close existing database and reopen FILENAME
|
||||
%output ?FILENAME? Send output to FILENAME or stdout
|
||||
%print STRING... Print literal STRING
|
||||
%prompt MAIN CONTINUE Replace the standard prompts
|
||||
%quit Exit this program
|
||||
%read FILENAME Execute SQL in FILENAME
|
||||
%restore ?DB? FILE Restore content of DB (default "main") from FILE
|
||||
%save FILE Write in-memory database into FILE
|
||||
%schema ?TABLE? Show the CREATE statements
|
||||
If TABLE specified, only show tables matching
|
||||
LIKE pattern TABLE.
|
||||
.separator STRING Change separator used by output mode and .import
|
||||
.show Show the current values for various settings
|
||||
.stats ON|OFF Turn stats on or off
|
||||
.tables ?TABLE? List names of tables
|
||||
%separator STRING ?NL? Change separator used by output mode and .import
|
||||
NL is the end-of-line mark for CSV
|
||||
%shell CMD ARGS... Run CMD ARGS... in a system shell
|
||||
%show Show the current values for various settings
|
||||
%stats on|off Turn stats on or off
|
||||
%system CMD ARGS... Run CMD ARGS... in a system shell
|
||||
%tables ?TABLE? List names of tables
|
||||
If TABLE specified, only list tables matching
|
||||
LIKE pattern TABLE.
|
||||
.timeout MS Try opening locked tables for MS milliseconds
|
||||
.trace FILE|off Output each SQL statement as it is run
|
||||
.vfsname ?AUX? Print the name of the VFS stack
|
||||
.width NUM1 NUM2 ... Set column widths for "column" mode
|
||||
.timer ON|OFF Turn the CPU timer measurement on or off
|
||||
%timeout MS Try opening locked tables for MS milliseconds
|
||||
%timer on|off Turn SQL timer on or off
|
||||
%trace FILE|off Output each SQL statement as it is run
|
||||
%vfsname ?AUX? Print the name of the VFS stack
|
||||
%width NUM1 NUM2 ... Set column widths for "column" mode
|
||||
Negative values right-justify
|
||||
sqlite>
|
||||
|cc .
|
||||
.sp
|
||||
.fi
|
||||
.SH OPTIONS
|
||||
@@ -269,7 +276,7 @@ o If the -init option is present, the specified file is processed.
|
||||
o All other command line options are processed.
|
||||
|
||||
.SH SEE ALSO
|
||||
http://www.sqlite.org/
|
||||
http://www.sqlite.org/cli.html
|
||||
.br
|
||||
The sqlite3-doc package.
|
||||
.SH AUTHOR
|
||||
|
||||
+18
-13
@@ -1457,23 +1457,28 @@ static void decodeIntArray(
|
||||
if( *z==' ' ) z++;
|
||||
}
|
||||
#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
assert( pIndex!=0 );
|
||||
assert( pIndex!=0 ); {
|
||||
#else
|
||||
if( pIndex )
|
||||
if( pIndex ){
|
||||
#endif
|
||||
while( z[0] ){
|
||||
if( sqlite3_strglob("unordered*", z)==0 ){
|
||||
pIndex->bUnordered = 1;
|
||||
}else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
|
||||
pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
|
||||
}
|
||||
pIndex->bUnordered = 0;
|
||||
pIndex->noSkipScan = 0;
|
||||
while( z[0] ){
|
||||
if( sqlite3_strglob("unordered*", z)==0 ){
|
||||
pIndex->bUnordered = 1;
|
||||
}else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
|
||||
pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
|
||||
}else if( sqlite3_strglob("noskipscan*", z)==0 ){
|
||||
pIndex->noSkipScan = 1;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_COSTMULT
|
||||
else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
|
||||
pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
|
||||
}
|
||||
else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
|
||||
pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
|
||||
}
|
||||
#endif
|
||||
while( z[0]!=0 && z[0]!=' ' ) z++;
|
||||
while( z[0]==' ' ) z++;
|
||||
while( z[0]!=0 && z[0]!=' ' ) z++;
|
||||
while( z[0]==' ' ) z++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ int sqlite3_set_authorizer(
|
||||
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
|
||||
void *pArg
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->xAuth = (sqlite3_xauth)xAuth;
|
||||
db->pAuthArg = pArg;
|
||||
|
||||
+45
-6
@@ -122,6 +122,20 @@ static int setDestPgsz(sqlite3_backup *p){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check that there is no open read-transaction on the b-tree passed as the
|
||||
** second argument. If there is not, return SQLITE_OK. Otherwise, if there
|
||||
** is an open read-transaction, return SQLITE_ERROR and leave an error
|
||||
** message in database handle db.
|
||||
*/
|
||||
static int checkReadTransaction(sqlite3 *db, Btree *p){
|
||||
if( sqlite3BtreeIsInReadTrans(p) ){
|
||||
sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create an sqlite3_backup process to copy the contents of zSrcDb from
|
||||
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
|
||||
@@ -138,6 +152,13 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
){
|
||||
sqlite3_backup *p; /* Value to return */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(pSrcDb)||!sqlite3SafetyCheckOk(pDestDb) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Lock the source database handle. The destination database
|
||||
** handle is not locked in this routine, but it is locked in
|
||||
** sqlite3_backup_step(). The user is required to ensure that no
|
||||
@@ -174,12 +195,15 @@ sqlite3_backup *sqlite3_backup_init(
|
||||
p->iNext = 1;
|
||||
p->isAttached = 0;
|
||||
|
||||
if( 0==p->pSrc || 0==p->pDest || setDestPgsz(p)==SQLITE_NOMEM ){
|
||||
if( 0==p->pSrc || 0==p->pDest
|
||||
|| setDestPgsz(p)==SQLITE_NOMEM
|
||||
|| checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
|
||||
){
|
||||
/* One (or both) of the named databases did not exist or an OOM
|
||||
** error was hit. The error has already been written into the
|
||||
** pDestDb handle. All that is left to do here is free the
|
||||
** sqlite3_backup structure.
|
||||
*/
|
||||
** error was hit. Or there is a transaction open on the destination
|
||||
** database. The error has already been written into the pDestDb
|
||||
** handle. All that is left to do here is free the sqlite3_backup
|
||||
** structure. */
|
||||
sqlite3_free(p);
|
||||
p = 0;
|
||||
}
|
||||
@@ -334,6 +358,9 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
||||
int pgszSrc = 0; /* Source page size */
|
||||
int pgszDest = 0; /* Destination page size */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( p==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(p->pSrcDb->mutex);
|
||||
sqlite3BtreeEnter(p->pSrc);
|
||||
if( p->pDestDb ){
|
||||
@@ -597,7 +624,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
}
|
||||
|
||||
/* If a transaction is still open on the Btree, roll it back. */
|
||||
sqlite3BtreeRollback(p->pDest, SQLITE_OK);
|
||||
sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0);
|
||||
|
||||
/* Set the error code of the destination database handle. */
|
||||
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
|
||||
@@ -623,6 +650,12 @@ int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
** call to sqlite3_backup_step().
|
||||
*/
|
||||
int sqlite3_backup_remaining(sqlite3_backup *p){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( p==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return p->nRemaining;
|
||||
}
|
||||
|
||||
@@ -631,6 +664,12 @@ int sqlite3_backup_remaining(sqlite3_backup *p){
|
||||
** recent call to sqlite3_backup_step().
|
||||
*/
|
||||
int sqlite3_backup_pagecount(sqlite3_backup *p){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( p==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return p->nPagecount;
|
||||
}
|
||||
|
||||
|
||||
+300
-123
@@ -1139,6 +1139,11 @@ static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
|
||||
** end of the page and all free space is collected into one
|
||||
** big FreeBlk that occurs in between the header and cell
|
||||
** pointer array and the cell content area.
|
||||
**
|
||||
** EVIDENCE-OF: R-44582-60138 SQLite may from time to time reorganize a
|
||||
** b-tree page so that there are no freeblocks or fragment bytes, all
|
||||
** unused bytes are contained in the unallocated space region, and all
|
||||
** cells are packed tightly at the end of the page.
|
||||
*/
|
||||
static int defragmentPage(MemPage *pPage){
|
||||
int i; /* Loop counter */
|
||||
@@ -1231,10 +1236,8 @@ static int defragmentPage(MemPage *pPage){
|
||||
**
|
||||
** If no suitable space can be found on the free-list, return NULL.
|
||||
**
|
||||
** This function may detect corruption within pPg. If it does and argument
|
||||
** pRc is non-NULL, then *pRc is set to SQLITE_CORRUPT and NULL is returned.
|
||||
** Or, if corruption is detected and pRc is NULL, NULL is returned and the
|
||||
** corruption goes unreported.
|
||||
** This function may detect corruption within pPg. If corruption is
|
||||
** detected then *pRc is set to SQLITE_CORRUPT and NULL is returned.
|
||||
**
|
||||
** If a slot of at least nByte bytes is found but cannot be used because
|
||||
** there are already at least 60 fragmented bytes on the page, return NULL.
|
||||
@@ -1249,16 +1252,23 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
|
||||
|
||||
for(iAddr=hdr+1; (pc = get2byte(&aData[iAddr]))>0; iAddr=pc){
|
||||
int size; /* Size of the free slot */
|
||||
/* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
|
||||
** increasing offset. */
|
||||
if( pc>usableSize-4 || pc<iAddr+4 ){
|
||||
if( pRc ) *pRc = SQLITE_CORRUPT_BKPT;
|
||||
*pRc = SQLITE_CORRUPT_BKPT;
|
||||
return 0;
|
||||
}
|
||||
/* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each
|
||||
** freeblock form a big-endian integer which is the size of the freeblock
|
||||
** in bytes, including the 4-byte header. */
|
||||
size = get2byte(&aData[pc+2]);
|
||||
if( size>=nByte ){
|
||||
int x = size - nByte;
|
||||
testcase( x==4 );
|
||||
testcase( x==3 );
|
||||
if( x<4 ){
|
||||
/* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total
|
||||
** number of bytes in fragments may not exceed 60. */
|
||||
if( aData[hdr+7]>=60 ){
|
||||
if( pbDefrag ) *pbDefrag = 1;
|
||||
return 0;
|
||||
@@ -1268,7 +1278,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc, int *pbDefrag){
|
||||
memcpy(&aData[iAddr], &aData[pc], 2);
|
||||
aData[hdr+7] += (u8)x;
|
||||
}else if( size+pc > usableSize ){
|
||||
if( pRc ) *pRc = SQLITE_CORRUPT_BKPT;
|
||||
*pRc = SQLITE_CORRUPT_BKPT;
|
||||
return 0;
|
||||
}else{
|
||||
/* The slot remains on the free-list. Reduce its size to account
|
||||
@@ -1299,9 +1309,8 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */
|
||||
u8 * const data = pPage->aData; /* Local cache of pPage->aData */
|
||||
int top; /* First byte of cell content area */
|
||||
int rc = SQLITE_OK; /* Integer return code */
|
||||
int gap; /* First byte of gap between cell pointers and cell content */
|
||||
int rc; /* Integer return code */
|
||||
int usableSize; /* Usable size of the page */
|
||||
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
assert( pPage->pBt );
|
||||
@@ -1309,20 +1318,18 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
assert( nByte>=0 ); /* Minimum cell size is 4 */
|
||||
assert( pPage->nFree>=nByte );
|
||||
assert( pPage->nOverflow==0 );
|
||||
usableSize = pPage->pBt->usableSize;
|
||||
assert( nByte < usableSize-8 );
|
||||
assert( nByte < (int)(pPage->pBt->usableSize-8) );
|
||||
|
||||
assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
|
||||
gap = pPage->cellOffset + 2*pPage->nCell;
|
||||
assert( gap<=65536 );
|
||||
top = get2byte(&data[hdr+5]);
|
||||
if( gap>top ){
|
||||
if( top==0 ){
|
||||
top = 65536;
|
||||
}else{
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
/* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size
|
||||
** and the reserved space is zero (the usual value for reserved space)
|
||||
** then the cell content offset of an empty page wants to be 65536.
|
||||
** However, that integer is too large to be stored in a 2-byte unsigned
|
||||
** integer, so a value of 0 is used in its place. */
|
||||
top = get2byteNotZero(&data[hdr+5]);
|
||||
if( gap>top ) return SQLITE_CORRUPT_BKPT;
|
||||
|
||||
/* If there is enough space between gap and top for one more cell pointer
|
||||
** array entry offset, and if the freelist is not empty, then search the
|
||||
@@ -1332,13 +1339,13 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
testcase( gap+1==top );
|
||||
testcase( gap==top );
|
||||
if( gap+2<=top && (data[hdr+1] || data[hdr+2]) ){
|
||||
int rc = SQLITE_OK;
|
||||
int bDefrag = 0;
|
||||
u8 *pSpace = pageFindSlot(pPage, nByte, &rc, &bDefrag);
|
||||
if( rc ) return rc;
|
||||
if( bDefrag ) goto defragment_page;
|
||||
if( pSpace ){
|
||||
*pIdx = pSpace - data;
|
||||
assert( pSpace>=data && (pSpace - data)<65536 );
|
||||
*pIdx = (int)(pSpace - data);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
@@ -1349,7 +1356,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
||||
testcase( gap+2+nByte==top );
|
||||
if( gap+2+nByte>top ){
|
||||
defragment_page:
|
||||
testcase( pPage->nCell==0 );
|
||||
assert( pPage->nCell>0 || CORRUPT_DB );
|
||||
rc = defragmentPage(pPage);
|
||||
if( rc ) return rc;
|
||||
top = get2byteNotZero(&data[hdr+5]);
|
||||
@@ -1491,18 +1498,32 @@ static int decodeFlags(MemPage *pPage, int flagByte){
|
||||
pPage->childPtrSize = 4-4*pPage->leaf;
|
||||
pBt = pPage->pBt;
|
||||
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
|
||||
/* EVIDENCE-OF: R-03640-13415 A value of 5 means the page is an interior
|
||||
** table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
|
||||
/* EVIDENCE-OF: R-20501-61796 A value of 13 means the page is a leaf
|
||||
** table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
|
||||
pPage->intKey = 1;
|
||||
pPage->intKeyLeaf = pPage->leaf;
|
||||
pPage->noPayload = !pPage->leaf;
|
||||
pPage->maxLocal = pBt->maxLeaf;
|
||||
pPage->minLocal = pBt->minLeaf;
|
||||
}else if( flagByte==PTF_ZERODATA ){
|
||||
/* EVIDENCE-OF: R-27225-53936 A value of 2 means the page is an interior
|
||||
** index b-tree page. */
|
||||
assert( (PTF_ZERODATA)==2 );
|
||||
/* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf
|
||||
** index b-tree page. */
|
||||
assert( (PTF_ZERODATA|PTF_LEAF)==10 );
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->noPayload = 0;
|
||||
pPage->maxLocal = pBt->maxLocal;
|
||||
pPage->minLocal = pBt->minLocal;
|
||||
}else{
|
||||
/* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is
|
||||
** an error. */
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pPage->max1bytePayload = pBt->max1bytePayload;
|
||||
@@ -1542,21 +1563,33 @@ static int btreeInitPage(MemPage *pPage){
|
||||
|
||||
hdr = pPage->hdrOffset;
|
||||
data = pPage->aData;
|
||||
/* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
|
||||
** the b-tree page type. */
|
||||
if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
|
||||
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
|
||||
pPage->maskPage = (u16)(pBt->pageSize - 1);
|
||||
pPage->nOverflow = 0;
|
||||
usableSize = pBt->usableSize;
|
||||
pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
|
||||
pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize;
|
||||
pPage->aDataEnd = &data[usableSize];
|
||||
pPage->aCellIdx = &data[cellOffset];
|
||||
/* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates
|
||||
** the start of the cell content area. A zero value for this integer is
|
||||
** interpreted as 65536. */
|
||||
top = get2byteNotZero(&data[hdr+5]);
|
||||
/* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
|
||||
** number of cells on the page. */
|
||||
pPage->nCell = get2byte(&data[hdr+3]);
|
||||
if( pPage->nCell>MX_CELL(pBt) ){
|
||||
/* To many cells for a single page. The page must be corrupt */
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
testcase( pPage->nCell==MX_CELL(pBt) );
|
||||
/* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
|
||||
** possible for a root page of a table that contains no rows) then the
|
||||
** offset to the cell content area will equal the page size minus the
|
||||
** bytes of reserved space. */
|
||||
assert( pPage->nCell>0 || top==usableSize || CORRUPT_DB );
|
||||
|
||||
/* A malformed database page might cause us to read past the end
|
||||
** of page when parsing a cell.
|
||||
@@ -1590,13 +1623,20 @@ static int btreeInitPage(MemPage *pPage){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Compute the total free space on the page */
|
||||
/* Compute the total free space on the page
|
||||
** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the
|
||||
** start of the first freeblock on the page, or is zero if there are no
|
||||
** freeblocks. */
|
||||
pc = get2byte(&data[hdr+1]);
|
||||
nFree = data[hdr+7] + top;
|
||||
nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */
|
||||
while( pc>0 ){
|
||||
u16 next, size;
|
||||
if( pc<iCellFirst || pc>iCellLast ){
|
||||
/* Start of free block is off the page */
|
||||
/* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will
|
||||
** always be at least one cell before the first freeblock.
|
||||
**
|
||||
** Or, the freeblock is off the end of the page
|
||||
*/
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
next = get2byte(&data[pc]);
|
||||
@@ -2002,6 +2042,9 @@ int sqlite3BtreeOpen(
|
||||
#ifdef SQLITE_SECURE_DELETE
|
||||
pBt->btsFlags |= BTS_SECURE_DELETE;
|
||||
#endif
|
||||
/* EVIDENCE-OF: R-51873-39618 The page size for a database file is
|
||||
** determined by the 2-byte integer located at an offset of 16 bytes from
|
||||
** the beginning of the database file. */
|
||||
pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
|
||||
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|
||||
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
|
||||
@@ -2020,6 +2063,9 @@ int sqlite3BtreeOpen(
|
||||
#endif
|
||||
nReserve = 0;
|
||||
}else{
|
||||
/* EVIDENCE-OF: R-37497-42412 The size of the reserved region is
|
||||
** determined by the one-byte unsigned integer found at an offset of 20
|
||||
** into the database file header. */
|
||||
nReserve = zDbHeader[20];
|
||||
pBt->btsFlags |= BTS_PAGESIZE_FIXED;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
@@ -2217,7 +2263,7 @@ int sqlite3BtreeClose(Btree *p){
|
||||
** The call to sqlite3BtreeRollback() drops any table-locks held by
|
||||
** this handle.
|
||||
*/
|
||||
sqlite3BtreeRollback(p, SQLITE_OK);
|
||||
sqlite3BtreeRollback(p, SQLITE_OK, 0);
|
||||
sqlite3BtreeLeave(p);
|
||||
|
||||
/* If there are still other outstanding references to the shared-btree
|
||||
@@ -2529,6 +2575,9 @@ static int lockBtree(BtShared *pBt){
|
||||
u32 usableSize;
|
||||
u8 *page1 = pPage1->aData;
|
||||
rc = SQLITE_NOTADB;
|
||||
/* EVIDENCE-OF: R-43737-39999 Every valid SQLite database file begins
|
||||
** with the following 16 bytes (in hex): 53 51 4c 69 74 65 20 66 6f 72 6d
|
||||
** 61 74 20 33 00. */
|
||||
if( memcmp(page1, zMagicHeader, 16)!=0 ){
|
||||
goto page1_init_failed;
|
||||
}
|
||||
@@ -2569,15 +2618,21 @@ static int lockBtree(BtShared *pBt){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The maximum embedded fraction must be exactly 25%. And the minimum
|
||||
** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
|
||||
/* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload
|
||||
** fractions and the leaf payload fraction values must be 64, 32, and 32.
|
||||
**
|
||||
** The original design allowed these amounts to vary, but as of
|
||||
** version 3.6.0, we require them to be fixed.
|
||||
*/
|
||||
if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
|
||||
goto page1_init_failed;
|
||||
}
|
||||
/* EVIDENCE-OF: R-51873-39618 The page size for a database file is
|
||||
** determined by the 2-byte integer located at an offset of 16 bytes from
|
||||
** the beginning of the database file. */
|
||||
pageSize = (page1[16]<<8) | (page1[17]<<16);
|
||||
/* EVIDENCE-OF: R-25008-21688 The size of a page is a power of two
|
||||
** between 512 and 65536 inclusive. */
|
||||
if( ((pageSize-1)&pageSize)!=0
|
||||
|| pageSize>SQLITE_MAX_PAGE_SIZE
|
||||
|| pageSize<=256
|
||||
@@ -2585,6 +2640,13 @@ static int lockBtree(BtShared *pBt){
|
||||
goto page1_init_failed;
|
||||
}
|
||||
assert( (pageSize & 7)==0 );
|
||||
/* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte
|
||||
** integer at offset 20 is the number of bytes of space at the end of
|
||||
** each page to reserve for extensions.
|
||||
**
|
||||
** EVIDENCE-OF: R-37497-42412 The size of the reserved region is
|
||||
** determined by the one-byte unsigned integer found at an offset of 20
|
||||
** into the database file header. */
|
||||
usableSize = pageSize - page1[20];
|
||||
if( (u32)pageSize!=pBt->pageSize ){
|
||||
/* After reading the first page of the database assuming a page size
|
||||
@@ -2605,6 +2667,9 @@ static int lockBtree(BtShared *pBt){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto page1_init_failed;
|
||||
}
|
||||
/* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to
|
||||
** be less than 480. In other words, if the page size is 512, then the
|
||||
** reserved space size cannot exceed 32. */
|
||||
if( usableSize<480 ){
|
||||
goto page1_init_failed;
|
||||
}
|
||||
@@ -3510,60 +3575,91 @@ int sqlite3BtreeCommit(Btree *p){
|
||||
|
||||
/*
|
||||
** This routine sets the state to CURSOR_FAULT and the error
|
||||
** code to errCode for every cursor on BtShared that pBtree
|
||||
** references.
|
||||
** code to errCode for every cursor on any BtShared that pBtree
|
||||
** references. Or if the writeOnly flag is set to 1, then only
|
||||
** trip write cursors and leave read cursors unchanged.
|
||||
**
|
||||
** Every cursor is tripped, including cursors that belong
|
||||
** to other database connections that happen to be sharing
|
||||
** the cache with pBtree.
|
||||
** Every cursor is a candidate to be tripped, including cursors
|
||||
** that belong to other database connections that happen to be
|
||||
** sharing the cache with pBtree.
|
||||
**
|
||||
** This routine gets called when a rollback occurs.
|
||||
** All cursors using the same cache must be tripped
|
||||
** to prevent them from trying to use the btree after
|
||||
** the rollback. The rollback may have deleted tables
|
||||
** or moved root pages, so it is not sufficient to
|
||||
** save the state of the cursor. The cursor must be
|
||||
** invalidated.
|
||||
** This routine gets called when a rollback occurs. If the writeOnly
|
||||
** flag is true, then only write-cursors need be tripped - read-only
|
||||
** cursors save their current positions so that they may continue
|
||||
** following the rollback. Or, if writeOnly is false, all cursors are
|
||||
** tripped. In general, writeOnly is false if the transaction being
|
||||
** rolled back modified the database schema. In this case b-tree root
|
||||
** pages may be moved or deleted from the database altogether, making
|
||||
** it unsafe for read cursors to continue.
|
||||
**
|
||||
** If the writeOnly flag is true and an error is encountered while
|
||||
** saving the current position of a read-only cursor, all cursors,
|
||||
** including all read-cursors are tripped.
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or if an error occurs while
|
||||
** saving a cursor position, an SQLite error code.
|
||||
*/
|
||||
void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
|
||||
int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
|
||||
BtCursor *p;
|
||||
if( pBtree==0 ) return;
|
||||
sqlite3BtreeEnter(pBtree);
|
||||
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
|
||||
int i;
|
||||
sqlite3BtreeClearCursor(p);
|
||||
p->eState = CURSOR_FAULT;
|
||||
p->skipNext = errCode;
|
||||
for(i=0; i<=p->iPage; i++){
|
||||
releasePage(p->apPage[i]);
|
||||
p->apPage[i] = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
|
||||
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 ){
|
||||
rc = saveCursorPosition(p);
|
||||
if( rc!=SQLITE_OK ){
|
||||
(void)sqlite3BtreeTripAllCursors(pBtree, rc, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
sqlite3BtreeClearCursor(p);
|
||||
p->eState = CURSOR_FAULT;
|
||||
p->skipNext = errCode;
|
||||
}
|
||||
for(i=0; i<=p->iPage; i++){
|
||||
releasePage(p->apPage[i]);
|
||||
p->apPage[i] = 0;
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
}
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rollback the transaction in progress. All cursors will be
|
||||
** invalided by this operation. Any attempt to use a cursor
|
||||
** that was open at the beginning of this operation will result
|
||||
** in an error.
|
||||
** Rollback the transaction in progress.
|
||||
**
|
||||
** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
|
||||
** Only write cursors are tripped if writeOnly is true but all cursors are
|
||||
** tripped if writeOnly is false. Any attempt to use
|
||||
** a tripped cursor will result in an error.
|
||||
**
|
||||
** This will release the write lock on the database file. If there
|
||||
** are no active cursors, it also releases the read lock.
|
||||
*/
|
||||
int sqlite3BtreeRollback(Btree *p, int tripCode){
|
||||
int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
|
||||
int rc;
|
||||
BtShared *pBt = p->pBt;
|
||||
MemPage *pPage1;
|
||||
|
||||
assert( writeOnly==1 || writeOnly==0 );
|
||||
assert( tripCode==SQLITE_ABORT_ROLLBACK || tripCode==SQLITE_OK );
|
||||
sqlite3BtreeEnter(p);
|
||||
if( tripCode==SQLITE_OK ){
|
||||
rc = tripCode = saveAllCursors(pBt, 0, 0);
|
||||
if( rc ) writeOnly = 0;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
if( tripCode ){
|
||||
sqlite3BtreeTripAllCursors(p, tripCode);
|
||||
int rc2 = sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
|
||||
assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) );
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
btreeIntegrity(p);
|
||||
|
||||
@@ -3898,13 +3994,9 @@ int sqlite3BtreeCursorIsValid(BtCursor *pCur){
|
||||
*/
|
||||
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
|
||||
if( pCur->eState!=CURSOR_VALID ){
|
||||
*pSize = 0;
|
||||
}else{
|
||||
getCellInfo(pCur);
|
||||
*pSize = pCur->info.nKey;
|
||||
}
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
getCellInfo(pCur);
|
||||
*pSize = pCur->info.nKey;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -5155,6 +5247,8 @@ static int allocateBtreePage(
|
||||
assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
|
||||
pPage1 = pBt->pPage1;
|
||||
mxPage = btreePagecount(pBt);
|
||||
/* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
|
||||
** stores stores the total number of pages on the freelist. */
|
||||
n = get4byte(&pPage1->aData[36]);
|
||||
testcase( n==mxPage-1 );
|
||||
if( n>=mxPage ){
|
||||
@@ -5201,8 +5295,14 @@ static int allocateBtreePage(
|
||||
do {
|
||||
pPrevTrunk = pTrunk;
|
||||
if( pPrevTrunk ){
|
||||
/* EVIDENCE-OF: R-01506-11053 The first integer on a freelist trunk page
|
||||
** is the page number of the next freelist trunk page in the list or
|
||||
** zero if this is the last freelist trunk page. */
|
||||
iTrunk = get4byte(&pPrevTrunk->aData[0]);
|
||||
}else{
|
||||
/* EVIDENCE-OF: R-59841-13798 The 4-byte big-endian integer at offset 32
|
||||
** stores the page number of the first page of the freelist, or zero if
|
||||
** the freelist is empty. */
|
||||
iTrunk = get4byte(&pPage1->aData[32]);
|
||||
}
|
||||
testcase( iTrunk==mxPage );
|
||||
@@ -5217,8 +5317,9 @@ static int allocateBtreePage(
|
||||
}
|
||||
assert( pTrunk!=0 );
|
||||
assert( pTrunk->aData!=0 );
|
||||
|
||||
k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */
|
||||
/* EVIDENCE-OF: R-13523-04394 The second integer on a freelist trunk page
|
||||
** is the number of leaf page pointers to follow. */
|
||||
k = get4byte(&pTrunk->aData[4]);
|
||||
if( k==0 && !searchList ){
|
||||
/* The trunk has no leaves and the list is not being searched.
|
||||
** So extract the trunk page itself and use it as the newly
|
||||
@@ -5536,6 +5637,11 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
|
||||
** for now. At some point in the future (once everyone has upgraded
|
||||
** to 3.6.0 or later) we should consider fixing the conditional above
|
||||
** to read "usableSize/4-2" instead of "usableSize/4-8".
|
||||
**
|
||||
** EVIDENCE-OF: R-19920-11576 However, newer versions of SQLite still
|
||||
** avoid using the last six entries in the freelist trunk page array in
|
||||
** order that database files created by newer versions of SQLite can be
|
||||
** read by older versions of SQLite.
|
||||
*/
|
||||
rc = sqlite3PagerWrite(pTrunk->pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -5887,9 +5993,17 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
return;
|
||||
}
|
||||
pPage->nCell--;
|
||||
memmove(ptr, ptr+2, 2*(pPage->nCell - idx));
|
||||
put2byte(&data[hdr+3], pPage->nCell);
|
||||
pPage->nFree += 2;
|
||||
if( pPage->nCell==0 ){
|
||||
memset(&data[hdr+1], 0, 4);
|
||||
data[hdr+7] = 0;
|
||||
put2byte(&data[hdr+5], pPage->pBt->usableSize);
|
||||
pPage->nFree = pPage->pBt->usableSize - pPage->hdrOffset
|
||||
- pPage->childPtrSize - 8;
|
||||
}else{
|
||||
memmove(ptr, ptr+2, 2*(pPage->nCell - idx));
|
||||
put2byte(&data[hdr+3], pPage->nCell);
|
||||
pPage->nFree += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6078,8 +6192,9 @@ static int pageInsertArray(
|
||||
assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */
|
||||
for(i=0; i<nCell; i++){
|
||||
int sz = szCell[i];
|
||||
int rc;
|
||||
u8 *pSlot;
|
||||
if( bFreelist==0 || (pSlot = pageFindSlot(pPg, sz, 0, 0))==0 ){
|
||||
if( bFreelist==0 || (pSlot = pageFindSlot(pPg, sz, &rc, 0))==0 ){
|
||||
pData -= sz;
|
||||
if( pData<pBegin ) return 1;
|
||||
pSlot = pData;
|
||||
@@ -6120,7 +6235,10 @@ static int pageFreeArray(
|
||||
if( pCell>=pStart && pCell<pEnd ){
|
||||
int sz = szCell[i];
|
||||
if( pFree!=(pCell + sz) ){
|
||||
if( pFree ) freeSpace(pPg, pFree - aData, szFree);
|
||||
if( pFree ){
|
||||
assert( pFree>aData && (pFree - aData)<65536 );
|
||||
freeSpace(pPg, (u16)(pFree - aData), szFree);
|
||||
}
|
||||
pFree = pCell;
|
||||
szFree = sz;
|
||||
if( pFree+sz>pEnd ) return 0;
|
||||
@@ -6131,11 +6249,22 @@ static int pageFreeArray(
|
||||
nRet++;
|
||||
}
|
||||
}
|
||||
if( pFree ) freeSpace(pPg, pFree - aData, szFree);
|
||||
if( pFree ){
|
||||
assert( pFree>aData && (pFree - aData)<65536 );
|
||||
freeSpace(pPg, (u16)(pFree - aData), szFree);
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** apCell[] and szCell[] contains pointers to and sizes of all cells in the
|
||||
** pages being balanced. The current page, pPg, has pPg->nCell cells starting
|
||||
** with apCell[iOld]. After balancing, this page should hold nNew cells
|
||||
** starting at apCell[iNew].
|
||||
**
|
||||
** This routine makes the necessary adjustments to pPg so that it contains
|
||||
** the correct cells after being balanced.
|
||||
**
|
||||
** The pPg->nFree field is invalid when this function returns. It is the
|
||||
** responsibility of the caller to set it correctly.
|
||||
*/
|
||||
@@ -6176,12 +6305,13 @@ static void editPage(
|
||||
);
|
||||
}
|
||||
|
||||
pData = &aData[get2byte(&aData[hdr+5])];
|
||||
pData = &aData[get2byteNotZero(&aData[hdr+5])];
|
||||
if( pData<pBegin ) goto editpage_fail;
|
||||
|
||||
/* Add cells to the start of the page */
|
||||
if( iNew<iOld ){
|
||||
int nAdd = iOld-iNew;
|
||||
int nAdd = MIN(nNew,iOld-iNew);
|
||||
assert( (iOld-iNew)<nNew || nCell==0 || CORRUPT_DB );
|
||||
pCellptr = pPg->aCellIdx;
|
||||
memmove(&pCellptr[nAdd*2], pCellptr, nCell*2);
|
||||
if( pageInsertArray(
|
||||
@@ -6195,7 +6325,7 @@ static void editPage(
|
||||
for(i=0; i<pPg->nOverflow; i++){
|
||||
int iCell = (iOld + pPg->aiOvfl[i]) - iNew;
|
||||
if( iCell>=0 && iCell<nNew ){
|
||||
u8 *pCellptr = &pPg->aCellIdx[iCell * 2];
|
||||
pCellptr = &pPg->aCellIdx[iCell * 2];
|
||||
memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
|
||||
nCell++;
|
||||
if( pageInsertArray(
|
||||
@@ -6286,7 +6416,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
||||
assert( pPage->nOverflow==1 );
|
||||
|
||||
/* This error condition is now caught prior to reaching this function */
|
||||
if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT;
|
||||
if( NEVER(pPage->nCell==0) ) return SQLITE_CORRUPT_BKPT;
|
||||
|
||||
/* Allocate a new page. This page will become the right-sibling of
|
||||
** pPage. Make the parent page writable, so that the new divider cell
|
||||
@@ -6529,13 +6659,14 @@ static int balance_nonroot(
|
||||
u8 *apDiv[NB-1]; /* Divider cells in pParent */
|
||||
int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */
|
||||
int cntOld[NB+2]; /* Old index in aCell[] after i-th page */
|
||||
int szNew[NB+2]; /* Combined size of cells place on i-th page */
|
||||
int szNew[NB+2]; /* Combined size of cells placed on i-th page */
|
||||
u8 **apCell = 0; /* All cells begin balanced */
|
||||
u16 *szCell; /* Local size of all cells in apCell[] */
|
||||
u8 *aSpace1; /* Space for copies of dividers cells */
|
||||
Pgno pgno; /* Temp var to store a page number in */
|
||||
u8 abDone[NB+2]; /* True after i'th new page is populated */
|
||||
Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
|
||||
Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */
|
||||
u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */
|
||||
|
||||
memset(abDone, 0, sizeof(abDone));
|
||||
@@ -6651,6 +6782,10 @@ static int balance_nonroot(
|
||||
nMaxCells*sizeof(u8*) /* apCell */
|
||||
+ nMaxCells*sizeof(u16) /* szCell */
|
||||
+ pBt->pageSize; /* aSpace1 */
|
||||
|
||||
/* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer
|
||||
** that is more than 6 times the database page size. */
|
||||
assert( szScratch<=6*pBt->pageSize );
|
||||
apCell = sqlite3ScratchMalloc( szScratch );
|
||||
if( apCell==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -6767,9 +6902,10 @@ static int balance_nonroot(
|
||||
|
||||
/*
|
||||
** The packing computed by the previous block is biased toward the siblings
|
||||
** on the left side. The left siblings are always nearly full, while the
|
||||
** right-most sibling might be nearly empty. This block of code attempts
|
||||
** to adjust the packing of siblings to get a better balance.
|
||||
** on the left side (siblings with smaller keys). The left siblings are
|
||||
** always nearly full, while the right-most sibling might be nearly empty.
|
||||
** The next block of code attempts to adjust the packing of siblings to
|
||||
** get a better balance.
|
||||
**
|
||||
** This adjustment is more than an optimization. The packing above might
|
||||
** be so out of balance as to be illegal. For example, the right-most
|
||||
@@ -6798,18 +6934,14 @@ static int balance_nonroot(
|
||||
szNew[i-1] = szLeft;
|
||||
}
|
||||
|
||||
/* Either we found one or more cells (cntnew[0])>0) or pPage is
|
||||
** a virtual root page. A virtual root page is when the real root
|
||||
** page is page 1 and we are the only child of that page.
|
||||
**
|
||||
** UPDATE: The assert() below is not necessarily true if the database
|
||||
** file is corrupt. The corruption will be detected and reported later
|
||||
** in this procedure so there is no need to act upon it now.
|
||||
/* Sanity check: For a non-corrupt database file one of the follwing
|
||||
** must be true:
|
||||
** (1) We found one or more cells (cntNew[0])>0), or
|
||||
** (2) pPage is a virtual root page. A virtual root page is when
|
||||
** the real root page is page 1 and we are the only child of
|
||||
** that page.
|
||||
*/
|
||||
#if 0
|
||||
assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) );
|
||||
#endif
|
||||
|
||||
assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB);
|
||||
TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n",
|
||||
apOld[0]->pgno, apOld[0]->nCell,
|
||||
nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0,
|
||||
@@ -6864,15 +6996,16 @@ static int balance_nonroot(
|
||||
** for large insertions and deletions.
|
||||
*/
|
||||
for(i=0; i<nNew; i++){
|
||||
aPgno[i] = apNew[i]->pgno;
|
||||
aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
|
||||
aPgFlags[i] = apNew[i]->pDbPage->flags;
|
||||
for(j=0; j<i; j++){
|
||||
if( aPgno[j]==aPgno[i] ){
|
||||
/* This branch is taken if the set of sibling pages somehow contains
|
||||
** duplicate entries. This can happen if the database is corrupt.
|
||||
** It would be simpler to detect this as part of the loop below, but
|
||||
** in order to avoid populating the pager cache with two separate
|
||||
** objects associated with the same page number. */
|
||||
** we do the detection here in order to avoid populating the pager
|
||||
** cache with two separate objects associated with the same
|
||||
** page number. */
|
||||
assert( CORRUPT_DB );
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
@@ -6881,12 +7014,11 @@ static int balance_nonroot(
|
||||
}
|
||||
for(i=0; i<nNew; i++){
|
||||
int iBest = 0; /* aPgno[] index of page number to use */
|
||||
Pgno pgno; /* Page number to use */
|
||||
for(j=1; j<nNew; j++){
|
||||
if( aPgno[j]<aPgno[iBest] ) iBest = j;
|
||||
if( aPgOrder[j]<aPgOrder[iBest] ) iBest = j;
|
||||
}
|
||||
pgno = aPgno[iBest];
|
||||
aPgno[iBest] = 0xffffffff;
|
||||
pgno = aPgOrder[iBest];
|
||||
aPgOrder[iBest] = 0xffffffff;
|
||||
if( iBest!=i ){
|
||||
if( iBest>i ){
|
||||
sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0);
|
||||
@@ -6957,7 +7089,7 @@ static int balance_nonroot(
|
||||
}
|
||||
|
||||
/* Cell pCell is destined for new sibling page pNew. Originally, it
|
||||
** was either part of sibling page iOld (possibly an overflow page),
|
||||
** was either part of sibling page iOld (possibly an overflow cell),
|
||||
** or else the divider cell to the left of sibling page iOld. So,
|
||||
** if sibling page iOld had the same page number as pNew, and if
|
||||
** pCell really was a part of sibling page iOld (not a divider or
|
||||
@@ -7029,26 +7161,43 @@ static int balance_nonroot(
|
||||
** is important, as this code needs to avoid disrupting any page from which
|
||||
** cells may still to be read. In practice, this means:
|
||||
**
|
||||
** 1) If cells are to be removed from the start of the page and shifted
|
||||
** to the left-hand sibling, it is not safe to update the page until
|
||||
** the left-hand sibling (apNew[i-1]) has already been updated.
|
||||
** (1) If cells are moving left (from apNew[iPg] to apNew[iPg-1])
|
||||
** then it is not safe to update page apNew[iPg] until after
|
||||
** the left-hand sibling apNew[iPg-1] has been updated.
|
||||
**
|
||||
** 2) If cells are to be removed from the end of the page and shifted
|
||||
** to the right-hand sibling, it is not safe to update the page until
|
||||
** the right-hand sibling (apNew[i+1]) has already been updated.
|
||||
** (2) If cells are moving right (from apNew[iPg] to apNew[iPg+1])
|
||||
** then it is not safe to update page apNew[iPg] until after
|
||||
** the right-hand sibling apNew[iPg+1] has been updated.
|
||||
**
|
||||
** If neither of the above apply, the page is safe to update.
|
||||
**
|
||||
** The iPg value in the following loop starts at nNew-1 goes down
|
||||
** to 0, then back up to nNew-1 again, thus making two passes over
|
||||
** the pages. On the initial downward pass, only condition (1) above
|
||||
** needs to be tested because (2) will always be true from the previous
|
||||
** step. On the upward pass, both conditions are always true, so the
|
||||
** upwards pass simply processes pages that were missed on the downward
|
||||
** pass.
|
||||
*/
|
||||
for(i=0; i<nNew*2; i++){
|
||||
int iPg = (i>=nNew ? i-nNew : nNew-1-i);
|
||||
if( abDone[iPg]==0
|
||||
&& (iPg==0 || cntOld[iPg-1]>=cntNew[iPg-1] || abDone[iPg-1])
|
||||
&& (cntNew[iPg]>=cntOld[iPg] || abDone[iPg+1])
|
||||
for(i=1-nNew; i<nNew; i++){
|
||||
int iPg = i<0 ? -i : i;
|
||||
assert( iPg>=0 && iPg<nNew );
|
||||
if( abDone[iPg] ) continue; /* Skip pages already processed */
|
||||
if( i>=0 /* On the upwards pass, or... */
|
||||
|| cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */
|
||||
){
|
||||
int iNew;
|
||||
int iOld;
|
||||
int nNewCell;
|
||||
|
||||
/* Verify condition (1): If cells are moving left, update iPg
|
||||
** only after iPg-1 has already been updated. */
|
||||
assert( iPg==0 || cntOld[iPg-1]>=cntNew[iPg-1] || abDone[iPg-1] );
|
||||
|
||||
/* Verify condition (2): If cells are moving right, update iPg
|
||||
** only after iPg+1 has already been updated. */
|
||||
assert( cntNew[iPg]>=cntOld[iPg] || abDone[iPg+1] );
|
||||
|
||||
if( iPg==0 ){
|
||||
iNew = iOld = 0;
|
||||
nNewCell = cntNew[0];
|
||||
@@ -7059,12 +7208,14 @@ static int balance_nonroot(
|
||||
}
|
||||
|
||||
editPage(apNew[iPg], iOld, iNew, nNewCell, apCell, szCell);
|
||||
abDone[iPg] = 1;
|
||||
abDone[iPg]++;
|
||||
apNew[iPg]->nFree = usableSpace-szNew[iPg];
|
||||
assert( apNew[iPg]->nOverflow==0 );
|
||||
assert( apNew[iPg]->nCell==nNewCell );
|
||||
}
|
||||
}
|
||||
|
||||
/* All pages have been processed exactly once */
|
||||
assert( memcmp(abDone, "\01\01\01\01\01", nNew)==0 );
|
||||
|
||||
assert( nOld>0 );
|
||||
@@ -7081,19 +7232,20 @@ static int balance_nonroot(
|
||||
** sets all pointer-map entries corresponding to database image pages
|
||||
** for which the pointer is stored within the content being copied.
|
||||
**
|
||||
** The second assert below verifies that the child page is defragmented
|
||||
** (it must be, as it was just reconstructed using assemblePage()). This
|
||||
** is important if the parent page happens to be page 1 of the database
|
||||
** image. */
|
||||
** It is critical that the child page be defragmented before being
|
||||
** copied into the parent, because if the parent is page 1 then it will
|
||||
** by smaller than the child due to the database header, and so all the
|
||||
** free space needs to be up front.
|
||||
*/
|
||||
assert( nNew==1 );
|
||||
rc = defragmentPage(apNew[0]);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( apNew[0]->nFree ==
|
||||
(get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2)
|
||||
);
|
||||
copyNodeContent(apNew[0], pParent, &rc);
|
||||
freePage(apNew[0], &rc);
|
||||
}
|
||||
testcase( rc!=SQLITE_OK );
|
||||
assert( apNew[0]->nFree ==
|
||||
(get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2)
|
||||
|| rc!=SQLITE_OK
|
||||
);
|
||||
copyNodeContent(apNew[0], pParent, &rc);
|
||||
freePage(apNew[0], &rc);
|
||||
}else if( ISAUTOVACUUM && !leafCorrection ){
|
||||
/* Fix the pointer map entries associated with the right-child of each
|
||||
** sibling page. All other pointer map entries have already been taken
|
||||
@@ -8508,8 +8660,14 @@ static int checkTreePage(
|
||||
assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
|
||||
memset(hit+contentOffset, 0, usableSize-contentOffset);
|
||||
memset(hit, 1, contentOffset);
|
||||
/* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
|
||||
** number of cells on the page. */
|
||||
nCell = get2byte(&data[hdr+3]);
|
||||
/* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
|
||||
** immediately follows the b-tree page header. */
|
||||
cellStart = hdr + 12 - 4*pPage->leaf;
|
||||
/* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
|
||||
** integer offsets to the cell contents. */
|
||||
for(i=0; i<nCell; i++){
|
||||
int pc = get2byte(&data[cellStart+i*2]);
|
||||
u32 size = 65536;
|
||||
@@ -8525,6 +8683,9 @@ static int checkTreePage(
|
||||
for(j=pc+size-1; j>=pc; j--) hit[j]++;
|
||||
}
|
||||
}
|
||||
/* EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
|
||||
** is the offset of the first freeblock, or zero if there are no
|
||||
** freeblocks on the page. */
|
||||
i = get2byte(&data[hdr+1]);
|
||||
while( i>0 ){
|
||||
int size, j;
|
||||
@@ -8532,7 +8693,13 @@ static int checkTreePage(
|
||||
size = get2byte(&data[i+2]);
|
||||
assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */
|
||||
for(j=i+size-1; j>=i; j--) hit[j]++;
|
||||
/* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a
|
||||
** big-endian integer which is the offset in the b-tree page of the next
|
||||
** freeblock in the chain, or zero if the freeblock is the last on the
|
||||
** chain. */
|
||||
j = get2byte(&data[i]);
|
||||
/* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
|
||||
** increasing offset. */
|
||||
assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */
|
||||
assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */
|
||||
i = j;
|
||||
@@ -8546,6 +8713,11 @@ static int checkTreePage(
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
|
||||
** is stored in the fifth field of the b-tree page header.
|
||||
** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
|
||||
** number of fragmented free bytes within the cell content area.
|
||||
*/
|
||||
if( cnt!=data[hdr+7] ){
|
||||
checkAppendMsg(pCheck,
|
||||
"Fragmentation of %d bytes reported as %d on page %d",
|
||||
@@ -8948,3 +9120,8 @@ void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){
|
||||
int sqlite3BtreeIsReadonly(Btree *p){
|
||||
return (p->pBt->btsFlags & BTS_READ_ONLY)!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of the header added to each page by this module.
|
||||
*/
|
||||
int sqlite3HeaderSizeBtree(void){ return sizeof(MemPage); }
|
||||
|
||||
+3
-2
@@ -83,7 +83,7 @@ int sqlite3BtreeBeginTrans(Btree*,int);
|
||||
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
||||
int sqlite3BtreeCommitPhaseTwo(Btree*, int);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*,int);
|
||||
int sqlite3BtreeRollback(Btree*,int,int);
|
||||
int sqlite3BtreeBeginStmt(Btree*,int);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
@@ -116,7 +116,7 @@ int sqlite3BtreeIncrVacuum(Btree *);
|
||||
int sqlite3BtreeDropTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTableOfCursor(BtCursor*);
|
||||
void sqlite3BtreeTripAllCursors(Btree*, int);
|
||||
int sqlite3BtreeTripAllCursors(Btree*, int, int);
|
||||
|
||||
void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
|
||||
int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
|
||||
@@ -196,6 +196,7 @@ void sqlite3BtreeClearCursor(BtCursor *);
|
||||
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
|
||||
void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask);
|
||||
int sqlite3BtreeIsReadonly(Btree *pBt);
|
||||
int sqlite3HeaderSizeBtree(void);
|
||||
|
||||
#ifndef NDEBUG
|
||||
int sqlite3BtreeCursorIsValid(BtCursor*);
|
||||
|
||||
+8
-2
@@ -489,6 +489,11 @@ struct CellInfo {
|
||||
**
|
||||
** Fields in this structure are accessed under the BtShared.mutex
|
||||
** found at self->pBt->mutex.
|
||||
**
|
||||
** skipNext meaning:
|
||||
** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op.
|
||||
** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op.
|
||||
** eState==FAULT: Cursor fault with skipNext as error code.
|
||||
*/
|
||||
struct BtCursor {
|
||||
Btree *pBtree; /* The Btree to which this cursor belongs */
|
||||
@@ -501,7 +506,8 @@ struct BtCursor {
|
||||
void *pKey; /* Saved key that was cursor last known position */
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
int nOvflAlloc; /* Allocated size of aOverflow[] array */
|
||||
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
|
||||
int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
|
||||
** Error code if eState==CURSOR_FAULT */
|
||||
u8 curFlags; /* zero or more BTCF_* flags defined below */
|
||||
u8 eState; /* One of the CURSOR_XXX constants (see below) */
|
||||
u8 hints; /* As configured by CursorSetHints() */
|
||||
@@ -547,7 +553,7 @@ struct BtCursor {
|
||||
** on a different connection that shares the BtShared cache with this
|
||||
** cursor. The error has left the cache in an inconsistent state.
|
||||
** Do nothing else with this cursor. Any attempt to use the cursor
|
||||
** should return the error code stored in BtCursor.skip
|
||||
** should return the error code stored in BtCursor.skipNext
|
||||
*/
|
||||
#define CURSOR_INVALID 0
|
||||
#define CURSOR_VALID 1
|
||||
|
||||
+5
-1
@@ -307,7 +307,11 @@ int sqlite3UserAuthTable(const char *zTable){
|
||||
Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
Table *p = 0;
|
||||
int i;
|
||||
assert( zName!=0 );
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zName==0 ) return 0;
|
||||
#endif
|
||||
|
||||
/* All mutexes are required for schema access. Make sure we hold them. */
|
||||
assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) );
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
|
||||
+30
-2
@@ -102,7 +102,7 @@ CollSeq *sqlite3GetCollSeq(
|
||||
if( p && !p->xCmp && synthCollSeq(db, p) ){
|
||||
p = 0;
|
||||
}
|
||||
assert( !p || p->xCmp );
|
||||
assert( p==0 || (sqlite3ValidCollSeq(p) && p->xCmp!=0) );
|
||||
if( p==0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
|
||||
}
|
||||
@@ -186,6 +186,31 @@ static CollSeq *findCollSeqEntry(
|
||||
return pColl;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** The following routine does sanity checking on a CollSeq object and
|
||||
** returns 1 if everything looks ok and 0 if the CollSeq object appears
|
||||
** to be corrupt. This routine is used only inside of assert() statements.
|
||||
*/
|
||||
int sqlite3ValidCollSeq(const CollSeq *p){
|
||||
/* The CollSeq must be one of a triple and the zName field must
|
||||
** point to the first byte after that triple
|
||||
*/
|
||||
int n = (int)(p->zName - (char*)p)/sizeof(CollSeq);
|
||||
if( n<=0 || n>3 ) return 0;
|
||||
|
||||
/* Check for valid enc values */
|
||||
if( p->enc==SQLITE_UTF8 ) return 1;
|
||||
if( p->enc==SQLITE_UTF16LE ) return 1;
|
||||
if( p->enc==SQLITE_UTF16BE ) return 1;
|
||||
if( p->enc==(SQLITE_UTF16LE|SQLITE_UTF16_ALIGNED) ) return 1;
|
||||
if( p->enc==(SQLITE_UTF16BE|SQLITE_UTF16_ALIGNED) ) return 1;
|
||||
|
||||
/* Otherwise, malformed */
|
||||
return 0;
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** Parameter zName points to a UTF-8 encoded string nName bytes long.
|
||||
** Return the CollSeq* pointer for the collation sequence named zName
|
||||
@@ -215,7 +240,10 @@ CollSeq *sqlite3FindCollSeq(
|
||||
}
|
||||
assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
||||
assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
|
||||
if( pColl ) pColl += enc-1;
|
||||
if( pColl ){
|
||||
pColl += enc-1;
|
||||
assert( sqlite3ValidCollSeq(pColl) );
|
||||
}
|
||||
return pColl;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,13 @@ int sqlite3_complete(const char *zSql){
|
||||
u8 state = 0; /* Current state, using numbers defined in header comment */
|
||||
u8 token; /* Value of the next token */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( zSql==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* A complex statement machine used to detect the end of a CREATE TRIGGER
|
||||
** statement. This is the normal case.
|
||||
|
||||
+10
@@ -63,6 +63,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_DISABLE_LFS
|
||||
"DISABLE_LFS",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
"ENABLE_API_ARMOR",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
"ENABLE_ATOMIC_WRITE",
|
||||
#endif
|
||||
@@ -388,6 +391,13 @@ static const char * const azCompileOpt[] = {
|
||||
*/
|
||||
int sqlite3_compileoption_used(const char *zOptName){
|
||||
int i, n;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( zOptName==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if( sqlite3StrNICmp(zOptName, "SQLITE_", 7)==0 ) zOptName += 7;
|
||||
n = sqlite3Strlen30(zOptName);
|
||||
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@
|
||||
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** SQLite processes all times and dates as Julian Day numbers. The
|
||||
** SQLite processes all times and dates as julian day numbers. The
|
||||
** dates and times are stored as the number of days since noon
|
||||
** in Greenwich on November 24, 4714 B.C. according to the Gregorian
|
||||
** calendar system.
|
||||
@@ -31,7 +31,7 @@
|
||||
**
|
||||
** The Gregorian calendar system is used for all dates and times,
|
||||
** even those that predate the Gregorian calendar. Historians usually
|
||||
** use the Julian calendar for dates prior to 1582-10-15 and for some
|
||||
** use the julian calendar for dates prior to 1582-10-15 and for some
|
||||
** dates afterwards, depending on locale. Beware of this difference.
|
||||
**
|
||||
** The conversion algorithms are implemented based on descriptions
|
||||
@@ -304,7 +304,7 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to parse the given string into a Julian Day Number. Return
|
||||
** Attempt to parse the given string into a julian day number. Return
|
||||
** the number of errors.
|
||||
**
|
||||
** The following are acceptable forms for the input string:
|
||||
@@ -875,7 +875,7 @@ static void dateFunc(
|
||||
** %f ** fractional seconds SS.SSS
|
||||
** %H hour 00-24
|
||||
** %j day of year 000-366
|
||||
** %J ** Julian day number
|
||||
** %J ** julian day number
|
||||
** %m month 01-12
|
||||
** %M minute 00-59
|
||||
** %s seconds since 1970-01-01
|
||||
|
||||
+1
-1
@@ -481,7 +481,7 @@ void sqlite3DeleteFrom(
|
||||
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
|
||||
assert( !IsVirtual(pTab) );
|
||||
if( aToOpen[iDataCur-iTabCur] ){
|
||||
assert( pPk!=0 );
|
||||
assert( pPk!=0 || pTab->pSelect!=0 );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
+6
-3
@@ -1414,7 +1414,8 @@ int sqlite3ExprCanBeNull(const Expr *p){
|
||||
return 0;
|
||||
case TK_COLUMN:
|
||||
assert( p->pTab!=0 );
|
||||
return p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0;
|
||||
return ExprHasProperty(p, EP_CanBeNull) ||
|
||||
(p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0);
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
@@ -1857,7 +1858,6 @@ int sqlite3CodeSubselect(
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
pSelect->iLimit = 0;
|
||||
testcase( pSelect->selFlags & SF_Distinct );
|
||||
pSelect->selFlags &= ~SF_Distinct;
|
||||
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
||||
if( sqlite3Select(pParse, pSelect, &dest) ){
|
||||
sqlite3KeyInfoUnref(pKeyInfo);
|
||||
@@ -3004,7 +3004,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
/* If the column has REAL affinity, it may currently be stored as an
|
||||
** integer. Use OP_RealAffinity to make sure it is really real. */
|
||||
** integer. Use OP_RealAffinity to make sure it is really real.
|
||||
**
|
||||
** EVIDENCE-OF: R-60985-57662 SQLite will convert the value back to
|
||||
** floating point when extracting it from the record. */
|
||||
if( pExpr->iColumn>=0
|
||||
&& pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL
|
||||
){
|
||||
|
||||
+2
-2
@@ -157,8 +157,8 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
default: {
|
||||
/* Because sqlite3_value_double() returns 0.0 if the argument is not
|
||||
** something that can be converted into a number, we have:
|
||||
** IMP: R-57326-31541 Abs(X) return 0.0 if X is a string or blob that
|
||||
** cannot be converted to a numeric value.
|
||||
** IMP: R-01992-00519 Abs(X) returns 0.0 if X is a string or blob
|
||||
** that cannot be converted to a numeric value.
|
||||
*/
|
||||
double rVal = sqlite3_value_double(argv[0]);
|
||||
if( rVal<0 ) rVal = -rVal;
|
||||
|
||||
+10
-2
@@ -135,11 +135,19 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
**
|
||||
** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled
|
||||
** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options.
|
||||
**
|
||||
** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally
|
||||
** disabled. The default value may be changed by compiling with the
|
||||
** SQLITE_USE_URI symbol defined.
|
||||
*/
|
||||
#ifndef SQLITE_USE_URI
|
||||
# define SQLITE_USE_URI 0
|
||||
#endif
|
||||
|
||||
/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the
|
||||
** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if
|
||||
** that compile-time option is omitted.
|
||||
*/
|
||||
#ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN
|
||||
# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1
|
||||
#endif
|
||||
@@ -229,8 +237,8 @@ const Token sqlite3IntTokens[] = {
|
||||
**
|
||||
** IMPORTANT: Changing the pending byte to any value other than
|
||||
** 0x40000000 results in an incompatible database file format!
|
||||
** Changing the pending byte during operating results in undefined
|
||||
** and dileterious behavior.
|
||||
** Changing the pending byte during operation will result in undefined
|
||||
** and incorrect behavior.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_WSD
|
||||
int sqlite3PendingByte = 0x40000000;
|
||||
|
||||
+292
-40
@@ -329,15 +329,17 @@ int sqlite3_config(int op, ...){
|
||||
switch( op ){
|
||||
|
||||
/* Mutex configuration options are only available in a threadsafe
|
||||
** compile.
|
||||
** compile.
|
||||
*/
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-54466-46756 */
|
||||
case SQLITE_CONFIG_SINGLETHREAD: {
|
||||
/* Disable all mutexing */
|
||||
sqlite3GlobalConfig.bCoreMutex = 0;
|
||||
sqlite3GlobalConfig.bFullMutex = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-20520-54086 */
|
||||
case SQLITE_CONFIG_MULTITHREAD: {
|
||||
/* Disable mutexing of database connections */
|
||||
/* Enable mutexing of core data structures */
|
||||
@@ -345,17 +347,23 @@ int sqlite3_config(int op, ...){
|
||||
sqlite3GlobalConfig.bFullMutex = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-59593-21810 */
|
||||
case SQLITE_CONFIG_SERIALIZED: {
|
||||
/* Enable all mutexing */
|
||||
sqlite3GlobalConfig.bCoreMutex = 1;
|
||||
sqlite3GlobalConfig.bFullMutex = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-63666-48755 */
|
||||
case SQLITE_CONFIG_MUTEX: {
|
||||
/* Specify an alternative mutex implementation */
|
||||
sqlite3GlobalConfig.mutex = *va_arg(ap, sqlite3_mutex_methods*);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-14450-37597 */
|
||||
case SQLITE_CONFIG_GETMUTEX: {
|
||||
/* Retrieve the current mutex implementation */
|
||||
*va_arg(ap, sqlite3_mutex_methods*) = sqlite3GlobalConfig.mutex;
|
||||
@@ -363,37 +371,61 @@ int sqlite3_config(int op, ...){
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
case SQLITE_CONFIG_MALLOC: {
|
||||
/* Specify an alternative malloc implementation */
|
||||
/* EVIDENCE-OF: R-55594-21030 The SQLITE_CONFIG_MALLOC option takes a
|
||||
** single argument which is a pointer to an instance of the
|
||||
** sqlite3_mem_methods structure. The argument specifies alternative
|
||||
** low-level memory allocation routines to be used in place of the memory
|
||||
** allocation routines built into SQLite. */
|
||||
sqlite3GlobalConfig.m = *va_arg(ap, sqlite3_mem_methods*);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_GETMALLOC: {
|
||||
/* Retrieve the current malloc() implementation */
|
||||
/* EVIDENCE-OF: R-51213-46414 The SQLITE_CONFIG_GETMALLOC option takes a
|
||||
** single argument which is a pointer to an instance of the
|
||||
** sqlite3_mem_methods structure. The sqlite3_mem_methods structure is
|
||||
** filled with the currently defined memory allocation routines. */
|
||||
if( sqlite3GlobalConfig.m.xMalloc==0 ) sqlite3MemSetDefault();
|
||||
*va_arg(ap, sqlite3_mem_methods*) = sqlite3GlobalConfig.m;
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_MEMSTATUS: {
|
||||
/* Enable or disable the malloc status collection */
|
||||
/* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes
|
||||
** single argument of type int, interpreted as a boolean, which enables
|
||||
** or disables the collection of memory allocation statistics. */
|
||||
sqlite3GlobalConfig.bMemstat = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_SCRATCH: {
|
||||
/* Designate a buffer for scratch memory space */
|
||||
/* EVIDENCE-OF: R-08404-60887 There are three arguments to
|
||||
** SQLITE_CONFIG_SCRATCH: A pointer an 8-byte aligned memory buffer from
|
||||
** which the scratch allocations will be drawn, the size of each scratch
|
||||
** allocation (sz), and the maximum number of scratch allocations (N). */
|
||||
sqlite3GlobalConfig.pScratch = va_arg(ap, void*);
|
||||
sqlite3GlobalConfig.szScratch = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.nScratch = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_PAGECACHE: {
|
||||
/* Designate a buffer for page cache memory space */
|
||||
/* EVIDENCE-OF: R-31408-40510 There are three arguments to
|
||||
** SQLITE_CONFIG_PAGECACHE: A pointer to 8-byte aligned memory, the size
|
||||
** of each page buffer (sz), and the number of pages (N). */
|
||||
sqlite3GlobalConfig.pPage = va_arg(ap, void*);
|
||||
sqlite3GlobalConfig.szPage = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.nPage = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_PCACHE_HDRSZ: {
|
||||
/* EVIDENCE-OF: R-39100-27317 The SQLITE_CONFIG_PCACHE_HDRSZ option takes
|
||||
** a single parameter which is a pointer to an integer and writes into
|
||||
** that integer the number of extra bytes per page required for each page
|
||||
** in SQLITE_CONFIG_PAGECACHE. */
|
||||
*va_arg(ap, int*) =
|
||||
sqlite3HeaderSizeBtree() +
|
||||
sqlite3HeaderSizePcache() +
|
||||
sqlite3HeaderSizePcache1();
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_PCACHE: {
|
||||
/* no-op */
|
||||
@@ -406,11 +438,18 @@ int sqlite3_config(int op, ...){
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_PCACHE2: {
|
||||
/* Specify an alternative page cache implementation */
|
||||
/* EVIDENCE-OF: R-63325-48378 The SQLITE_CONFIG_PCACHE2 option takes a
|
||||
** single argument which is a pointer to an sqlite3_pcache_methods2
|
||||
** object. This object specifies the interface to a custom page cache
|
||||
** implementation. */
|
||||
sqlite3GlobalConfig.pcache2 = *va_arg(ap, sqlite3_pcache_methods2*);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_GETPCACHE2: {
|
||||
/* EVIDENCE-OF: R-22035-46182 The SQLITE_CONFIG_GETPCACHE2 option takes a
|
||||
** single argument which is a pointer to an sqlite3_pcache_methods2
|
||||
** object. SQLite copies of the current page cache implementation into
|
||||
** that object. */
|
||||
if( sqlite3GlobalConfig.pcache2.xInit==0 ){
|
||||
sqlite3PCacheSetDefault();
|
||||
}
|
||||
@@ -418,9 +457,14 @@ int sqlite3_config(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* EVIDENCE-OF: R-06626-12911 The SQLITE_CONFIG_HEAP option is only
|
||||
** available if SQLite is compiled with either SQLITE_ENABLE_MEMSYS3 or
|
||||
** SQLITE_ENABLE_MEMSYS5 and returns SQLITE_ERROR if invoked otherwise. */
|
||||
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
||||
case SQLITE_CONFIG_HEAP: {
|
||||
/* Designate a buffer for heap memory space */
|
||||
/* EVIDENCE-OF: R-19854-42126 There are three arguments to
|
||||
** SQLITE_CONFIG_HEAP: An 8-byte aligned pointer to the memory, the
|
||||
** number of bytes in the memory buffer, and the minimum allocation size. */
|
||||
sqlite3GlobalConfig.pHeap = va_arg(ap, void*);
|
||||
sqlite3GlobalConfig.nHeap = va_arg(ap, int);
|
||||
sqlite3GlobalConfig.mnReq = va_arg(ap, int);
|
||||
@@ -433,17 +477,19 @@ int sqlite3_config(int op, ...){
|
||||
}
|
||||
|
||||
if( sqlite3GlobalConfig.pHeap==0 ){
|
||||
/* If the heap pointer is NULL, then restore the malloc implementation
|
||||
** back to NULL pointers too. This will cause the malloc to go
|
||||
** back to its default implementation when sqlite3_initialize() is
|
||||
** run.
|
||||
/* EVIDENCE-OF: R-49920-60189 If the first pointer (the memory pointer)
|
||||
** is NULL, then SQLite reverts to using its default memory allocator
|
||||
** (the system malloc() implementation), undoing any prior invocation of
|
||||
** SQLITE_CONFIG_MALLOC.
|
||||
**
|
||||
** Setting sqlite3GlobalConfig.m to all zeros will cause malloc to
|
||||
** revert to its default implementation when sqlite3_initialize() is run
|
||||
*/
|
||||
memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m));
|
||||
}else{
|
||||
/* The heap pointer is not NULL, then install one of the
|
||||
** mem5.c/mem3.c methods. The enclosing #if guarantees at
|
||||
** least one of these methods is currently enabled.
|
||||
*/
|
||||
/* EVIDENCE-OF: R-61006-08918 If the memory pointer is not NULL then the
|
||||
** alternative memory allocator is engaged to handle all of SQLites
|
||||
** memory allocation needs. */
|
||||
#ifdef SQLITE_ENABLE_MEMSYS3
|
||||
sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3();
|
||||
#endif
|
||||
@@ -482,11 +528,19 @@ int sqlite3_config(int op, ...){
|
||||
** sqlite3_config(SQLITE_CONFIG_URI,0) configuration calls.
|
||||
*/
|
||||
case SQLITE_CONFIG_URI: {
|
||||
/* EVIDENCE-OF: R-25451-61125 The SQLITE_CONFIG_URI option takes a single
|
||||
** argument of type int. If non-zero, then URI handling is globally
|
||||
** enabled. If the parameter is zero, then URI handling is globally
|
||||
** disabled. */
|
||||
sqlite3GlobalConfig.bOpenUri = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_COVERING_INDEX_SCAN: {
|
||||
/* EVIDENCE-OF: R-36592-02772 The SQLITE_CONFIG_COVERING_INDEX_SCAN
|
||||
** option takes a single integer argument which is interpreted as a
|
||||
** boolean in order to enable or disable the use of covering indices for
|
||||
** full table scans in the query optimizer. */
|
||||
sqlite3GlobalConfig.bUseCis = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
@@ -501,20 +555,33 @@ int sqlite3_config(int op, ...){
|
||||
#endif
|
||||
|
||||
case SQLITE_CONFIG_MMAP_SIZE: {
|
||||
/* EVIDENCE-OF: R-58063-38258 SQLITE_CONFIG_MMAP_SIZE takes two 64-bit
|
||||
** integer (sqlite3_int64) values that are the default mmap size limit
|
||||
** (the default setting for PRAGMA mmap_size) and the maximum allowed
|
||||
** mmap size limit. */
|
||||
sqlite3_int64 szMmap = va_arg(ap, sqlite3_int64);
|
||||
sqlite3_int64 mxMmap = va_arg(ap, sqlite3_int64);
|
||||
if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ){
|
||||
mxMmap = SQLITE_MAX_MMAP_SIZE;
|
||||
}
|
||||
sqlite3GlobalConfig.mxMmap = mxMmap;
|
||||
/* EVIDENCE-OF: R-53367-43190 If either argument to this option is
|
||||
** negative, then that argument is changed to its compile-time default.
|
||||
**
|
||||
** EVIDENCE-OF: R-34993-45031 The maximum allowed mmap size will be
|
||||
** silently truncated if necessary so that it does not exceed the
|
||||
** compile-time maximum mmap size set by the SQLITE_MAX_MMAP_SIZE
|
||||
** compile-time option.
|
||||
*/
|
||||
if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ) mxMmap = SQLITE_MAX_MMAP_SIZE;
|
||||
if( szMmap<0 ) szMmap = SQLITE_DEFAULT_MMAP_SIZE;
|
||||
if( szMmap>mxMmap) szMmap = mxMmap;
|
||||
sqlite3GlobalConfig.mxMmap = mxMmap;
|
||||
sqlite3GlobalConfig.szMmap = szMmap;
|
||||
break;
|
||||
}
|
||||
|
||||
#if SQLITE_OS_WIN && defined(SQLITE_WIN32_MALLOC)
|
||||
#if SQLITE_OS_WIN && defined(SQLITE_WIN32_MALLOC) /* IMP: R-04780-55815 */
|
||||
case SQLITE_CONFIG_WIN32_HEAPSIZE: {
|
||||
/* EVIDENCE-OF: R-34926-03360 SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit
|
||||
** unsigned integer value that specifies the maximum size of the created
|
||||
** heap. */
|
||||
sqlite3GlobalConfig.nHeap = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
@@ -598,6 +665,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
||||
** Return the mutex associated with a database connection.
|
||||
*/
|
||||
sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return db->mutex;
|
||||
}
|
||||
|
||||
@@ -607,6 +680,10 @@ sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){
|
||||
*/
|
||||
int sqlite3_db_release_memory(sqlite3 *db){
|
||||
int i;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
@@ -696,13 +773,20 @@ static int binCollFunc(
|
||||
){
|
||||
int rc, n;
|
||||
n = nKey1<nKey2 ? nKey1 : nKey2;
|
||||
/* EVIDENCE-OF: R-65033-28449 The built-in BINARY collation compares
|
||||
** strings byte by byte using the memcmp() function from the standard C
|
||||
** library. */
|
||||
rc = memcmp(pKey1, pKey2, n);
|
||||
if( rc==0 ){
|
||||
if( padFlag
|
||||
&& allSpaces(((char*)pKey1)+n, nKey1-n)
|
||||
&& allSpaces(((char*)pKey2)+n, nKey2-n)
|
||||
){
|
||||
/* Leave rc unchanged at 0 */
|
||||
/* EVIDENCE-OF: R-31624-24737 RTRIM is like BINARY except that extra
|
||||
** spaces at the end of either string do not change the result. In other
|
||||
** words, strings will compare equal to one another as long as they
|
||||
** differ only in the number of spaces at the end.
|
||||
*/
|
||||
}else{
|
||||
rc = nKey1 - nKey2;
|
||||
}
|
||||
@@ -737,6 +821,12 @@ static int nocaseCollatingFunc(
|
||||
** Return the ROWID of the most recent insert
|
||||
*/
|
||||
sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return db->lastRowid;
|
||||
}
|
||||
|
||||
@@ -744,6 +834,12 @@ sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){
|
||||
** Return the number of changes in the most recent call to sqlite3_exec().
|
||||
*/
|
||||
int sqlite3_changes(sqlite3 *db){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return db->nChange;
|
||||
}
|
||||
|
||||
@@ -751,6 +847,12 @@ int sqlite3_changes(sqlite3 *db){
|
||||
** Return the number of changes since the database handle was opened.
|
||||
*/
|
||||
int sqlite3_total_changes(sqlite3 *db){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return db->nTotalChange;
|
||||
}
|
||||
|
||||
@@ -932,7 +1034,13 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
if( pDb->pBt ){
|
||||
sqlite3BtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
if( j!=1 ){
|
||||
if( j!=1 && pDb->pSchema ){
|
||||
/* Must clear the KeyInfo cache. See ticket [e4a18565a36884b00edf] */
|
||||
for(i=sqliteHashFirst(&pDb->pSchema->idxHash); i; i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
sqlite3KeyInfoUnref(pIdx->pKeyInfo);
|
||||
pIdx->pKeyInfo = 0;
|
||||
}
|
||||
pDb->pSchema = 0;
|
||||
}
|
||||
}
|
||||
@@ -1016,13 +1124,15 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
|
||||
|
||||
/*
|
||||
** Rollback all database files. If tripCode is not SQLITE_OK, then
|
||||
** any open cursors are invalidated ("tripped" - as in "tripping a circuit
|
||||
** any write cursors are invalidated ("tripped" - as in "tripping a circuit
|
||||
** breaker") and made to return tripCode if there are any further
|
||||
** attempts to use that cursor.
|
||||
** attempts to use that cursor. Read cursors remain open and valid
|
||||
** but are "saved" in case the table pages are moved around.
|
||||
*/
|
||||
void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
int i;
|
||||
int inTrans = 0;
|
||||
int schemaChange;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
sqlite3BeginBenignMalloc();
|
||||
|
||||
@@ -1033,6 +1143,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;
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *p = db->aDb[i].pBt;
|
||||
@@ -1040,7 +1151,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
|
||||
if( sqlite3BtreeIsInTrans(p) ){
|
||||
inTrans = 1;
|
||||
}
|
||||
sqlite3BtreeRollback(p, tripCode);
|
||||
sqlite3BtreeRollback(p, tripCode, !schemaChange);
|
||||
}
|
||||
}
|
||||
sqlite3VtabRollback(db);
|
||||
@@ -1296,6 +1407,9 @@ int sqlite3_busy_handler(
|
||||
int (*xBusy)(void*,int),
|
||||
void *pArg
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->busyHandler.xFunc = xBusy;
|
||||
db->busyHandler.pArg = pArg;
|
||||
@@ -1317,6 +1431,12 @@ void sqlite3_progress_handler(
|
||||
int (*xProgress)(void*),
|
||||
void *pArg
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( nOps>0 ){
|
||||
db->xProgress = xProgress;
|
||||
@@ -1337,6 +1457,9 @@ void sqlite3_progress_handler(
|
||||
** specified number of milliseconds before returning 0.
|
||||
*/
|
||||
int sqlite3_busy_timeout(sqlite3 *db, int ms){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
if( ms>0 ){
|
||||
sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);
|
||||
db->busyTimeout = ms;
|
||||
@@ -1350,6 +1473,12 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){
|
||||
** Cause any pending operation to stop at its earliest opportunity.
|
||||
*/
|
||||
void sqlite3_interrupt(sqlite3 *db){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
db->u1.isInterrupted = 1;
|
||||
}
|
||||
|
||||
@@ -1487,6 +1616,12 @@ int sqlite3_create_function_v2(
|
||||
){
|
||||
int rc = SQLITE_ERROR;
|
||||
FuncDestructor *pArg = 0;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( xDestroy ){
|
||||
pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
|
||||
@@ -1523,6 +1658,10 @@ int sqlite3_create_function16(
|
||||
){
|
||||
int rc;
|
||||
char *zFunc8;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zFunctionName==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
|
||||
@@ -1554,6 +1693,12 @@ int sqlite3_overload_function(
|
||||
){
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zName==0 || nArg<-2 ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
|
||||
rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
|
||||
@@ -1575,6 +1720,13 @@ int sqlite3_overload_function(
|
||||
*/
|
||||
void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
|
||||
void *pOld;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pTraceArg;
|
||||
db->xTrace = xTrace;
|
||||
@@ -1596,6 +1748,13 @@ void *sqlite3_profile(
|
||||
void *pArg
|
||||
){
|
||||
void *pOld;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pProfileArg;
|
||||
db->xProfile = xProfile;
|
||||
@@ -1616,6 +1775,13 @@ void *sqlite3_commit_hook(
|
||||
void *pArg /* Argument to the function */
|
||||
){
|
||||
void *pOld;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pCommitArg;
|
||||
db->xCommitCallback = xCallback;
|
||||
@@ -1634,6 +1800,13 @@ void *sqlite3_update_hook(
|
||||
void *pArg /* Argument to the function */
|
||||
){
|
||||
void *pRet;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pRet = db->pUpdateArg;
|
||||
db->xUpdateCallback = xCallback;
|
||||
@@ -1652,6 +1825,13 @@ void *sqlite3_rollback_hook(
|
||||
void *pArg /* Argument to the function */
|
||||
){
|
||||
void *pRet;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pRet = db->pRollbackArg;
|
||||
db->xRollbackCallback = xCallback;
|
||||
@@ -1698,6 +1878,9 @@ int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){
|
||||
UNUSED_PARAMETER(db);
|
||||
UNUSED_PARAMETER(nFrame);
|
||||
#else
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
if( nFrame>0 ){
|
||||
sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame));
|
||||
}else{
|
||||
@@ -1718,6 +1901,12 @@ void *sqlite3_wal_hook(
|
||||
){
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
void *pRet;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pRet = db->pWalArg;
|
||||
db->xWalCallback = xCallback;
|
||||
@@ -1745,14 +1934,21 @@ int sqlite3_wal_checkpoint_v2(
|
||||
int rc; /* Return code */
|
||||
int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
|
||||
/* Initialize the output variables to -1 in case an error occurs. */
|
||||
if( pnLog ) *pnLog = -1;
|
||||
if( pnCkpt ) *pnCkpt = -1;
|
||||
|
||||
assert( SQLITE_CHECKPOINT_FULL>SQLITE_CHECKPOINT_PASSIVE );
|
||||
assert( SQLITE_CHECKPOINT_FULL<SQLITE_CHECKPOINT_RESTART );
|
||||
assert( SQLITE_CHECKPOINT_PASSIVE+2==SQLITE_CHECKPOINT_RESTART );
|
||||
if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_RESTART ){
|
||||
assert( SQLITE_CHECKPOINT_PASSIVE==0 );
|
||||
assert( SQLITE_CHECKPOINT_FULL==1 );
|
||||
assert( SQLITE_CHECKPOINT_RESTART==2 );
|
||||
assert( SQLITE_CHECKPOINT_TRUNCATE==3 );
|
||||
if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
|
||||
/* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
|
||||
** mode: */
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
@@ -1780,7 +1976,9 @@ int sqlite3_wal_checkpoint_v2(
|
||||
** checkpointed.
|
||||
*/
|
||||
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
|
||||
return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0);
|
||||
/* EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is equivalent to
|
||||
** sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0). */
|
||||
return sqlite3_wal_checkpoint_v2(db,zDb,SQLITE_CHECKPOINT_PASSIVE,0,0);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
@@ -2141,6 +2339,12 @@ static const int aHardLimit[] = {
|
||||
int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
int oldLimit;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* EVIDENCE-OF: R-30189-54097 For each limit category SQLITE_LIMIT_NAME
|
||||
** there is a hard upper bound set at compile-time by a C preprocessor
|
||||
@@ -2217,7 +2421,8 @@ int sqlite3ParseUri(
|
||||
|
||||
assert( *pzErrMsg==0 );
|
||||
|
||||
if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri)
|
||||
if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */
|
||||
|| sqlite3GlobalConfig.bOpenUri) /* IMP: R-51689-46548 */
|
||||
&& nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */
|
||||
){
|
||||
char *zOpt;
|
||||
@@ -2426,6 +2631,9 @@ static int openDatabase(
|
||||
char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
|
||||
char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
*ppDb = 0;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
@@ -2540,20 +2748,24 @@ static int openDatabase(
|
||||
/* Add the default collation sequence BINARY. BINARY works for both UTF-8
|
||||
** and UTF-16, so add a version for each to avoid any unnecessary
|
||||
** conversions. The only error that can occur here is a malloc() failure.
|
||||
**
|
||||
** EVIDENCE-OF: R-52786-44878 SQLite defines three built-in collating
|
||||
** functions:
|
||||
*/
|
||||
createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0);
|
||||
createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0);
|
||||
createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0);
|
||||
createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
|
||||
createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
|
||||
if( db->mallocFailed ){
|
||||
goto opendb_out;
|
||||
}
|
||||
/* EVIDENCE-OF: R-08308-17224 The default collating function for all
|
||||
** strings is BINARY.
|
||||
*/
|
||||
db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0);
|
||||
assert( db->pDfltColl!=0 );
|
||||
|
||||
/* Also add a UTF-8 case-insensitive collation sequence. */
|
||||
createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
|
||||
|
||||
/* Parse the filename/URI argument. */
|
||||
db->openFlags = flags;
|
||||
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
|
||||
@@ -2715,13 +2927,15 @@ int sqlite3_open16(
|
||||
sqlite3_value *pVal;
|
||||
int rc;
|
||||
|
||||
assert( zFilename );
|
||||
assert( ppDb );
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
*ppDb = 0;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
rc = sqlite3_initialize();
|
||||
if( rc ) return rc;
|
||||
#endif
|
||||
if( zFilename==0 ) zFilename = "\000\000";
|
||||
pVal = sqlite3ValueNew(0);
|
||||
sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC);
|
||||
zFilename8 = sqlite3ValueText(pVal, SQLITE_UTF8);
|
||||
@@ -2766,6 +2980,10 @@ int sqlite3_create_collation_v2(
|
||||
void(*xDel)(void*)
|
||||
){
|
||||
int rc;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xDel);
|
||||
@@ -2787,6 +3005,10 @@ int sqlite3_create_collation16(
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
char *zName8;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
zName8 = sqlite3Utf16to8(db, zName, -1, SQLITE_UTF16NATIVE);
|
||||
@@ -2809,6 +3031,9 @@ int sqlite3_collation_needed(
|
||||
void *pCollNeededArg,
|
||||
void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*)
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->xCollNeeded = xCollNeeded;
|
||||
db->xCollNeeded16 = 0;
|
||||
@@ -2827,6 +3052,9 @@ int sqlite3_collation_needed16(
|
||||
void *pCollNeededArg,
|
||||
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*)
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->xCollNeeded = 0;
|
||||
db->xCollNeeded16 = xCollNeeded16;
|
||||
@@ -2853,6 +3081,12 @@ int sqlite3_global_recover(void){
|
||||
** by the next COMMIT or ROLLBACK.
|
||||
*/
|
||||
int sqlite3_get_autocommit(sqlite3 *db){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return db->autoCommit;
|
||||
}
|
||||
|
||||
@@ -3035,6 +3269,9 @@ int sqlite3_sleep(int ms){
|
||||
** Enable or disable the extended result codes.
|
||||
*/
|
||||
int sqlite3_extended_result_codes(sqlite3 *db, int onoff){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
db->errMask = onoff ? 0xffffffff : 0xff;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
@@ -3048,6 +3285,9 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
|
||||
int rc = SQLITE_ERROR;
|
||||
Btree *pBtree;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pBtree = sqlite3DbNameToBtree(db, zDbName);
|
||||
if( pBtree ){
|
||||
@@ -3390,7 +3630,7 @@ int sqlite3_test_control(int op, ...){
|
||||
** returns a NULL pointer.
|
||||
*/
|
||||
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
|
||||
if( zFilename==0 ) return 0;
|
||||
if( zFilename==0 || zParam==0 ) return 0;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] ){
|
||||
int x = strcmp(zFilename, zParam);
|
||||
@@ -3446,6 +3686,12 @@ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
|
||||
** connection.
|
||||
*/
|
||||
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
return pBt ? sqlite3BtreeGetFilename(pBt) : 0;
|
||||
}
|
||||
@@ -3455,6 +3701,12 @@ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
|
||||
** no such database exists.
|
||||
*/
|
||||
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
|
||||
}
|
||||
|
||||
+6
-5
@@ -377,11 +377,12 @@ void *sqlite3ScratchMalloc(int n){
|
||||
|
||||
|
||||
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
||||
/* Verify that no more than two scratch allocations per thread
|
||||
** are outstanding at one time. (This is only checked in the
|
||||
** single-threaded case since checking in the multi-threaded case
|
||||
** would be much more complicated.) */
|
||||
assert( scratchAllocOut<=1 );
|
||||
/* EVIDENCE-OF: R-12970-05880 SQLite will not use more than one scratch
|
||||
** buffers per thread.
|
||||
**
|
||||
** This can only be checked in single-threaded mode.
|
||||
*/
|
||||
assert( scratchAllocOut==0 );
|
||||
if( p ) scratchAllocOut++;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ int sqlite3MutexEnd(void){
|
||||
sqlite3_mutex *sqlite3_mutex_alloc(int id){
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
|
||||
if( id>SQLITE_MUTEX_RECURSIVE && sqlite3MutexInit() ) return 0;
|
||||
#endif
|
||||
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
|
||||
}
|
||||
|
||||
+6
-2
@@ -175,8 +175,12 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( iType-2 >= 0 );
|
||||
assert( iType-2 < ArraySize(staticMutexes) );
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
p = &staticMutexes[iType-2];
|
||||
#if SQLITE_MUTEX_NREF
|
||||
p->id = iType;
|
||||
|
||||
@@ -361,6 +361,10 @@ int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
|
||||
int rc = sqlite3_initialize();
|
||||
if( rc ) return rc;
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( pVfs==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
|
||||
MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
sqlite3_mutex_enter(mutex);
|
||||
vfsUnlink(pVfs);
|
||||
|
||||
+17
-10
@@ -34,6 +34,11 @@
|
||||
with SQLITE_OMIT_WAL."
|
||||
#endif
|
||||
|
||||
#if !SQLITE_OS_WINNT && SQLITE_MAX_MMAP_SIZE>0
|
||||
# error "Memory mapped files require support from the Windows NT kernel,\
|
||||
compile with SQLITE_MAX_MMAP_SIZE=0."
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
|
||||
** based on the sub-platform)?
|
||||
@@ -163,10 +168,11 @@
|
||||
|
||||
/*
|
||||
** Do we need to manually define the Win32 file mapping APIs for use with WAL
|
||||
** mode (e.g. these APIs are available in the Windows CE SDK; however, they
|
||||
** are not present in the header file)?
|
||||
** mode or memory mapped files (e.g. these APIs are available in the Windows
|
||||
** CE SDK; however, they are not present in the header file)?
|
||||
*/
|
||||
#if SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL)
|
||||
#if SQLITE_WIN32_FILEMAPPING_API && \
|
||||
(!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
||||
/*
|
||||
** Two of the file mapping APIs are different under WinRT. Figure out which
|
||||
** set we need.
|
||||
@@ -194,7 +200,7 @@ WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
||||
** This file mapping API is common to both Win32 and WinRT.
|
||||
*/
|
||||
WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
|
||||
#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */
|
||||
#endif /* SQLITE_WIN32_FILEMAPPING_API */
|
||||
|
||||
/*
|
||||
** Some Microsoft compilers lack this definition.
|
||||
@@ -487,7 +493,7 @@ static struct win_syscall {
|
||||
LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
|
||||
|
||||
#if (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
|
||||
!defined(SQLITE_OMIT_WAL))
|
||||
(!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
|
||||
{ "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 },
|
||||
#else
|
||||
{ "CreateFileMappingA", (SYSCALL)0, 0 },
|
||||
@@ -497,7 +503,7 @@ static struct win_syscall {
|
||||
DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent)
|
||||
|
||||
#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
|
||||
!defined(SQLITE_OMIT_WAL))
|
||||
(!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
|
||||
{ "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 },
|
||||
#else
|
||||
{ "CreateFileMappingW", (SYSCALL)0, 0 },
|
||||
@@ -837,7 +843,8 @@ static struct win_syscall {
|
||||
LPOVERLAPPED))aSyscall[48].pCurrent)
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL))
|
||||
#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \
|
||||
(!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
|
||||
{ "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 },
|
||||
#else
|
||||
{ "MapViewOfFile", (SYSCALL)0, 0 },
|
||||
@@ -907,7 +914,7 @@ static struct win_syscall {
|
||||
#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
|
||||
LPOVERLAPPED))aSyscall[58].pCurrent)
|
||||
|
||||
#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL)
|
||||
#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
||||
{ "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
|
||||
#else
|
||||
{ "UnmapViewOfFile", (SYSCALL)0, 0 },
|
||||
@@ -970,7 +977,7 @@ static struct win_syscall {
|
||||
#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
|
||||
FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent)
|
||||
|
||||
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
|
||||
#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
||||
{ "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
|
||||
#else
|
||||
{ "MapViewOfFileFromApp", (SYSCALL)0, 0 },
|
||||
@@ -1034,7 +1041,7 @@ static struct win_syscall {
|
||||
|
||||
#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent)
|
||||
|
||||
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
|
||||
#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
||||
{ "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
|
||||
#else
|
||||
{ "CreateFileMappingFromApp", (SYSCALL)0, 0 },
|
||||
|
||||
+3
-2
@@ -2899,7 +2899,7 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
|
||||
**
|
||||
** For an encrypted database, the situation is more complex: bytes
|
||||
** 24..39 of the database are white noise. But the probability of
|
||||
** white noising equaling 16 bytes of 0xff is vanishingly small so
|
||||
** white noise equaling 16 bytes of 0xff is vanishingly small so
|
||||
** we should still be ok.
|
||||
*/
|
||||
memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers));
|
||||
@@ -7075,7 +7075,8 @@ int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
|
||||
int rc = SQLITE_OK;
|
||||
if( pPager->pWal ){
|
||||
rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
|
||||
pPager->xBusyHandler, pPager->pBusyHandlerArg,
|
||||
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
|
||||
pPager->pBusyHandlerArg,
|
||||
pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
|
||||
pnLog, pnCkpt
|
||||
);
|
||||
|
||||
@@ -651,6 +651,13 @@ void sqlite3PcacheShrink(PCache *pCache){
|
||||
sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of the header added by this middleware layer
|
||||
** in the page-cache hierarchy.
|
||||
*/
|
||||
int sqlite3HeaderSizePcache(void){ return sizeof(PgHdr); }
|
||||
|
||||
|
||||
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
|
||||
/*
|
||||
** For all dirty pages currently in the cache, invoke the specified
|
||||
|
||||
@@ -160,4 +160,8 @@ void sqlite3PcacheStats(int*,int*,int*,int*);
|
||||
|
||||
void sqlite3PCacheSetDefault(void);
|
||||
|
||||
/* Return the header size */
|
||||
int sqlite3HeaderSizePcache(void);
|
||||
int sqlite3HeaderSizePcache1(void);
|
||||
|
||||
#endif /* _PCACHE_H_ */
|
||||
|
||||
@@ -981,6 +981,11 @@ void sqlite3PCacheSetDefault(void){
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of the header on each page of this PCACHE implementation.
|
||||
*/
|
||||
int sqlite3HeaderSizePcache1(void){ return sizeof(PgHdr1); }
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/*
|
||||
** This function is called to free superfluous dynamically allocated memory
|
||||
|
||||
+3
-1
@@ -2195,7 +2195,7 @@ void sqlite3Pragma(
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/*
|
||||
** PRAGMA [database.]wal_checkpoint = passive|full|restart
|
||||
** PRAGMA [database.]wal_checkpoint = passive|full|restart|truncate
|
||||
**
|
||||
** Checkpoint the database.
|
||||
*/
|
||||
@@ -2207,6 +2207,8 @@ void sqlite3Pragma(
|
||||
eMode = SQLITE_CHECKPOINT_FULL;
|
||||
}else if( sqlite3StrICmp(zRight, "restart")==0 ){
|
||||
eMode = SQLITE_CHECKPOINT_RESTART;
|
||||
}else if( sqlite3StrICmp(zRight, "truncate")==0 ){
|
||||
eMode = SQLITE_CHECKPOINT_TRUNCATE;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeSetNumCols(v, 3);
|
||||
|
||||
+9
-4
@@ -709,9 +709,12 @@ static int sqlite3LockAndPrepare(
|
||||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
int rc;
|
||||
assert( ppStmt!=0 );
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
*ppStmt = 0;
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
@@ -818,9 +821,11 @@ static int sqlite3Prepare16(
|
||||
const char *zTail8 = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( ppStmt );
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
*ppStmt = 0;
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
if( nBytes>=0 ){
|
||||
|
||||
+46
-14
@@ -212,7 +212,7 @@ void sqlite3VXPrintf(
|
||||
const et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char *zOut; /* Rendering buffer */
|
||||
int nOut; /* Size of the rendering buffer */
|
||||
char *zExtra; /* Malloced memory used by some conversion */
|
||||
char *zExtra = 0; /* Malloced memory used by some conversion */
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
int exp, e2; /* exponent of real numbers */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
@@ -223,6 +223,13 @@ void sqlite3VXPrintf(
|
||||
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ap==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
sqlite3StrAccumReset(pAccum);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
bufpt = 0;
|
||||
if( bFlags ){
|
||||
if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
|
||||
@@ -329,7 +336,6 @@ void sqlite3VXPrintf(
|
||||
break;
|
||||
}
|
||||
}
|
||||
zExtra = 0;
|
||||
|
||||
/*
|
||||
** At this point, variables are initialized as follows:
|
||||
@@ -620,13 +626,16 @@ void sqlite3VXPrintf(
|
||||
}else{
|
||||
c = va_arg(ap,int);
|
||||
}
|
||||
buf[0] = (char)c;
|
||||
if( precision>=0 ){
|
||||
for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
|
||||
length = precision;
|
||||
}else{
|
||||
length =1;
|
||||
if( precision>1 ){
|
||||
width -= precision-1;
|
||||
if( width>1 && !flag_leftjustify ){
|
||||
sqlite3AppendChar(pAccum, width-1, ' ');
|
||||
width = 0;
|
||||
}
|
||||
sqlite3AppendChar(pAccum, precision-1, c);
|
||||
}
|
||||
length = 1;
|
||||
buf[0] = c;
|
||||
bufpt = buf;
|
||||
break;
|
||||
case etSTRING:
|
||||
@@ -727,11 +736,14 @@ void sqlite3VXPrintf(
|
||||
** the output.
|
||||
*/
|
||||
width -= length;
|
||||
if( width>0 && !flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
|
||||
if( width>0 && !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
||||
sqlite3StrAccumAppend(pAccum, bufpt, length);
|
||||
if( width>0 && flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
|
||||
if( width>0 && flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
||||
|
||||
if( zExtra ) sqlite3_free(zExtra);
|
||||
if( zExtra ){
|
||||
sqlite3_free(zExtra);
|
||||
zExtra = 0;
|
||||
}
|
||||
}/* End for loop over the format string */
|
||||
} /* End of function */
|
||||
|
||||
@@ -758,6 +770,11 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
char *zOld = (p->zText==p->zBase ? 0 : p->zText);
|
||||
i64 szNew = p->nChar;
|
||||
szNew += N + 1;
|
||||
if( szNew+p->nChar<=p->mxAlloc ){
|
||||
/* Force exponential buffer size growth as long as it does not overflow,
|
||||
** to avoid having to call this routine too often */
|
||||
szNew += p->nChar;
|
||||
}
|
||||
if( szNew > p->mxAlloc ){
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_TOOBIG);
|
||||
@@ -774,6 +791,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
assert( p->zText!=0 || p->nChar==0 );
|
||||
if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
||||
p->zText = zNew;
|
||||
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
|
||||
}else{
|
||||
sqlite3StrAccumReset(p);
|
||||
setStrAccumError(p, STRACCUM_NOMEM);
|
||||
@@ -784,11 +802,11 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
||||
}
|
||||
|
||||
/*
|
||||
** Append N space characters to the given string buffer.
|
||||
** Append N copies of character c to the given string buffer.
|
||||
*/
|
||||
void sqlite3AppendSpace(StrAccum *p, int N){
|
||||
void sqlite3AppendChar(StrAccum *p, int N, char c){
|
||||
if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ) return;
|
||||
while( (N--)>0 ) p->zText[p->nChar++] = ' ';
|
||||
while( (N--)>0 ) p->zText[p->nChar++] = c;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -943,6 +961,13 @@ char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
||||
char *z;
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
StrAccum acc;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( zFormat==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
if( sqlite3_initialize() ) return 0;
|
||||
#endif
|
||||
@@ -985,6 +1010,13 @@ char *sqlite3_mprintf(const char *zFormat, ...){
|
||||
char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
|
||||
StrAccum acc;
|
||||
if( n<=0 ) return zBuf;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( zBuf==0 || zFormat==0 ) {
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
if( zBuf && n>0 ) zBuf[0] = 0;
|
||||
return zBuf;
|
||||
}
|
||||
#endif
|
||||
sqlite3StrAccumInit(&acc, zBuf, n, 0);
|
||||
acc.useMalloc = 0;
|
||||
sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
||||
|
||||
+11
-3
@@ -48,11 +48,19 @@ void sqlite3_randomness(int N, void *pBuf){
|
||||
#endif
|
||||
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
|
||||
sqlite3_mutex_enter(mutex);
|
||||
sqlite3_mutex *mutex;
|
||||
#endif
|
||||
|
||||
if( N<=0 ){
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
if( sqlite3_initialize() ) return;
|
||||
#endif
|
||||
|
||||
#if SQLITE_THREADSAFE
|
||||
mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
|
||||
#endif
|
||||
|
||||
sqlite3_mutex_enter(mutex);
|
||||
if( N<=0 || pBuf==0 ){
|
||||
wsdPrng.isInit = 0;
|
||||
sqlite3_mutex_leave(mutex);
|
||||
return;
|
||||
|
||||
@@ -320,6 +320,10 @@ static int lookupName(
|
||||
if( pMatch ){
|
||||
pExpr->iTable = pMatch->iCursor;
|
||||
pExpr->pTab = pMatch->pTab;
|
||||
assert( (pMatch->jointype & JT_RIGHT)==0 ); /* RIGHT JOIN not (yet) supported */
|
||||
if( (pMatch->jointype & JT_LEFT)!=0 ){
|
||||
ExprSetProperty(pExpr, EP_CanBeNull);
|
||||
}
|
||||
pSchema = pExpr->pTab->pSchema;
|
||||
}
|
||||
} /* if( pSrcList ) */
|
||||
|
||||
+1
-2
@@ -4829,7 +4829,7 @@ int sqlite3Select(
|
||||
**
|
||||
** is transformed to:
|
||||
**
|
||||
** SELECT xyz FROM ... GROUP BY xyz
|
||||
** SELECT xyz FROM ... GROUP BY xyz ORDER BY xyz
|
||||
**
|
||||
** The second form is preferred as a single index (or temp-table) may be
|
||||
** used for both the ORDER BY and DISTINCT processing. As originally
|
||||
@@ -4842,7 +4842,6 @@ int sqlite3Select(
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
|
||||
pGroupBy = p->pGroupBy;
|
||||
sSort.pOrderBy = 0;
|
||||
/* Notice that even thought SF_Distinct has been cleared from p->selFlags,
|
||||
** the sDistinct.isTnct is still set. Hence, isTnct represents the
|
||||
** original setting of the SF_Distinct flag, not the current setting */
|
||||
|
||||
+181
-60
@@ -172,7 +172,8 @@ static HANDLE hProcess;
|
||||
static FILETIME ftKernelBegin;
|
||||
static FILETIME ftUserBegin;
|
||||
static sqlite3_int64 ftWallBegin;
|
||||
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
|
||||
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME,
|
||||
LPFILETIME, LPFILETIME);
|
||||
static GETPROCTIMES getProcessTimesAddr = NULL;
|
||||
|
||||
/*
|
||||
@@ -183,15 +184,16 @@ static int hasTimer(void){
|
||||
if( getProcessTimesAddr ){
|
||||
return 1;
|
||||
} else {
|
||||
/* GetProcessTimes() isn't supported in WIN95 and some other Windows versions.
|
||||
** See if the version we are running on has it, and if it does, save off
|
||||
** a pointer to it and the current process handle.
|
||||
/* GetProcessTimes() isn't supported in WIN95 and some other Windows
|
||||
** versions. See if the version we are running on has it, and if it
|
||||
** does, save off a pointer to it and the current process handle.
|
||||
*/
|
||||
hProcess = GetCurrentProcess();
|
||||
if( hProcess ){
|
||||
HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
|
||||
if( NULL != hinstLib ){
|
||||
getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
|
||||
getProcessTimesAddr =
|
||||
(GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
|
||||
if( NULL != getProcessTimesAddr ){
|
||||
return 1;
|
||||
}
|
||||
@@ -208,7 +210,8 @@ static int hasTimer(void){
|
||||
static void beginTimer(void){
|
||||
if( enableTimer && getProcessTimesAddr ){
|
||||
FILETIME ftCreation, ftExit;
|
||||
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin);
|
||||
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,
|
||||
&ftKernelBegin,&ftUserBegin);
|
||||
ftWallBegin = timeOfDay();
|
||||
}
|
||||
}
|
||||
@@ -227,7 +230,7 @@ static void endTimer(void){
|
||||
if( enableTimer && getProcessTimesAddr){
|
||||
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
|
||||
sqlite3_int64 ftWallEnd = timeOfDay();
|
||||
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd);
|
||||
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
|
||||
printf("Run Time: real %.3f user %f sys %f\n",
|
||||
(ftWallEnd - ftWallBegin)*0.001,
|
||||
timeDiff(&ftUserBegin, &ftUserEnd),
|
||||
@@ -457,6 +460,7 @@ struct ShellState {
|
||||
int echoOn; /* True to echo input commands */
|
||||
int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
|
||||
int statsOn; /* True to display memory stats before each finalize */
|
||||
int scanstatsOn; /* True to display scan stats before each finalize */
|
||||
int outCount; /* Revert to stdout when reaching zero */
|
||||
int cnt; /* Number of records displayed so far */
|
||||
FILE *out; /* Write results here */
|
||||
@@ -725,7 +729,13 @@ static void interrupt_handler(int NotUsed){
|
||||
** This is the callback routine that the shell
|
||||
** invokes for each row of a query result.
|
||||
*/
|
||||
static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int *aiType){
|
||||
static int shell_callback(
|
||||
void *pArg,
|
||||
int nArg, /* Number of result columns */
|
||||
char **azArg, /* Text of each result column */
|
||||
char **azCol, /* Column names */
|
||||
int *aiType /* Column types */
|
||||
){
|
||||
int i;
|
||||
ShellState *p = (ShellState*)pArg;
|
||||
|
||||
@@ -1104,57 +1114,77 @@ static int display_stats(
|
||||
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out,
|
||||
"Memory Used: %d (max %d) bytes\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n",
|
||||
iCur, iHiwtr);
|
||||
if( pArg->shellFlgs & SHFLG_Pagecache ){
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out,
|
||||
"Number of Pcache Pages Used: %d (max %d) pages\n",
|
||||
iCur, iHiwtr);
|
||||
}
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out,
|
||||
"Number of Pcache Overflow Bytes: %d (max %d) bytes\n",
|
||||
iCur, iHiwtr);
|
||||
if( pArg->shellFlgs & SHFLG_Scratch ){
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n",
|
||||
iCur, iHiwtr);
|
||||
}
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out,
|
||||
"Number of Scratch Overflow Bytes: %d (max %d) bytes\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Largest Allocation: %d bytes\n", iHiwtr);
|
||||
fprintf(pArg->out, "Largest Allocation: %d bytes\n",
|
||||
iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", iHiwtr);
|
||||
fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n",
|
||||
iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", iHiwtr);
|
||||
fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n",
|
||||
iHiwtr);
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", iCur, iHiwtr);
|
||||
fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n",
|
||||
iCur, iHiwtr);
|
||||
#endif
|
||||
}
|
||||
|
||||
if( pArg && pArg->out && db ){
|
||||
if( pArg->shellFlgs & SHFLG_Lookaside ){
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED,
|
||||
&iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n",
|
||||
iCur, iHiwtr);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT,
|
||||
&iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE,
|
||||
&iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset);
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL,
|
||||
&iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr);
|
||||
}
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1;
|
||||
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n",iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache hits: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
@@ -1165,18 +1195,19 @@ static int display_stats(
|
||||
fprintf(pArg->out, "Page cache writes: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
|
||||
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n",iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur);
|
||||
fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n",iCur);
|
||||
}
|
||||
|
||||
if( pArg && pArg->out && db && pArg->pStmt ){
|
||||
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset);
|
||||
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
|
||||
bReset);
|
||||
fprintf(pArg->out, "Fullscan Steps: %d\n", iCur);
|
||||
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset);
|
||||
fprintf(pArg->out, "Sort Operations: %d\n", iCur);
|
||||
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset);
|
||||
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset);
|
||||
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur);
|
||||
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
|
||||
fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur);
|
||||
@@ -1185,6 +1216,51 @@ static int display_stats(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Display scan stats.
|
||||
*/
|
||||
static void display_scanstats(
|
||||
sqlite3 *db, /* Database to query */
|
||||
ShellState *pArg /* Pointer to ShellState */
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
int i, k, n, mx;
|
||||
fprintf(pArg->out, "-------- scanstats --------\n");
|
||||
mx = 0;
|
||||
for(k=0; k<=mx; k++){
|
||||
double rEstLoop = 1.0;
|
||||
for(i=n=0; 1; i++){
|
||||
sqlite3_stmt *p = pArg->pStmt;
|
||||
sqlite3_int64 nLoop, nVisit;
|
||||
double rEst;
|
||||
int iSid;
|
||||
const char *zExplain;
|
||||
if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){
|
||||
break;
|
||||
}
|
||||
sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid);
|
||||
if( iSid>mx ) mx = iSid;
|
||||
if( iSid!=k ) continue;
|
||||
if( n==0 ){
|
||||
rEstLoop = (double)nLoop;
|
||||
if( k>0 ) fprintf(pArg->out, "-------- subquery %d -------\n", k);
|
||||
}
|
||||
n++;
|
||||
sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit);
|
||||
sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst);
|
||||
sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain);
|
||||
fprintf(pArg->out, "Loop %2d: %s\n", n, zExplain);
|
||||
rEstLoop *= rEst;
|
||||
fprintf(pArg->out,
|
||||
" nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n",
|
||||
nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst
|
||||
);
|
||||
}
|
||||
}
|
||||
fprintf(pArg->out, "---------------------------\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter azArray points to a zero-terminated array of strings. zStr
|
||||
** points to a single nul-terminated string. Return non-zero if zStr
|
||||
@@ -1226,7 +1302,8 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){
|
||||
|
||||
const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
|
||||
"NextIfOpen", "PrevIfOpen", 0 };
|
||||
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 };
|
||||
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
|
||||
"Rewind", 0 };
|
||||
const char *azGoto[] = { "Goto", 0 };
|
||||
|
||||
/* Try to figure out if this is really an EXPLAIN statement. If this
|
||||
@@ -1339,7 +1416,8 @@ static int shell_exec(
|
||||
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
|
||||
if( pArg && pArg->autoEQP ){
|
||||
sqlite3_stmt *pExplain;
|
||||
char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", sqlite3_sql(pStmt));
|
||||
char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s",
|
||||
sqlite3_sql(pStmt));
|
||||
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pExplain)==SQLITE_ROW ){
|
||||
@@ -1423,6 +1501,11 @@ static int shell_exec(
|
||||
display_stats(db, pArg, 0);
|
||||
}
|
||||
|
||||
/* print loop-counters if required */
|
||||
if( pArg && pArg->scanstatsOn ){
|
||||
display_scanstats(db, pArg);
|
||||
}
|
||||
|
||||
/* Finalize the statement just executed. If this fails, save a
|
||||
** copy of the error message. Otherwise, set zSql to point to the
|
||||
** next statement to execute. */
|
||||
@@ -1633,6 +1716,7 @@ static char zHelp[] =
|
||||
".read FILENAME Execute SQL in FILENAME\n"
|
||||
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n"
|
||||
".save FILE Write in-memory database into FILE\n"
|
||||
".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n"
|
||||
".schema ?TABLE? Show the CREATE statements\n"
|
||||
" If TABLE specified, only show tables matching\n"
|
||||
" LIKE pattern TABLE.\n"
|
||||
@@ -3014,6 +3098,19 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
sqlite3_close(pSrc);
|
||||
}else
|
||||
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
p->scanstatsOn = booleanValue(azArg[1]);
|
||||
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
fprintf(stderr, "Warning: .scanstats not available in this build.\n");
|
||||
#endif
|
||||
}else{
|
||||
fprintf(stderr, "Usage: .scanstats on|off\n");
|
||||
rc = 1;
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
|
||||
ShellState data;
|
||||
char *zErrMsg = 0;
|
||||
@@ -3271,7 +3368,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
for(i=0; i<nPrintRow; i++){
|
||||
for(j=i; j<nRow; j+=nPrintRow){
|
||||
char *zSp = j<nPrintRow ? "" : " ";
|
||||
fprintf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
|
||||
fprintf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:"");
|
||||
}
|
||||
fprintf(p->out, "\n");
|
||||
}
|
||||
@@ -3741,7 +3838,8 @@ static char *find_home_dir(void){
|
||||
static char *home_dir = NULL;
|
||||
if( home_dir ) return home_dir;
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL)
|
||||
#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \
|
||||
&& !defined(__RTP__) && !defined(_WRS_KERNEL)
|
||||
{
|
||||
struct passwd *pwent;
|
||||
uid_t uid = getuid();
|
||||
@@ -3940,10 +4038,12 @@ int main(int argc, char **argv){
|
||||
char *zErrMsg = 0;
|
||||
ShellState data;
|
||||
const char *zInitFile = 0;
|
||||
char *zFirstCmd = 0;
|
||||
int i;
|
||||
int rc = 0;
|
||||
int warnInmemoryDb = 0;
|
||||
int readStdin = 1;
|
||||
int nCmd = 0;
|
||||
char **azCmd = 0;
|
||||
|
||||
#if USE_SYSTEM_SQLITE+0!=1
|
||||
if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){
|
||||
@@ -3963,6 +4063,18 @@ int main(int argc, char **argv){
|
||||
signal(SIGINT, interrupt_handler);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_SHELL_DBNAME_PROC
|
||||
{
|
||||
/* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name
|
||||
** of a C-function that will provide the name of the database file. Use
|
||||
** this compile-time option to embed this shell program in larger
|
||||
** applications. */
|
||||
extern void SQLITE_SHELL_DBNAME_PROC(const char**);
|
||||
SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename);
|
||||
warnInmemoryDb = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Do an initial pass through the command-line argument to locate
|
||||
** the name of the database file, the name of the initialization file,
|
||||
** the size of the alternative malloc heap,
|
||||
@@ -3974,15 +4086,18 @@ int main(int argc, char **argv){
|
||||
if( z[0]!='-' ){
|
||||
if( data.zDbFilename==0 ){
|
||||
data.zDbFilename = z;
|
||||
continue;
|
||||
}else{
|
||||
/* Excesss arguments are interpreted as SQL (or dot-commands) and
|
||||
** mean that nothing is read from stdin */
|
||||
readStdin = 0;
|
||||
nCmd++;
|
||||
azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
|
||||
if( azCmd==0 ){
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
azCmd[nCmd-1] = z;
|
||||
}
|
||||
if( zFirstCmd==0 ){
|
||||
zFirstCmd = z;
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr,"%s: Error: too many options: \"%s\"\n", Argv0, argv[i]);
|
||||
fprintf(stderr,"Use -help for a list of options.\n");
|
||||
return 1;
|
||||
}
|
||||
if( z[1]=='-' ) z++;
|
||||
if( strcmp(z,"-separator")==0
|
||||
@@ -4072,11 +4187,6 @@ int main(int argc, char **argv){
|
||||
#else
|
||||
fprintf(stderr,"%s: Error: no database filename specified\n", Argv0);
|
||||
return 1;
|
||||
#endif
|
||||
#ifdef SQLITE_SHELL_DBNAME_PROC
|
||||
{ extern void SQLITE_SHELL_DBNAME_PROC(const char**);
|
||||
SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename);
|
||||
warnInmemoryDb = 0; }
|
||||
#endif
|
||||
}
|
||||
data.out = stdout;
|
||||
@@ -4140,6 +4250,8 @@ int main(int argc, char **argv){
|
||||
data.autoEQP = 1;
|
||||
}else if( strcmp(z,"-stats")==0 ){
|
||||
data.statsOn = 1;
|
||||
}else if( strcmp(z,"-scanstats")==0 ){
|
||||
data.scanstatsOn = 1;
|
||||
}else if( strcmp(z,"-bail")==0 ){
|
||||
bail_on_error = 1;
|
||||
}else if( strcmp(z,"-version")==0 ){
|
||||
@@ -4172,6 +4284,10 @@ int main(int argc, char **argv){
|
||||
}else if( strcmp(z,"-help")==0 ){
|
||||
usage(1);
|
||||
}else if( strcmp(z,"-cmd")==0 ){
|
||||
/* Run commands that follow -cmd first and separately from commands
|
||||
** that simply appear on the command-line. This seems goofy. It would
|
||||
** be better if all commands ran in the order that they appear. But
|
||||
** we retain the goofy behavior for historical compatibility. */
|
||||
if( i==argc-1 ) break;
|
||||
z = cmdline_option_value(argc,argv,++i);
|
||||
if( z[0]=='.' ){
|
||||
@@ -4195,23 +4311,28 @@ int main(int argc, char **argv){
|
||||
}
|
||||
}
|
||||
|
||||
if( zFirstCmd ){
|
||||
/* Run just the command that follows the database name
|
||||
if( !readStdin ){
|
||||
/* Run all arguments that do not begin with '-' as if they were separate
|
||||
** command-line inputs, except for the argToSkip argument which contains
|
||||
** the database filename.
|
||||
*/
|
||||
if( zFirstCmd[0]=='.' ){
|
||||
rc = do_meta_command(zFirstCmd, &data);
|
||||
if( rc==2 ) rc = 0;
|
||||
}else{
|
||||
open_db(&data, 0);
|
||||
rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg);
|
||||
if( zErrMsg!=0 ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
return rc!=0 ? rc : 1;
|
||||
}else if( rc!=0 ){
|
||||
fprintf(stderr,"Error: unable to process SQL \"%s\"\n", zFirstCmd);
|
||||
return rc;
|
||||
for(i=0; i<nCmd; i++){
|
||||
if( azCmd[i][0]=='.' ){
|
||||
rc = do_meta_command(azCmd[i], &data);
|
||||
if( rc ) return rc==2 ? 0 : rc;
|
||||
}else{
|
||||
open_db(&data, 0);
|
||||
rc = shell_exec(data.db, azCmd[i], shell_callback, &data, &zErrMsg);
|
||||
if( zErrMsg!=0 ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
return rc!=0 ? rc : 1;
|
||||
}else if( rc!=0 ){
|
||||
fprintf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(azCmd);
|
||||
}else{
|
||||
/* Run commands received from standard input
|
||||
*/
|
||||
|
||||
+439
-289
File diff suppressed because it is too large
Load Diff
+11
-7
@@ -193,10 +193,9 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1.
|
||||
** It determines whether or not the features related to
|
||||
** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can
|
||||
** be overridden at runtime using the sqlite3_config() API.
|
||||
** EVIDENCE-OF: R-25715-37072 Memory allocation statistics are enabled by
|
||||
** default unless SQLite is compiled with SQLITE_DEFAULT_MEMSTATUS=0 in
|
||||
** which case memory allocation statistics are disabled by default.
|
||||
*/
|
||||
#if !defined(SQLITE_DEFAULT_MEMSTATUS)
|
||||
# define SQLITE_DEFAULT_MEMSTATUS 1
|
||||
@@ -563,7 +562,7 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
|
||||
** gives a possible range of values of approximately 1.0e986 to 1e-986.
|
||||
** But the allowed values are "grainy". Not every value is representable.
|
||||
** For example, quantities 16 and 17 are both represented by a LogEst
|
||||
** of 40. However, since LogEst quantaties are suppose to be estimates,
|
||||
** of 40. However, since LogEst quantities are suppose to be estimates,
|
||||
** not exact values, this imprecision is not a problem.
|
||||
**
|
||||
** "LogEst" is short for "Logarithmic Estimate".
|
||||
@@ -1796,6 +1795,7 @@ struct Index {
|
||||
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
|
||||
unsigned isResized:1; /* True if resizeIndexObject() has been called */
|
||||
unsigned isCovering:1; /* True if this is a covering index */
|
||||
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
int nSample; /* Number of elements in aSample[] */
|
||||
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
|
||||
@@ -2015,7 +2015,7 @@ struct Expr {
|
||||
/*
|
||||
** The following are the meanings of bits in the Expr.flags field.
|
||||
*/
|
||||
#define EP_FromJoin 0x000001 /* Originated in ON or USING clause of a join */
|
||||
#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
|
||||
#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
|
||||
#define EP_Resolved 0x000004 /* IDs have been resolved to COLUMNs */
|
||||
#define EP_Error 0x000008 /* Expression contains one or more errors */
|
||||
@@ -2035,6 +2035,7 @@ struct Expr {
|
||||
#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
|
||||
#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
|
||||
#define EP_Constant 0x080000 /* Node is a constant */
|
||||
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
|
||||
|
||||
/*
|
||||
** These macros can be used to test, set, or clear bits in the
|
||||
@@ -3452,6 +3453,9 @@ const char *sqlite3ErrName(int);
|
||||
|
||||
const char *sqlite3ErrStr(int);
|
||||
int sqlite3ReadSchema(Parse *pParse);
|
||||
#if defined(SQLITE_DEBUG)
|
||||
int sqlite3ValidCollSeq(const CollSeq*);
|
||||
#endif
|
||||
CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
|
||||
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
|
||||
CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
|
||||
@@ -3543,7 +3547,7 @@ int sqlite3OpenTempDatabase(Parse *);
|
||||
void sqlite3StrAccumInit(StrAccum*, char*, int, int);
|
||||
void sqlite3StrAccumAppend(StrAccum*,const char*,int);
|
||||
void sqlite3StrAccumAppendAll(StrAccum*,const char*);
|
||||
void sqlite3AppendSpace(StrAccum*,int);
|
||||
void sqlite3AppendChar(StrAccum*,int,char);
|
||||
char *sqlite3StrAccumFinish(StrAccum*);
|
||||
void sqlite3StrAccumReset(StrAccum*);
|
||||
void sqlite3SelectDestInit(SelectDest*,int,int);
|
||||
|
||||
@@ -86,6 +86,9 @@ int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){
|
||||
if( op<0 || op>=ArraySize(wsdStat.nowValue) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
*pCurrent = wsdStat.nowValue[op];
|
||||
*pHighwater = wsdStat.mxValue[op];
|
||||
if( resetFlag ){
|
||||
@@ -105,6 +108,11 @@ int sqlite3_db_status(
|
||||
int resetFlag /* Reset high-water mark if true */
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
switch( op ){
|
||||
case SQLITE_DBSTATUS_LOOKASIDE_USED: {
|
||||
|
||||
@@ -126,6 +126,9 @@ int sqlite3_get_table(
|
||||
int rc;
|
||||
TabResult res;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( pazResult==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
*pazResult = 0;
|
||||
if( pnColumn ) *pnColumn = 0;
|
||||
if( pnRow ) *pnRow = 0;
|
||||
|
||||
@@ -3641,6 +3641,45 @@ static int db_use_legacy_prepare_cmd(
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: db_last_stmt_ptr DB
|
||||
**
|
||||
** If the statement cache associated with database DB is not empty,
|
||||
** return the text representation of the most recently used statement
|
||||
** handle.
|
||||
*/
|
||||
static int db_last_stmt_ptr(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
|
||||
Tcl_CmdInfo cmdInfo;
|
||||
SqliteDb *pDb;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char zBuf[100];
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
|
||||
Tcl_AppendResult(interp, "no such db: ", Tcl_GetString(objv[1]), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pDb = (SqliteDb*)cmdInfo.objClientData;
|
||||
|
||||
if( pDb->stmtList ) pStmt = pDb->stmtList->pStmt;
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -3686,6 +3725,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetest9_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestasync_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_autoext_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_blob_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
|
||||
extern int Sqlitetest_func_Init(Tcl_Interp*);
|
||||
extern int Sqlitetest_hexio_Init(Tcl_Interp*);
|
||||
@@ -3729,6 +3769,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetest9_Init(interp);
|
||||
Sqlitetestasync_Init(interp);
|
||||
Sqlitetest_autoext_Init(interp);
|
||||
Sqlitetest_blob_Init(interp);
|
||||
Sqlitetest_demovfs_Init(interp);
|
||||
Sqlitetest_func_Init(interp);
|
||||
Sqlitetest_hexio_Init(interp);
|
||||
@@ -3760,6 +3801,9 @@ static void init_all(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "db_use_legacy_prepare", db_use_legacy_prepare_cmd, 0, 0
|
||||
);
|
||||
Tcl_CreateObjCommand(
|
||||
interp, "db_last_stmt_ptr", db_last_stmt_ptr, 0, 0
|
||||
);
|
||||
|
||||
#ifdef SQLITE_SSE
|
||||
Sqlitetestsse_Init(interp);
|
||||
|
||||
+95
-154
@@ -1651,154 +1651,6 @@ static int blobHandleFromObj(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_bytes CHANNEL
|
||||
*/
|
||||
static int test_blob_bytes(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
nByte = sqlite3_blob_bytes(pBlob);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_close CHANNEL
|
||||
*/
|
||||
static int test_blob_close(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
sqlite3_blob_close(pBlob);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_read CHANNEL OFFSET N
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_read() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_read()
|
||||
** to read N bytes from offset OFFSET from the underlying SQLite
|
||||
** blob handle.
|
||||
**
|
||||
** On success, a byte-array object containing the read data is
|
||||
** returned. On failure, the interpreter result is set to the
|
||||
** text representation of the returned error code (i.e. "SQLITE_NOMEM")
|
||||
** and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_read(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
int iOffset;
|
||||
unsigned char *zBuf = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( nByte>0 ){
|
||||
zBuf = (unsigned char *)Tcl_Alloc(nByte);
|
||||
}
|
||||
rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
|
||||
}else{
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
Tcl_Free((char *)zBuf);
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_write CHANNEL OFFSET DATA ?NDATA?
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_write() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_write()
|
||||
** to write the DATA byte-array to the underlying SQLite blob handle.
|
||||
** at offset OFFSET.
|
||||
**
|
||||
** On success, an empty string is returned. On failure, the interpreter
|
||||
** result is set to the text representation of the returned error code
|
||||
** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_write(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int iOffset;
|
||||
int rc;
|
||||
|
||||
unsigned char *zBuf;
|
||||
int nBuf;
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA ?NDATA?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
|
||||
if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
static int test_blob_reopen(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
@@ -2301,6 +2153,77 @@ static int test_stmt_status(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
/*
|
||||
** Usage: sqlite3_stmt_scanstatus STMT IDX
|
||||
*/
|
||||
static int test_stmt_scanstatus(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt; /* First argument */
|
||||
int idx; /* Second argument */
|
||||
|
||||
const char *zName;
|
||||
const char *zExplain;
|
||||
sqlite3_int64 nLoop;
|
||||
sqlite3_int64 nVisit;
|
||||
double rEst;
|
||||
int res;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "STMT IDX");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
|
||||
|
||||
res = sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop);
|
||||
if( res==0 ){
|
||||
Tcl_Obj *pRet = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nLoop", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nLoop));
|
||||
sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit);
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nVisit", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewWideIntObj(nVisit));
|
||||
sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_EST, (void*)&rEst);
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("nEst", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewDoubleObj(rEst));
|
||||
sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_NAME, (void*)&zName);
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zName", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zName, -1));
|
||||
sqlite3_stmt_scanstatus(pStmt, idx, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain);
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj("zExplain", -1));
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zExplain, -1));
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_stmt_scanstatus_reset STMT
|
||||
*/
|
||||
static int test_stmt_scanstatus_reset(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt; /* First argument */
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "STMT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
sqlite3_stmt_scanstatus_reset(pStmt);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_next_stmt DB STMT
|
||||
**
|
||||
@@ -3715,6 +3638,7 @@ static int test_prepare_v2(
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zSql;
|
||||
char *zCopy = 0; /* malloc() copy of zSql */
|
||||
int bytes;
|
||||
const char *zTail = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
@@ -3730,7 +3654,21 @@ static int test_prepare_v2(
|
||||
zSql = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0);
|
||||
/* Instead of using zSql directly, make a copy into a buffer obtained
|
||||
** directly from malloc(). The idea is to make it easier for valgrind
|
||||
** to spot buffer overreads. */
|
||||
if( bytes>=0 ){
|
||||
zCopy = malloc(bytes);
|
||||
memcpy(zCopy, zSql, bytes);
|
||||
}else{
|
||||
int n = (int)strlen(zSql) + 1;
|
||||
zCopy = malloc(n);
|
||||
memcpy(zCopy, zSql, n);
|
||||
}
|
||||
rc = sqlite3_prepare_v2(db, zCopy, bytes, &pStmt, objc>=5 ? &zTail : 0);
|
||||
free(zCopy);
|
||||
zTail = &zSql[(zTail - zCopy)];
|
||||
|
||||
assert(rc==SQLITE_OK || pStmt==0);
|
||||
Tcl_ResetResult(interp);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
@@ -5752,10 +5690,11 @@ static int test_wal_checkpoint_v2(
|
||||
int nCkpt = -555;
|
||||
Tcl_Obj *pRet;
|
||||
|
||||
const char * aMode[] = { "passive", "full", "restart", 0 };
|
||||
const char * aMode[] = { "passive", "full", "restart", "truncate", 0 };
|
||||
assert( SQLITE_CHECKPOINT_PASSIVE==0 );
|
||||
assert( SQLITE_CHECKPOINT_FULL==1 );
|
||||
assert( SQLITE_CHECKPOINT_RESTART==2 );
|
||||
assert( SQLITE_CHECKPOINT_TRUNCATE==3 );
|
||||
|
||||
if( objc!=3 && objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?");
|
||||
@@ -6337,6 +6276,7 @@ static int tclLoadStaticExtensionCmd(
|
||||
){
|
||||
extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
@@ -6352,6 +6292,7 @@ static int tclLoadStaticExtensionCmd(
|
||||
} aExtension[] = {
|
||||
{ "amatch", sqlite3_amatch_init },
|
||||
{ "closure", sqlite3_closure_init },
|
||||
{ "eval", sqlite3_eval_init },
|
||||
{ "fileio", sqlite3_fileio_init },
|
||||
{ "fuzzer", sqlite3_fuzzer_init },
|
||||
{ "ieee754", sqlite3_ieee_init },
|
||||
@@ -6839,11 +6780,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_table_column_metadata", test_table_column_metadata, 0 },
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
{ "sqlite3_blob_read", test_blob_read, 0 },
|
||||
{ "sqlite3_blob_write", test_blob_write, 0 },
|
||||
{ "sqlite3_blob_reopen", test_blob_reopen, 0 },
|
||||
{ "sqlite3_blob_bytes", test_blob_bytes, 0 },
|
||||
{ "sqlite3_blob_close", test_blob_close, 0 },
|
||||
#endif
|
||||
{ "pcache_stats", test_pcache_stats, 0 },
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
@@ -6868,6 +6805,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_user_change", test_user_change, 0 },
|
||||
{ "sqlite3_user_delete", test_user_delete, 0 },
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
{ "sqlite3_stmt_scanstatus", test_stmt_scanstatus, 0 },
|
||||
{ "sqlite3_stmt_scanstatus_reset", test_stmt_scanstatus_reset, 0 },
|
||||
#endif
|
||||
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
|
||||
+319
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
** 2014 October 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* These functions are implemented in main.c. */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
/* From test1.c: */
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
extern void *sqlite3TestTextToPtr(const char *z);
|
||||
|
||||
/*
|
||||
** Return a pointer to a buffer containing a text representation of the
|
||||
** pointer passed as the only argument. The original pointer may be extracted
|
||||
** from the text using sqlite3TestTextToPtr().
|
||||
*/
|
||||
static char *ptrToText(void *p){
|
||||
static char buf[100];
|
||||
sqlite3_snprintf(sizeof(buf)-1, buf, "%p", p);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to extract a blob handle (type sqlite3_blob*) from the Tcl
|
||||
** object passed as the second argument. If successful, set *ppBlob to
|
||||
** point to the blob handle and return TCL_OK. Otherwise, store an error
|
||||
** message in the tcl interpreter and return TCL_ERROR. The final value
|
||||
** of *ppBlob is undefined in this case.
|
||||
**
|
||||
** If the object contains a string that begins with "incrblob_", then it
|
||||
** is assumed to be the name of a Tcl channel opened using the [db incrblob]
|
||||
** command (see tclsqlite.c). Otherwise, it is assumed to be a pointer
|
||||
** encoded using the ptrToText() routine or similar.
|
||||
*/
|
||||
static int blobHandleFromObj(
|
||||
Tcl_Interp *interp,
|
||||
Tcl_Obj *pObj,
|
||||
sqlite3_blob **ppBlob
|
||||
){
|
||||
char *z;
|
||||
int n;
|
||||
|
||||
z = Tcl_GetStringFromObj(pObj, &n);
|
||||
if( n==0 ){
|
||||
*ppBlob = 0;
|
||||
}else if( n>9 && 0==memcmp("incrblob_", z, 9) ){
|
||||
int notUsed;
|
||||
Tcl_Channel channel;
|
||||
ClientData instanceData;
|
||||
|
||||
channel = Tcl_GetChannel(interp, z, ¬Used);
|
||||
if( !channel ) return TCL_ERROR;
|
||||
|
||||
Tcl_Flush(channel);
|
||||
Tcl_Seek(channel, 0, SEEK_SET);
|
||||
|
||||
instanceData = Tcl_GetChannelInstanceData(channel);
|
||||
*ppBlob = *((sqlite3_blob **)instanceData);
|
||||
}else{
|
||||
*ppBlob = (sqlite3_blob*)sqlite3TestTextToPtr(z);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Like Tcl_GetString(), except that if the string is 0 bytes in size, a
|
||||
** NULL Pointer is returned.
|
||||
*/
|
||||
static char *blobStringFromObj(Tcl_Obj *pObj){
|
||||
int n;
|
||||
char *z;
|
||||
z = Tcl_GetStringFromObj(pObj, &n);
|
||||
return (n ? z : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_open DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME
|
||||
**
|
||||
** Tcl test harness for the sqlite3_blob_open() function.
|
||||
*/
|
||||
static int test_blob_open(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* Calling TCL interpreter */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zDb;
|
||||
const char *zTable;
|
||||
const char *zColumn;
|
||||
sqlite_int64 iRowid;
|
||||
int flags;
|
||||
const char *zVarname;
|
||||
int nVarname;
|
||||
|
||||
sqlite3_blob *pBlob = (sqlite3_blob*)0xFFFFFFFF;
|
||||
int rc;
|
||||
|
||||
if( objc!=8 ){
|
||||
const char *zUsage = "DB DATABASE TABLE COLUMN ROWID FLAGS VARNAME";
|
||||
Tcl_WrongNumArgs(interp, 1, objv, zUsage);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
zTable = blobStringFromObj(objv[3]);
|
||||
zColumn = Tcl_GetString(objv[4]);
|
||||
if( Tcl_GetWideIntFromObj(interp, objv[5], &iRowid) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[6], &flags) ) return TCL_ERROR;
|
||||
zVarname = Tcl_GetStringFromObj(objv[7], &nVarname);
|
||||
|
||||
if( nVarname>0 ){
|
||||
rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, &pBlob);
|
||||
Tcl_SetVar(interp, zVarname, ptrToText(pBlob), 0);
|
||||
}else{
|
||||
rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRowid, flags, 0);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_ResetResult(interp);
|
||||
}else{
|
||||
Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** sqlite3_blob_close HANDLE
|
||||
*/
|
||||
static int test_blob_close(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
rc = sqlite3_blob_close(pBlob);
|
||||
|
||||
if( rc ){
|
||||
Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_bytes HANDLE
|
||||
*/
|
||||
static int test_blob_bytes(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
nByte = sqlite3_blob_bytes(pBlob);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_read CHANNEL OFFSET N
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_read() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_read()
|
||||
** to read N bytes from offset OFFSET from the underlying SQLite
|
||||
** blob handle.
|
||||
**
|
||||
** On success, a byte-array object containing the read data is
|
||||
** returned. On failure, the interpreter result is set to the
|
||||
** text representation of the returned error code (i.e. "SQLITE_NOMEM")
|
||||
** and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_read(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int nByte;
|
||||
int iOffset;
|
||||
unsigned char *zBuf = 0;
|
||||
int rc;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
|
||||
){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( nByte>0 ){
|
||||
zBuf = (unsigned char *)Tcl_Alloc(nByte);
|
||||
}
|
||||
rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte));
|
||||
}else{
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
Tcl_Free((char *)zBuf);
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_blob_write HANDLE OFFSET DATA ?NDATA?
|
||||
**
|
||||
** This command is used to test the sqlite3_blob_write() in ways that
|
||||
** the Tcl channel interface does not. The first argument should
|
||||
** be the name of a valid channel created by the [incrblob] method
|
||||
** of a database handle. This function calls sqlite3_blob_write()
|
||||
** to write the DATA byte-array to the underlying SQLite blob handle.
|
||||
** at offset OFFSET.
|
||||
**
|
||||
** On success, an empty string is returned. On failure, the interpreter
|
||||
** result is set to the text representation of the returned error code
|
||||
** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown.
|
||||
*/
|
||||
static int test_blob_write(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_blob *pBlob;
|
||||
int iOffset;
|
||||
int rc;
|
||||
|
||||
unsigned char *zBuf;
|
||||
int nBuf;
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET DATA ?NDATA?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
|
||||
if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
}
|
||||
|
||||
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest_blob_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aObjCmd[] = {
|
||||
{ "sqlite3_blob_open", test_blob_open },
|
||||
{ "sqlite3_blob_close", test_blob_close },
|
||||
{ "sqlite3_blob_bytes", test_blob_bytes },
|
||||
{ "sqlite3_blob_read", test_blob_read },
|
||||
{ "sqlite3_blob_write", test_blob_write },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -155,6 +155,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "analyze", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
Tcl_SetVar2(interp, "sqlite_options", "api_armor", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "api_armor", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "atomicwrite", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -484,6 +490,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
Tcl_SetVar2(interp, "sqlite_options", "scanstatus", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "scanstatus", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
# if defined(__APPLE__)
|
||||
# define SQLITE_ENABLE_LOCKING_STYLE 1
|
||||
|
||||
+2
-2
@@ -431,8 +431,8 @@ void sqlite3Update(
|
||||
|
||||
/* Top of the update loop */
|
||||
if( okOnePass ){
|
||||
if( aToOpen[iDataCur-iBaseCur] ){
|
||||
assert( pPk!=0 );
|
||||
if( aToOpen[iDataCur-iBaseCur] && !isView ){
|
||||
assert( pPk );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
|
||||
+10
@@ -251,6 +251,11 @@ int sqlite3Dequote(char *z){
|
||||
*/
|
||||
int sqlite3_stricmp(const char *zLeft, const char *zRight){
|
||||
register unsigned char *a, *b;
|
||||
if( zLeft==0 ){
|
||||
return zRight ? -1 : 0;
|
||||
}else if( zRight==0 ){
|
||||
return 1;
|
||||
}
|
||||
a = (unsigned char *)zLeft;
|
||||
b = (unsigned char *)zRight;
|
||||
while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
|
||||
@@ -258,6 +263,11 @@ int sqlite3_stricmp(const char *zLeft, const char *zRight){
|
||||
}
|
||||
int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
|
||||
register unsigned char *a, *b;
|
||||
if( zLeft==0 ){
|
||||
return zRight ? -1 : 0;
|
||||
}else if( zRight==0 ){
|
||||
return 1;
|
||||
}
|
||||
a = (unsigned char *)zLeft;
|
||||
b = (unsigned char *)zRight;
|
||||
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
|
||||
** overwriting the database with the vacuumed content.
|
||||
**
|
||||
** Only 1x temporary space and only 1x writes would be required if
|
||||
** the copy of step (3) were replace by deleting the original database
|
||||
** the copy of step (3) were replaced by deleting the original database
|
||||
** and renaming the transient database as the original. But that will
|
||||
** not work if other processes are attached to the original database.
|
||||
** And a power loss in between deleting the original and renaming the
|
||||
|
||||
+42
-13
@@ -608,6 +608,9 @@ int sqlite3VdbeExec(
|
||||
#endif
|
||||
nVmStep++;
|
||||
pOp = &aOp[pc];
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
if( p->anExec ) p->anExec[pc]++;
|
||||
#endif
|
||||
|
||||
/* Only allow tracing if SQLITE_DEBUG is defined.
|
||||
*/
|
||||
@@ -2298,7 +2301,7 @@ case OP_Column: {
|
||||
pC->payloadSize = pC->szRow = avail = pReg->n;
|
||||
pC->aRow = (u8*)pReg->z;
|
||||
}else{
|
||||
MemSetTypeFlag(pDest, MEM_Null);
|
||||
sqlite3VdbeMemSetNull(pDest);
|
||||
goto op_column_out;
|
||||
}
|
||||
}else{
|
||||
@@ -2441,7 +2444,7 @@ case OP_Column: {
|
||||
if( pOp->p4type==P4_MEM ){
|
||||
sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
|
||||
}else{
|
||||
MemSetTypeFlag(pDest, MEM_Null);
|
||||
sqlite3VdbeMemSetNull(pDest);
|
||||
}
|
||||
goto op_column_out;
|
||||
}
|
||||
@@ -2633,7 +2636,10 @@ case OP_MakeRecord: {
|
||||
nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
|
||||
}while( (--pRec)>=pData0 );
|
||||
|
||||
/* Add the initial header varint and total the size */
|
||||
/* EVIDENCE-OF: R-22564-11647 The header begins with a single varint
|
||||
** which determines the total number of bytes in the header. The varint
|
||||
** value is the size of the header in bytes including the size varint
|
||||
** itself. */
|
||||
testcase( nHdr==126 );
|
||||
testcase( nHdr==127 );
|
||||
if( nHdr<=126 ){
|
||||
@@ -2667,7 +2673,11 @@ case OP_MakeRecord: {
|
||||
pRec = pData0;
|
||||
do{
|
||||
serial_type = pRec->uTemp;
|
||||
/* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more
|
||||
** additional varints, one per column. */
|
||||
i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
|
||||
/* EVIDENCE-OF: R-64536-51728 The values for each column in the record
|
||||
** immediately follow the header. */
|
||||
j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */
|
||||
}while( (++pRec)<=pLast );
|
||||
assert( i==nHdr );
|
||||
@@ -2822,11 +2832,18 @@ case OP_Savepoint: {
|
||||
db->isTransactionSavepoint = 0;
|
||||
rc = p->rc;
|
||||
}else{
|
||||
int isSchemaChange;
|
||||
iSavepoint = db->nSavepoint - iSavepoint - 1;
|
||||
if( p1==SAVEPOINT_ROLLBACK ){
|
||||
isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
|
||||
for(ii=0; ii<db->nDb; ii++){
|
||||
sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT);
|
||||
rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt,
|
||||
SQLITE_ABORT_ROLLBACK,
|
||||
isSchemaChange==0);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
}
|
||||
}else{
|
||||
isSchemaChange = 0;
|
||||
}
|
||||
for(ii=0; ii<db->nDb; ii++){
|
||||
rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
|
||||
@@ -2834,7 +2851,7 @@ case OP_Savepoint: {
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
}
|
||||
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
|
||||
if( isSchemaChange ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ResetAllSchemasOfConnection(db);
|
||||
db->flags = (db->flags | SQLITE_InternChanges);
|
||||
@@ -3231,7 +3248,7 @@ case OP_OpenWrite: {
|
||||
|| p->readOnly==0 );
|
||||
|
||||
if( p->expired ){
|
||||
rc = SQLITE_ABORT;
|
||||
rc = SQLITE_ABORT_ROLLBACK;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3795,10 +3812,10 @@ case OP_Found: { /* jump, in3 */
|
||||
}else{
|
||||
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
|
||||
pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
|
||||
);
|
||||
);
|
||||
if( pIdxKey==0 ) goto no_mem;
|
||||
assert( pIn3->flags & MEM_Blob );
|
||||
assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
|
||||
ExpandBlob(pIn3);
|
||||
sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
|
||||
}
|
||||
pIdxKey->default_rc = 0;
|
||||
@@ -4398,6 +4415,10 @@ case OP_Rowid: { /* out2-prerelease */
|
||||
assert( pC->pCursor!=0 );
|
||||
rc = sqlite3VdbeCursorRestore(pC);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( pC->nullRow ){
|
||||
pOut->flags = MEM_Null;
|
||||
break;
|
||||
}
|
||||
rc = sqlite3BtreeKeySize(pC->pCursor, &v);
|
||||
assert( rc==SQLITE_OK ); /* Always so because of CursorRestore() above */
|
||||
}
|
||||
@@ -4488,9 +4509,9 @@ case OP_Sort: { /* jump */
|
||||
**
|
||||
** The next use of the Rowid or Column or Next instruction for P1
|
||||
** will refer to the first entry in the database table or index.
|
||||
** If the table or index is empty and P2>0, then jump immediately to P2.
|
||||
** If P2 is 0 or if the table or index is not empty, fall through
|
||||
** to the following instruction.
|
||||
** If the table or index is empty, jump immediately to P2.
|
||||
** If the table or index is not empty, fall through to the following
|
||||
** instruction.
|
||||
**
|
||||
** This opcode leaves the cursor configured to move in forward order,
|
||||
** from the beginning toward the end. In other words, the cursor is
|
||||
@@ -5406,6 +5427,9 @@ case OP_Program: { /* jump */
|
||||
pFrame->token = pProgram->token;
|
||||
pFrame->aOnceFlag = p->aOnceFlag;
|
||||
pFrame->nOnceFlag = p->nOnceFlag;
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
pFrame->anExec = p->anExec;
|
||||
#endif
|
||||
|
||||
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
|
||||
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
|
||||
@@ -5423,6 +5447,7 @@ case OP_Program: { /* jump */
|
||||
pFrame->pParent = p->pFrame;
|
||||
pFrame->lastRowid = lastRowid;
|
||||
pFrame->nChange = p->nChange;
|
||||
pFrame->nDbChange = p->db->nChange;
|
||||
p->nChange = 0;
|
||||
p->pFrame = pFrame;
|
||||
p->aMem = aMem = &VdbeFrameMem(pFrame)[-1];
|
||||
@@ -5433,6 +5458,9 @@ case OP_Program: { /* jump */
|
||||
p->nOp = pProgram->nOp;
|
||||
p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
|
||||
p->nOnceFlag = pProgram->nOnce;
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
p->anExec = 0;
|
||||
#endif
|
||||
pc = -1;
|
||||
memset(p->aOnceFlag, 0, p->nOnceFlag);
|
||||
|
||||
@@ -5677,8 +5705,8 @@ case OP_AggFinal: {
|
||||
/* Opcode: Checkpoint P1 P2 P3 * *
|
||||
**
|
||||
** Checkpoint database P1. This is a no-op if P1 is not currently in
|
||||
** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL
|
||||
** or RESTART. Write 1 or 0 into mem[P3] if the checkpoint returns
|
||||
** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL,
|
||||
** RESTART, or TRUNCATE. Write 1 or 0 into mem[P3] if the checkpoint returns
|
||||
** SQLITE_BUSY or not, respectively. Write the number of pages in the
|
||||
** WAL after the checkpoint into mem[P3+1] and the number of pages
|
||||
** in the WAL that have been checkpointed after the checkpoint
|
||||
@@ -5696,6 +5724,7 @@ case OP_Checkpoint: {
|
||||
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
|
||||
|| pOp->p2==SQLITE_CHECKPOINT_FULL
|
||||
|| pOp->p2==SQLITE_CHECKPOINT_RESTART
|
||||
|| pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
|
||||
);
|
||||
rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
|
||||
if( rc==SQLITE_BUSY ){
|
||||
|
||||
@@ -282,4 +282,10 @@ void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
|
||||
# define VDBE_OFFSET_LINENO(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*);
|
||||
#else
|
||||
# define sqlite3VdbeScanStatus(a,b,c,d,e)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+18
-1
@@ -132,6 +132,7 @@ struct VdbeFrame {
|
||||
Vdbe *v; /* VM this frame belongs to */
|
||||
VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
|
||||
Op *aOp; /* Program instructions for parent frame */
|
||||
i64 *anExec; /* Event counters from parent frame */
|
||||
Mem *aMem; /* Array of memory cells for parent frame */
|
||||
u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */
|
||||
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
|
||||
@@ -144,7 +145,8 @@ struct VdbeFrame {
|
||||
int nOnceFlag; /* Number of entries in aOnceFlag */
|
||||
int nChildMem; /* Number of memory cells for child frame */
|
||||
int nChildCsr; /* Number of cursors for child frame */
|
||||
int nChange; /* Statement changes (Vdbe.nChanges) */
|
||||
int nChange; /* Statement changes (Vdbe.nChange) */
|
||||
int nDbChange; /* Value of db->nChange */
|
||||
};
|
||||
|
||||
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
|
||||
@@ -295,6 +297,16 @@ struct Explain {
|
||||
*/
|
||||
typedef unsigned bft; /* Bit Field Type */
|
||||
|
||||
typedef struct ScanStatus ScanStatus;
|
||||
struct ScanStatus {
|
||||
int addrExplain; /* OP_Explain for loop */
|
||||
int addrLoop; /* Address of "loops" counter */
|
||||
int addrVisit; /* Address of "rows visited" counter */
|
||||
int iSelectID; /* The "Select-ID" for this loop */
|
||||
LogEst nEst; /* Estimated output rows per loop */
|
||||
char *zName; /* Name of table or index */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the virtual machine. This structure contains the complete
|
||||
** state of the virtual machine.
|
||||
@@ -367,6 +379,11 @@ struct Vdbe {
|
||||
int nOnceFlag; /* Size of array aOnceFlag[] */
|
||||
u8 *aOnceFlag; /* Flags for OP_Once */
|
||||
AuxData *pAuxData; /* Linked list of auxdata allocations */
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
i64 *anExec; /* Number of times each op has been executed */
|
||||
int nScan; /* Entries in aScan[] */
|
||||
ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+95
-5
@@ -966,11 +966,19 @@ static const void *columnName(
|
||||
const void *(*xFunc)(Mem*),
|
||||
int useType
|
||||
){
|
||||
const void *ret = 0;
|
||||
Vdbe *p = (Vdbe *)pStmt;
|
||||
const void *ret;
|
||||
Vdbe *p;
|
||||
int n;
|
||||
sqlite3 *db = p->db;
|
||||
|
||||
sqlite3 *db;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( pStmt==0 ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
ret = 0;
|
||||
p = (Vdbe *)pStmt;
|
||||
db = p->db;
|
||||
assert( db!=0 );
|
||||
n = sqlite3_column_count(pStmt);
|
||||
if( N<n && N>=0 ){
|
||||
@@ -1435,6 +1443,12 @@ int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
|
||||
*/
|
||||
sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
|
||||
sqlite3_stmt *pNext;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(pDb) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
sqlite3_mutex_enter(pDb->mutex);
|
||||
if( pStmt==0 ){
|
||||
pNext = (sqlite3_stmt*)pDb->pVdbe;
|
||||
@@ -1450,7 +1464,83 @@ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
|
||||
*/
|
||||
int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
|
||||
Vdbe *pVdbe = (Vdbe*)pStmt;
|
||||
u32 v = pVdbe->aCounter[op];
|
||||
u32 v;
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !pStmt ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
v = pVdbe->aCounter[op];
|
||||
if( resetFlag ) pVdbe->aCounter[op] = 0;
|
||||
return (int)v;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
/*
|
||||
** Return status data for a single loop within query pStmt.
|
||||
*/
|
||||
int sqlite3_stmt_scanstatus(
|
||||
sqlite3_stmt *pStmt, /* Prepared statement being queried */
|
||||
int idx, /* Index of loop to report on */
|
||||
int iScanStatusOp, /* Which metric to return */
|
||||
void *pOut /* OUT: Write the answer here */
|
||||
){
|
||||
Vdbe *p = (Vdbe*)pStmt;
|
||||
ScanStatus *pScan;
|
||||
if( idx<0 || idx>=p->nScan ) return 1;
|
||||
pScan = &p->aScan[idx];
|
||||
switch( iScanStatusOp ){
|
||||
case SQLITE_SCANSTAT_NLOOP: {
|
||||
*(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop];
|
||||
break;
|
||||
}
|
||||
case SQLITE_SCANSTAT_NVISIT: {
|
||||
*(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit];
|
||||
break;
|
||||
}
|
||||
case SQLITE_SCANSTAT_EST: {
|
||||
double r = 1.0;
|
||||
LogEst x = pScan->nEst;
|
||||
while( x<100 ){
|
||||
x += 10;
|
||||
r *= 0.5;
|
||||
}
|
||||
*(double*)pOut = r*sqlite3LogEstToInt(x);
|
||||
break;
|
||||
}
|
||||
case SQLITE_SCANSTAT_NAME: {
|
||||
*(const char**)pOut = pScan->zName;
|
||||
break;
|
||||
}
|
||||
case SQLITE_SCANSTAT_EXPLAIN: {
|
||||
if( pScan->addrExplain ){
|
||||
*(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z;
|
||||
}else{
|
||||
*(const char**)pOut = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_SCANSTAT_SELECTID: {
|
||||
if( pScan->addrExplain ){
|
||||
*(int*)pOut = p->aOp[ pScan->addrExplain ].p1;
|
||||
}else{
|
||||
*(int*)pOut = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
|
||||
*/
|
||||
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
|
||||
Vdbe *p = (Vdbe*)pStmt;
|
||||
memset(p->anExec, 0, p->nOp * sizeof(i64));
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
|
||||
|
||||
+74
-6
@@ -597,6 +597,34 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){
|
||||
return addr;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
||||
/*
|
||||
** Add an entry to the array of counters managed by sqlite3_stmt_scanstatus().
|
||||
*/
|
||||
void sqlite3VdbeScanStatus(
|
||||
Vdbe *p, /* VM to add scanstatus() to */
|
||||
int addrExplain, /* Address of OP_Explain (or 0) */
|
||||
int addrLoop, /* Address of loop counter */
|
||||
int addrVisit, /* Address of rows visited counter */
|
||||
LogEst nEst, /* Estimated number of output rows */
|
||||
const char *zName /* Name of table or index being scanned */
|
||||
){
|
||||
int nByte = (p->nScan+1) * sizeof(ScanStatus);
|
||||
ScanStatus *aNew;
|
||||
aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
|
||||
if( aNew ){
|
||||
ScanStatus *pNew = &aNew[p->nScan++];
|
||||
pNew->addrExplain = addrExplain;
|
||||
pNew->addrLoop = addrLoop;
|
||||
pNew->addrVisit = addrVisit;
|
||||
pNew->nEst = nEst;
|
||||
pNew->zName = sqlite3DbStrDup(p->db, zName);
|
||||
p->aScan = aNew;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Change the value of the P1 operand for a specific instruction.
|
||||
** This routine is useful when a large program is loaded from a
|
||||
@@ -1026,6 +1054,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
CollSeq *pColl = pKeyInfo->aColl[j];
|
||||
const char *zColl = pColl ? pColl->zName : "nil";
|
||||
int n = sqlite3Strlen30(zColl);
|
||||
assert( pColl==0 || sqlite3ValidCollSeq(pColl) );
|
||||
if( n==6 && memcmp(zColl,"BINARY",6)==0 ){
|
||||
zColl = "B";
|
||||
n = 1;
|
||||
@@ -1695,6 +1724,9 @@ void sqlite3VdbeMakeReady(
|
||||
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
|
||||
&zCsr, zEnd, &nByte);
|
||||
p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
p->anExec = allocSpace(p->anExec, p->nOp*sizeof(i64), &zCsr, zEnd, &nByte);
|
||||
#endif
|
||||
if( nByte ){
|
||||
p->pFree = sqlite3DbMallocZero(db, nByte);
|
||||
}
|
||||
@@ -1711,7 +1743,7 @@ void sqlite3VdbeMakeReady(
|
||||
p->aVar[n].db = db;
|
||||
}
|
||||
}
|
||||
if( p->azVar ){
|
||||
if( p->azVar && pParse->nzVar>0 ){
|
||||
p->nzVar = pParse->nzVar;
|
||||
memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0]));
|
||||
memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0]));
|
||||
@@ -1762,6 +1794,9 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
|
||||
*/
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
|
||||
Vdbe *v = pFrame->v;
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
v->anExec = pFrame->anExec;
|
||||
#endif
|
||||
v->aOnceFlag = pFrame->aOnceFlag;
|
||||
v->nOnceFlag = pFrame->nOnceFlag;
|
||||
v->aOp = pFrame->aOp;
|
||||
@@ -1772,6 +1807,7 @@ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
|
||||
v->nCursor = pFrame->nCursor;
|
||||
v->db->lastRowid = pFrame->lastRowid;
|
||||
v->nChange = pFrame->nChange;
|
||||
v->db->nChange = pFrame->nDbChange;
|
||||
return pFrame->pc;
|
||||
}
|
||||
|
||||
@@ -2339,6 +2375,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
|
||||
sqlite3CloseSavepoints(db);
|
||||
db->autoCommit = 1;
|
||||
p->nChange = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2379,6 +2416,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
p->rc = rc;
|
||||
sqlite3RollbackAll(db, SQLITE_OK);
|
||||
p->nChange = 0;
|
||||
}else{
|
||||
db->nDeferredCons = 0;
|
||||
db->nDeferredImmCons = 0;
|
||||
@@ -2387,6 +2425,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}
|
||||
}else{
|
||||
sqlite3RollbackAll(db, SQLITE_OK);
|
||||
p->nChange = 0;
|
||||
}
|
||||
db->nStatement = 0;
|
||||
}else if( eStatementOp==0 ){
|
||||
@@ -2398,6 +2437,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
|
||||
sqlite3CloseSavepoints(db);
|
||||
db->autoCommit = 1;
|
||||
p->nChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2418,6 +2458,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
|
||||
sqlite3CloseSavepoints(db);
|
||||
db->autoCommit = 1;
|
||||
p->nChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2679,6 +2720,12 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
|
||||
sqlite3DbFree(db, p->aColName);
|
||||
sqlite3DbFree(db, p->zSql);
|
||||
sqlite3DbFree(db, p->pFree);
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
for(i=0; i<p->nScan; i++){
|
||||
sqlite3DbFree(db, p->aScan[i].zName);
|
||||
}
|
||||
sqlite3DbFree(db, p->aScan);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2837,9 +2884,7 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
|
||||
i64 i = pMem->u.i;
|
||||
u64 u;
|
||||
if( i<0 ){
|
||||
if( i<(-MAX_6BYTE) ) return 6;
|
||||
/* Previous test prevents: u = -(-9223372036854775808) */
|
||||
u = -i;
|
||||
u = ~i;
|
||||
}else{
|
||||
u = i;
|
||||
}
|
||||
@@ -3005,10 +3050,14 @@ static u32 SQLITE_NOINLINE serialGet(
|
||||
u32 y = FOUR_BYTE_UINT(buf+4);
|
||||
x = (x<<32) + y;
|
||||
if( serial_type==6 ){
|
||||
/* EVIDENCE-OF: R-29851-52272 Value is a big-endian 64-bit
|
||||
** twos-complement integer. */
|
||||
pMem->u.i = *(i64*)&x;
|
||||
pMem->flags = MEM_Int;
|
||||
testcase( pMem->u.i<0 );
|
||||
}else{
|
||||
/* EVIDENCE-OF: R-57343-49114 Value is a big-endian IEEE 754-2008 64-bit
|
||||
** floating point number. */
|
||||
#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT)
|
||||
/* Verify that integers and floating point values use the same
|
||||
** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
|
||||
@@ -3036,35 +3085,46 @@ u32 sqlite3VdbeSerialGet(
|
||||
switch( serial_type ){
|
||||
case 10: /* Reserved for future use */
|
||||
case 11: /* Reserved for future use */
|
||||
case 0: { /* NULL */
|
||||
case 0: { /* Null */
|
||||
/* EVIDENCE-OF: R-24078-09375 Value is a NULL. */
|
||||
pMem->flags = MEM_Null;
|
||||
break;
|
||||
}
|
||||
case 1: { /* 1-byte signed integer */
|
||||
case 1: {
|
||||
/* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement
|
||||
** integer. */
|
||||
pMem->u.i = ONE_BYTE_INT(buf);
|
||||
pMem->flags = MEM_Int;
|
||||
testcase( pMem->u.i<0 );
|
||||
return 1;
|
||||
}
|
||||
case 2: { /* 2-byte signed integer */
|
||||
/* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit
|
||||
** twos-complement integer. */
|
||||
pMem->u.i = TWO_BYTE_INT(buf);
|
||||
pMem->flags = MEM_Int;
|
||||
testcase( pMem->u.i<0 );
|
||||
return 2;
|
||||
}
|
||||
case 3: { /* 3-byte signed integer */
|
||||
/* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit
|
||||
** twos-complement integer. */
|
||||
pMem->u.i = THREE_BYTE_INT(buf);
|
||||
pMem->flags = MEM_Int;
|
||||
testcase( pMem->u.i<0 );
|
||||
return 3;
|
||||
}
|
||||
case 4: { /* 4-byte signed integer */
|
||||
/* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit
|
||||
** twos-complement integer. */
|
||||
pMem->u.i = FOUR_BYTE_INT(buf);
|
||||
pMem->flags = MEM_Int;
|
||||
testcase( pMem->u.i<0 );
|
||||
return 4;
|
||||
}
|
||||
case 5: { /* 6-byte signed integer */
|
||||
/* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit
|
||||
** twos-complement integer. */
|
||||
pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf);
|
||||
pMem->flags = MEM_Int;
|
||||
testcase( pMem->u.i<0 );
|
||||
@@ -3078,11 +3138,17 @@ u32 sqlite3VdbeSerialGet(
|
||||
}
|
||||
case 8: /* Integer 0 */
|
||||
case 9: { /* Integer 1 */
|
||||
/* EVIDENCE-OF: R-12976-22893 Value is the integer 0. */
|
||||
/* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */
|
||||
pMem->u.i = serial_type-8;
|
||||
pMem->flags = MEM_Int;
|
||||
return 0;
|
||||
}
|
||||
default: {
|
||||
/* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in
|
||||
** length.
|
||||
** EVIDENCE-OF: R-28401-00140 Value is a string in the text encoding and
|
||||
** (N-13)/2 bytes in length. */
|
||||
static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem };
|
||||
pMem->z = (char *)buf;
|
||||
pMem->n = (serial_type-12)/2;
|
||||
@@ -3293,6 +3359,7 @@ static int vdbeCompareMemString(
|
||||
const CollSeq *pColl,
|
||||
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
|
||||
){
|
||||
assert( sqlite3ValidCollSeq(pColl) );
|
||||
if( pMem1->enc==pColl->enc ){
|
||||
/* The strings are already in the correct encoding. Call the
|
||||
** comparison function directly */
|
||||
@@ -3408,6 +3475,7 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
|
||||
assert( !pColl || pColl->xCmp );
|
||||
|
||||
if( pColl ){
|
||||
assert( sqlite3ValidCollSeq(pColl) );
|
||||
return vdbeCompareMemString(pMem1, pMem2, pColl, 0);
|
||||
}
|
||||
/* If a NULL pointer was passed as the collate function, fall through
|
||||
|
||||
+6
-2
@@ -153,6 +153,11 @@ int sqlite3_blob_open(
|
||||
Parse *pParse = 0;
|
||||
Incrblob *pBlob = 0;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || ppBlob==0 || zTable==0 ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
flags = !!flags; /* flags = (flags ? 1 : 0); */
|
||||
*ppBlob = 0;
|
||||
|
||||
@@ -371,7 +376,6 @@ static int blobReadWrite(
|
||||
if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){
|
||||
/* Request is out of range. Return a transient error. */
|
||||
rc = SQLITE_ERROR;
|
||||
sqlite3Error(db, SQLITE_ERROR);
|
||||
}else if( v==0 ){
|
||||
/* If there is no statement handle, then the blob-handle has
|
||||
** already been invalidated. Return SQLITE_ABORT in this case.
|
||||
@@ -389,10 +393,10 @@ static int blobReadWrite(
|
||||
sqlite3VdbeFinalize(v);
|
||||
p->pStmt = 0;
|
||||
}else{
|
||||
db->errCode = rc;
|
||||
v->rc = rc;
|
||||
}
|
||||
}
|
||||
sqlite3Error(db, rc);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
|
||||
+12
-7
@@ -100,7 +100,7 @@
|
||||
** The sorter is running in multi-threaded mode if (a) the library was built
|
||||
** with pre-processor symbol SQLITE_MAX_WORKER_THREADS set to a value greater
|
||||
** than zero, and (b) worker threads have been enabled at runtime by calling
|
||||
** sqlite3_config(SQLITE_CONFIG_WORKER_THREADS, ...).
|
||||
** "PRAGMA threads=N" with some value of N greater than 0.
|
||||
**
|
||||
** When Rewind() is called, any data remaining in memory is flushed to a
|
||||
** final PMA. So at this point the data is stored in some number of sorted
|
||||
@@ -147,6 +147,13 @@
|
||||
# define SQLITE_DEBUG_SORTER_THREADS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Hard-coded maximum amount of data to accumulate in memory before flushing
|
||||
** to a level 0 PMA. The purpose of this limit is to prevent various integer
|
||||
** overflows. 512MiB.
|
||||
*/
|
||||
#define SQLITE_MAX_MXPMASIZE (1<<29)
|
||||
|
||||
/*
|
||||
** Private objects used by the sorter
|
||||
*/
|
||||
@@ -845,13 +852,11 @@ int sqlite3VdbeSorterInit(
|
||||
pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz;
|
||||
mxCache = db->aDb[0].pSchema->cache_size;
|
||||
if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
|
||||
pSorter->mxPmaSize = mxCache * pgsz;
|
||||
pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_MXPMASIZE);
|
||||
|
||||
/* If the application has not configure scratch memory using
|
||||
** SQLITE_CONFIG_SCRATCH then we assume it is OK to do large memory
|
||||
** allocations. If scratch memory has been configured, then assume
|
||||
** large memory allocations should be avoided to prevent heap
|
||||
** fragmentation.
|
||||
/* EVIDENCE-OF: R-26747-61719 When the application provides any amount of
|
||||
** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary
|
||||
** large heap allocations.
|
||||
*/
|
||||
if( sqlite3GlobalConfig.pScratch==0 ){
|
||||
assert( pSorter->iMemory==0 );
|
||||
|
||||
+21
-2
@@ -81,6 +81,9 @@ int sqlite3_create_module(
|
||||
const sqlite3_module *pModule, /* The definition of the module */
|
||||
void *pAux /* Context pointer for xCreate/xConnect */
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
return createModule(db, zName, pModule, pAux, 0);
|
||||
}
|
||||
|
||||
@@ -94,6 +97,9 @@ int sqlite3_create_module_v2(
|
||||
void *pAux, /* Context pointer for xCreate/xConnect */
|
||||
void (*xDestroy)(void *) /* Module destructor function */
|
||||
){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
return createModule(db, zName, pModule, pAux, xDestroy);
|
||||
}
|
||||
|
||||
@@ -326,7 +332,12 @@ void sqlite3VtabBeginParse(
|
||||
addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
|
||||
addModuleArgument(db, pTable, 0);
|
||||
addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName));
|
||||
pParse->sNameToken.n = (int)(&pModuleName->z[pModuleName->n] - pName1->z);
|
||||
assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0)
|
||||
|| (pParse->sNameToken.z==pName1->z && pName2->z==0)
|
||||
);
|
||||
pParse->sNameToken.n = (int)(
|
||||
&pModuleName->z[pModuleName->n] - pParse->sNameToken.z
|
||||
);
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
/* Creating a virtual table invokes the authorization callback twice.
|
||||
@@ -698,6 +709,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
Table *pTab;
|
||||
char *zErr = 0;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){
|
||||
sqlite3Error(db, SQLITE_MISUSE);
|
||||
@@ -1054,6 +1068,9 @@ int sqlite3_vtab_on_conflict(sqlite3 *db){
|
||||
static const unsigned char aMap[] = {
|
||||
SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE
|
||||
};
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 );
|
||||
assert( OE_Ignore==4 && OE_Replace==5 );
|
||||
assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 );
|
||||
@@ -1069,8 +1086,10 @@ int sqlite3_vtab_config(sqlite3 *db, int op, ...){
|
||||
va_list ap;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
va_start(ap, op);
|
||||
switch( op ){
|
||||
case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
|
||||
|
||||
@@ -1504,7 +1504,7 @@ static void walMergesort(
|
||||
** Free an iterator allocated by walIteratorInit().
|
||||
*/
|
||||
static void walIteratorFree(WalIterator *p){
|
||||
sqlite3ScratchFree(p);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1539,7 +1539,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
|
||||
nByte = sizeof(WalIterator)
|
||||
+ (nSegment-1)*sizeof(struct WalSegment)
|
||||
+ iLast*sizeof(ht_slot);
|
||||
p = (WalIterator *)sqlite3ScratchMalloc(nByte);
|
||||
p = (WalIterator *)sqlite3_malloc(nByte);
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@@ -1549,7 +1549,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
|
||||
/* Allocate temporary space used by the merge-sort routine. This block
|
||||
** of memory will be freed before this function returns.
|
||||
*/
|
||||
aTmp = (ht_slot *)sqlite3ScratchMalloc(
|
||||
aTmp = (ht_slot *)sqlite3_malloc(
|
||||
sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
|
||||
);
|
||||
if( !aTmp ){
|
||||
@@ -1586,7 +1586,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
|
||||
p->aSegment[i].aPgno = (u32 *)aPgno;
|
||||
}
|
||||
}
|
||||
sqlite3ScratchFree(aTmp);
|
||||
sqlite3_free(aTmp);
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
walIteratorFree(p);
|
||||
@@ -1623,6 +1623,38 @@ static int walPagesize(Wal *pWal){
|
||||
return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
|
||||
}
|
||||
|
||||
/*
|
||||
** The following is guaranteed when this function is called:
|
||||
**
|
||||
** a) the WRITER lock is held,
|
||||
** b) the entire log file has been checkpointed, and
|
||||
** c) any existing readers are reading exclusively from the database
|
||||
** file - there are no readers that may attempt to read a frame from
|
||||
** the log file.
|
||||
**
|
||||
** This function updates the shared-memory structures so that the next
|
||||
** client to write to the database (which may be this one) does so by
|
||||
** writing frames into the start of the log file.
|
||||
**
|
||||
** The value of parameter salt1 is used as the aSalt[1] value in the
|
||||
** new wal-index header. It should be passed a pseudo-random value (i.e.
|
||||
** one obtained from sqlite3_randomness()).
|
||||
*/
|
||||
static void walRestartHdr(Wal *pWal, u32 salt1){
|
||||
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
|
||||
int i; /* Loop counter */
|
||||
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
|
||||
pWal->nCkpt++;
|
||||
pWal->hdr.mxFrame = 0;
|
||||
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
|
||||
memcpy(&pWal->hdr.aSalt[1], &salt1, 4);
|
||||
walIndexWriteHdr(pWal);
|
||||
pInfo->nBackfill = 0;
|
||||
pInfo->aReadMark[1] = 0;
|
||||
for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
|
||||
assert( pInfo->aReadMark[0]==0 );
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy as much content as we can from the WAL back into the database file
|
||||
** in response to an sqlite3_wal_checkpoint() request or the equivalent.
|
||||
@@ -1657,7 +1689,7 @@ static int walPagesize(Wal *pWal){
|
||||
static int walCheckpoint(
|
||||
Wal *pWal, /* Wal connection */
|
||||
int eMode, /* One of PASSIVE, FULL or RESTART */
|
||||
int (*xBusyCall)(void*), /* Function to call when busy */
|
||||
int (*xBusy)(void*), /* Function to call when busy */
|
||||
void *pBusyArg, /* Context argument for xBusyHandler */
|
||||
int sync_flags, /* Flags for OsSync() (or 0) */
|
||||
u8 *zBuf /* Temporary buffer to use */
|
||||
@@ -1671,7 +1703,6 @@ static int walCheckpoint(
|
||||
u32 mxPage; /* Max database page to write */
|
||||
int i; /* Loop counter */
|
||||
volatile WalCkptInfo *pInfo; /* The checkpoint status information */
|
||||
int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */
|
||||
|
||||
szPage = walPagesize(pWal);
|
||||
testcase( szPage<=32768 );
|
||||
@@ -1686,7 +1717,9 @@ static int walCheckpoint(
|
||||
}
|
||||
assert( pIter );
|
||||
|
||||
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall;
|
||||
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
|
||||
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
|
||||
assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
|
||||
|
||||
/* Compute in mxSafeFrame the index of the last frame of the WAL that is
|
||||
** safe to write into the database. Frames beyond mxSafeFrame might
|
||||
@@ -1775,19 +1808,38 @@ static int walCheckpoint(
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
/* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal
|
||||
** file has been copied into the database file, then block until all
|
||||
** readers have finished using the wal file. This ensures that the next
|
||||
** process to write to the database restarts the wal file.
|
||||
/* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
|
||||
** entire wal file has been copied into the database file, then block
|
||||
** until all readers have finished using the wal file. This ensures that
|
||||
** the next process to write to the database restarts the wal file.
|
||||
*/
|
||||
if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
|
||||
assert( pWal->writeLock );
|
||||
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
|
||||
rc = SQLITE_BUSY;
|
||||
}else if( eMode==SQLITE_CHECKPOINT_RESTART ){
|
||||
}else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
|
||||
u32 salt1;
|
||||
sqlite3_randomness(4, &salt1);
|
||||
assert( mxSafeFrame==pWal->hdr.mxFrame );
|
||||
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){
|
||||
/* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as
|
||||
** SQLITE_CHECKPOINT_RESTART with the addition that it also
|
||||
** truncates the log file to zero bytes just prior to a
|
||||
** successful return.
|
||||
**
|
||||
** In theory, it might be safe to do this without updating the
|
||||
** wal-index header in shared memory, as all subsequent reader or
|
||||
** writer clients should see that the entire log file has been
|
||||
** checkpointed and behave accordingly. This seems unsafe though,
|
||||
** as it would leave the system in a state where the contents of
|
||||
** the wal-index header do not match the contents of the
|
||||
** file-system. To avoid this, update the wal-index header to
|
||||
** indicate that the log file contains zero valid frames. */
|
||||
walRestartHdr(pWal, salt1);
|
||||
rc = sqlite3OsTruncate(pWal->pWalFd, 0);
|
||||
}
|
||||
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
|
||||
}
|
||||
}
|
||||
@@ -2525,7 +2577,6 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
|
||||
}
|
||||
if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
|
||||
}
|
||||
assert( rc==SQLITE_OK );
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2574,7 +2625,6 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called just before writing a set of frames to the log
|
||||
** file (see sqlite3WalFrames()). It checks to see if, instead of appending
|
||||
@@ -2607,20 +2657,8 @@ static int walRestartLog(Wal *pWal){
|
||||
** In theory it would be Ok to update the cache of the header only
|
||||
** at this point. But updating the actual wal-index header is also
|
||||
** safe and means there is no special case for sqlite3WalUndo()
|
||||
** to handle if this transaction is rolled back.
|
||||
*/
|
||||
int i; /* Loop counter */
|
||||
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
|
||||
|
||||
pWal->nCkpt++;
|
||||
pWal->hdr.mxFrame = 0;
|
||||
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
|
||||
aSalt[1] = salt1;
|
||||
walIndexWriteHdr(pWal);
|
||||
pInfo->nBackfill = 0;
|
||||
pInfo->aReadMark[1] = 0;
|
||||
for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
|
||||
assert( pInfo->aReadMark[0]==0 );
|
||||
** to handle if this transaction is rolled back. */
|
||||
walRestartHdr(pWal, salt1);
|
||||
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
|
||||
}else if( rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
@@ -2908,7 +2946,7 @@ int sqlite3WalFrames(
|
||||
*/
|
||||
int sqlite3WalCheckpoint(
|
||||
Wal *pWal, /* Wal connection */
|
||||
int eMode, /* PASSIVE, FULL or RESTART */
|
||||
int eMode, /* PASSIVE, FULL, RESTART, or TRUNCATE */
|
||||
int (*xBusy)(void*), /* Function to call when busy */
|
||||
void *pBusyArg, /* Context argument for xBusyHandler */
|
||||
int sync_flags, /* Flags to sync db file with (or 0) */
|
||||
@@ -2920,29 +2958,42 @@ int sqlite3WalCheckpoint(
|
||||
int rc; /* Return code */
|
||||
int isChanged = 0; /* True if a new wal-index header is loaded */
|
||||
int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
|
||||
int (*xBusy2)(void*) = xBusy; /* Busy handler for eMode2 */
|
||||
|
||||
assert( pWal->ckptLock==0 );
|
||||
assert( pWal->writeLock==0 );
|
||||
|
||||
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
|
||||
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
|
||||
assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
|
||||
|
||||
if( pWal->readOnly ) return SQLITE_READONLY;
|
||||
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
|
||||
|
||||
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
|
||||
** "checkpoint" lock on the database file. */
|
||||
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
|
||||
if( rc ){
|
||||
/* Usually this is SQLITE_BUSY meaning that another thread or process
|
||||
** is already running a checkpoint, or maybe a recovery. But it might
|
||||
** also be SQLITE_IOERR. */
|
||||
/* EVIDENCE-OF: R-10421-19736 If any other process is running a
|
||||
** checkpoint operation at the same time, the lock cannot be obtained and
|
||||
** SQLITE_BUSY is returned.
|
||||
** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
|
||||
** it will not be invoked in this case.
|
||||
*/
|
||||
testcase( rc==SQLITE_BUSY );
|
||||
testcase( xBusy!=0 );
|
||||
return rc;
|
||||
}
|
||||
pWal->ckptLock = 1;
|
||||
|
||||
/* If this is a blocking-checkpoint, then obtain the write-lock as well
|
||||
** to prevent any writers from running while the checkpoint is underway.
|
||||
** This has to be done before the call to walIndexReadHdr() below.
|
||||
/* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
|
||||
** TRUNCATE modes also obtain the exclusive "writer" lock on the database
|
||||
** file.
|
||||
**
|
||||
** If the writer lock cannot be obtained, then a passive checkpoint is
|
||||
** run instead. Since the checkpointer is not holding the writer lock,
|
||||
** there is no point in blocking waiting for any readers. Assuming no
|
||||
** other error occurs, this function will return SQLITE_BUSY to the caller.
|
||||
** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
|
||||
** immediately, and a busy-handler is configured, it is invoked and the
|
||||
** writer lock retried until either the busy-handler returns 0 or the
|
||||
** lock is successfully obtained.
|
||||
*/
|
||||
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
|
||||
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
|
||||
@@ -2950,6 +3001,7 @@ int sqlite3WalCheckpoint(
|
||||
pWal->writeLock = 1;
|
||||
}else if( rc==SQLITE_BUSY ){
|
||||
eMode2 = SQLITE_CHECKPOINT_PASSIVE;
|
||||
xBusy2 = 0;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
@@ -2967,7 +3019,7 @@ int sqlite3WalCheckpoint(
|
||||
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf);
|
||||
rc = walCheckpoint(pWal, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
|
||||
}
|
||||
|
||||
/* If no error occurred, set the output variables. */
|
||||
|
||||
+135
-47
@@ -1651,7 +1651,7 @@ static void constructAutomaticIndex(
|
||||
** if they go out of sync.
|
||||
*/
|
||||
extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
|
||||
mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol;
|
||||
mxBitCol = MIN(BMS-1,pTable->nCol);
|
||||
testcase( pTable->nCol==BMS-1 );
|
||||
testcase( pTable->nCol==BMS-2 );
|
||||
for(i=0; i<mxBitCol; i++){
|
||||
@@ -1660,7 +1660,6 @@ static void constructAutomaticIndex(
|
||||
if( pSrc->colUsed & MASKBIT(BMS-1) ){
|
||||
nKeyCol += pTable->nCol - BMS + 1;
|
||||
}
|
||||
pLoop->wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY;
|
||||
|
||||
/* Construct the Index object to describe this index */
|
||||
pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed);
|
||||
@@ -1898,7 +1897,6 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
||||
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
/*
|
||||
** Estimate the location of a particular key among all keys in an
|
||||
@@ -1907,9 +1905,10 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
|
||||
** aStat[0] Est. number of rows less than pVal
|
||||
** aStat[1] Est. number of rows equal to pVal
|
||||
**
|
||||
** Return SQLITE_OK on success.
|
||||
** Return the index of the sample that is the smallest sample that
|
||||
** is greater than or equal to pRec.
|
||||
*/
|
||||
static void whereKeyStats(
|
||||
static int whereKeyStats(
|
||||
Parse *pParse, /* Database connection */
|
||||
Index *pIdx, /* Index to consider domain of */
|
||||
UnpackedRecord *pRec, /* Vector of values to consider */
|
||||
@@ -1991,6 +1990,7 @@ static void whereKeyStats(
|
||||
}
|
||||
aStat[0] = iLower + iGap;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
|
||||
@@ -2141,7 +2141,7 @@ static int whereRangeSkipScanEst(
|
||||
** If either of the upper or lower bound is not present, then NULL is passed in
|
||||
** place of the corresponding WhereTerm.
|
||||
**
|
||||
** The value in (pBuilder->pNew->u.btree.nEq) is the index of the index
|
||||
** The value in (pBuilder->pNew->u.btree.nEq) is the number of the index
|
||||
** column subject to the range constraint. Or, equivalently, the number of
|
||||
** equality constraints optimized by the proposed index scan. For example,
|
||||
** assuming index p is on t1(a, b), and the SQL query is:
|
||||
@@ -2157,7 +2157,7 @@ static int whereRangeSkipScanEst(
|
||||
**
|
||||
** When this function is called, *pnOut is set to the sqlite3LogEst() of the
|
||||
** number of rows that the index scan is expected to visit without
|
||||
** considering the range constraints. If nEq is 0, this is the number of
|
||||
** considering the range constraints. If nEq is 0, then *pnOut is the number of
|
||||
** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced)
|
||||
** to account for the range constraints pLower and pUpper.
|
||||
**
|
||||
@@ -2181,9 +2181,7 @@ static int whereRangeScanEst(
|
||||
Index *p = pLoop->u.btree.pIndex;
|
||||
int nEq = pLoop->u.btree.nEq;
|
||||
|
||||
if( p->nSample>0
|
||||
&& nEq<p->nSampleCol
|
||||
){
|
||||
if( p->nSample>0 && nEq<p->nSampleCol ){
|
||||
if( nEq==pBuilder->nRecValid ){
|
||||
UnpackedRecord *pRec = pBuilder->pRec;
|
||||
tRowcnt a[2];
|
||||
@@ -2199,15 +2197,19 @@ static int whereRangeScanEst(
|
||||
** is not a simple variable or literal value), the lower bound of the
|
||||
** range is $P. Due to a quirk in the way whereKeyStats() works, even
|
||||
** if $L is available, whereKeyStats() is called for both ($P) and
|
||||
** ($P:$L) and the larger of the two returned values used.
|
||||
** ($P:$L) and the larger of the two returned values is used.
|
||||
**
|
||||
** Similarly, iUpper is to be set to the estimate of the number of rows
|
||||
** less than the upper bound of the range query. Where the upper bound
|
||||
** is either ($P) or ($P:$U). Again, even if $U is available, both values
|
||||
** of iUpper are requested of whereKeyStats() and the smaller used.
|
||||
**
|
||||
** The number of rows between the two bounds is then just iUpper-iLower.
|
||||
*/
|
||||
tRowcnt iLower;
|
||||
tRowcnt iUpper;
|
||||
tRowcnt iLower; /* Rows less than the lower bound */
|
||||
tRowcnt iUpper; /* Rows less than the upper bound */
|
||||
int iLwrIdx = -2; /* aSample[] for the lower bound */
|
||||
int iUprIdx = -1; /* aSample[] for the upper bound */
|
||||
|
||||
if( pRec ){
|
||||
testcase( pRec->nField!=pBuilder->nRecValid );
|
||||
@@ -2245,7 +2247,7 @@ static int whereRangeScanEst(
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
|
||||
if( rc==SQLITE_OK && bOk ){
|
||||
tRowcnt iNew;
|
||||
whereKeyStats(pParse, p, pRec, 0, a);
|
||||
iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a);
|
||||
iNew = a[0] + ((pLower->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
|
||||
if( iNew>iLower ) iLower = iNew;
|
||||
nOut--;
|
||||
@@ -2260,7 +2262,7 @@ static int whereRangeScanEst(
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
|
||||
if( rc==SQLITE_OK && bOk ){
|
||||
tRowcnt iNew;
|
||||
whereKeyStats(pParse, p, pRec, 1, a);
|
||||
iUprIdx = whereKeyStats(pParse, p, pRec, 1, a);
|
||||
iNew = a[0] + ((pUpper->eOperator & (WO_GT|WO_LE)) ? a[1] : 0);
|
||||
if( iNew<iUpper ) iUpper = iNew;
|
||||
nOut--;
|
||||
@@ -2272,6 +2274,11 @@ static int whereRangeScanEst(
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iUpper>iLower ){
|
||||
nNew = sqlite3LogEst(iUpper - iLower);
|
||||
/* TUNING: If both iUpper and iLower are derived from the same
|
||||
** sample, then assume they are 4x more selective. This brings
|
||||
** the estimated selectivity more in line with what it would be
|
||||
** if estimated without the use of STAT3/4 tables. */
|
||||
if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
|
||||
}else{
|
||||
nNew = 10; assert( 10==sqlite3LogEst(2) );
|
||||
}
|
||||
@@ -2809,11 +2816,14 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){
|
||||
|
||||
/*
|
||||
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
|
||||
** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single
|
||||
** record is added to the output to describe the table scan strategy in
|
||||
** pLevel.
|
||||
** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was
|
||||
** defined at compile-time. If it is not a no-op, a single OP_Explain opcode
|
||||
** is added to the output to describe the table scan strategy in pLevel.
|
||||
**
|
||||
** If an OP_Explain opcode is added to the VM, its address is returned.
|
||||
** Otherwise, if no OP_Explain is coded, zero is returned.
|
||||
*/
|
||||
static void explainOneScan(
|
||||
static int explainOneScan(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pTabList, /* Table list this loop refers to */
|
||||
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
|
||||
@@ -2821,7 +2831,8 @@ static void explainOneScan(
|
||||
int iFrom, /* Value for "from" column of output */
|
||||
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
|
||||
){
|
||||
#ifndef SQLITE_DEBUG
|
||||
int ret = 0;
|
||||
#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
||||
if( pParse->explain==2 )
|
||||
#endif
|
||||
{
|
||||
@@ -2838,7 +2849,7 @@ static void explainOneScan(
|
||||
|
||||
pLoop = pLevel->pWLoop;
|
||||
flags = pLoop->wsFlags;
|
||||
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return;
|
||||
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0;
|
||||
|
||||
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|
||||
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|
||||
@@ -2910,13 +2921,46 @@ static void explainOneScan(
|
||||
}
|
||||
#endif
|
||||
zMsg = sqlite3StrAccumFinish(&str);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
# define explainOneScan(u,v,w,x,y,z)
|
||||
# define explainOneScan(u,v,w,x,y,z) 0
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
/*
|
||||
** Configure the VM passed as the first argument with an
|
||||
** sqlite3_stmt_scanstatus() entry corresponding to the scan used to
|
||||
** implement level pLvl. Argument pSrclist is a pointer to the FROM
|
||||
** clause that the scan reads data from.
|
||||
**
|
||||
** If argument addrExplain is not 0, it must be the address of an
|
||||
** OP_Explain instruction that describes the same loop.
|
||||
*/
|
||||
static void addScanStatus(
|
||||
Vdbe *v, /* Vdbe to add scanstatus entry to */
|
||||
SrcList *pSrclist, /* FROM clause pLvl reads data from */
|
||||
WhereLevel *pLvl, /* Level to add scanstatus() entry for */
|
||||
int addrExplain /* Address of OP_Explain (or 0) */
|
||||
){
|
||||
const char *zObj = 0;
|
||||
WhereLoop *pLoop = pLvl->pWLoop;
|
||||
if( (pLoop->wsFlags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
|
||||
zObj = pLoop->u.btree.pIndex->zName;
|
||||
}else{
|
||||
zObj = pSrclist->a[pLvl->iFrom].zName;
|
||||
}
|
||||
sqlite3VdbeScanStatus(
|
||||
v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
|
||||
);
|
||||
}
|
||||
#else
|
||||
# define addScanStatus(a, b, c, d) ((void)d)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Generate code for the start of the iLevel-th loop in the WHERE clause
|
||||
@@ -3547,10 +3591,9 @@ static Bitmask codeOneLoopStart(
|
||||
Expr *pExpr = pWC->a[iTerm].pExpr;
|
||||
if( &pWC->a[iTerm] == pTerm ) continue;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
|
||||
if( pWC->a[iTerm].wtFlags & (TERM_ORINFO|TERM_VIRTUAL) ) continue;
|
||||
if( (pWC->a[iTerm].wtFlags & TERM_VIRTUAL)!=0 ) continue;
|
||||
if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
|
||||
testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
|
||||
pExpr = sqlite3ExprDup(db, pExpr, 0);
|
||||
pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
|
||||
}
|
||||
@@ -3583,9 +3626,11 @@ static Bitmask codeOneLoopStart(
|
||||
assert( pSubWInfo || pParse->nErr || db->mallocFailed );
|
||||
if( pSubWInfo ){
|
||||
WhereLoop *pSubLoop;
|
||||
explainOneScan(
|
||||
int addrExplain = explainOneScan(
|
||||
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
|
||||
);
|
||||
addScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
|
||||
|
||||
/* This is the sub-WHERE clause body. First skip over
|
||||
** duplicate rows from prior sub-WHERE clauses, and record the
|
||||
** rowid (or PRIMARY KEY) for the current row so that the same
|
||||
@@ -3716,6 +3761,10 @@ static Bitmask codeOneLoopStart(
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
pLevel->addrVisit = sqlite3VdbeCurrentAddr(v);
|
||||
#endif
|
||||
|
||||
/* Insert code to test every subexpression that can be completely
|
||||
** computed using the current set of tables.
|
||||
*/
|
||||
@@ -3966,10 +4015,11 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if both of the following are true:
|
||||
** Return TRUE if all of the following are true:
|
||||
**
|
||||
** (1) X has the same or lower cost that Y
|
||||
** (2) X is a proper subset of Y
|
||||
** (3) X skips at least as many columns as Y
|
||||
**
|
||||
** By "proper subset" we mean that X uses fewer WHERE clause terms
|
||||
** than Y and that every WHERE clause term used by X is also used
|
||||
@@ -3977,7 +4027,9 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
|
||||
**
|
||||
** If X is a proper subset of Y then Y is a better choice and ought
|
||||
** to have a lower cost. This routine returns TRUE when that cost
|
||||
** relationship is inverted and needs to be adjusted.
|
||||
** relationship is inverted and needs to be adjusted. The third rule
|
||||
** was added because if X uses skip-scan less than Y it still might
|
||||
** deserve a lower cost even if it is a proper subset of Y.
|
||||
*/
|
||||
static int whereLoopCheaperProperSubset(
|
||||
const WhereLoop *pX, /* First WhereLoop to compare */
|
||||
@@ -3987,6 +4039,7 @@ static int whereLoopCheaperProperSubset(
|
||||
if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
|
||||
return 0; /* X is not a subset of Y */
|
||||
}
|
||||
if( pY->nSkip > pX->nSkip ) return 0;
|
||||
if( pX->rRun >= pY->rRun ){
|
||||
if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */
|
||||
if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */
|
||||
@@ -4022,9 +4075,7 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
|
||||
if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
|
||||
if( whereLoopCheaperProperSubset(p, pTemplate) ){
|
||||
/* Adjust pTemplate cost downward so that it is cheaper than its
|
||||
** subset p. Except, do not adjust the cost estimate downward for
|
||||
** a loop that skips more columns. */
|
||||
if( pTemplate->nSkip>p->nSkip ) continue;
|
||||
** subset p. */
|
||||
WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n",
|
||||
pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1));
|
||||
pTemplate->rRun = p->rRun;
|
||||
@@ -4078,8 +4129,9 @@ static WhereLoop **whereLoopFindLesser(
|
||||
|
||||
/* Any loop using an appliation-defined index (or PRIMARY KEY or
|
||||
** UNIQUE constraint) with one or more == constraints is better
|
||||
** than an automatic index. */
|
||||
** than an automatic index. Unless it is a skip-scan. */
|
||||
if( (p->wsFlags & WHERE_AUTO_INDEX)!=0
|
||||
&& (pTemplate->nSkip)==0
|
||||
&& (pTemplate->wsFlags & WHERE_INDEXED)!=0
|
||||
&& (pTemplate->wsFlags & WHERE_COLUMN_EQ)!=0
|
||||
&& (p->prereq & pTemplate->prereq)==pTemplate->prereq
|
||||
@@ -4238,10 +4290,30 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
|
||||
** Adjust the WhereLoop.nOut value downward to account for terms of the
|
||||
** WHERE clause that reference the loop but which are not used by an
|
||||
** index.
|
||||
*
|
||||
** For every WHERE clause term that is not used by the index
|
||||
** and which has a truth probability assigned by one of the likelihood(),
|
||||
** likely(), or unlikely() SQL functions, reduce the estimated number
|
||||
** of output rows by the probability specified.
|
||||
**
|
||||
** In the current implementation, the first extra WHERE clause term reduces
|
||||
** the number of output rows by a factor of 10 and each additional term
|
||||
** reduces the number of output rows by sqrt(2).
|
||||
** TUNING: For every WHERE clause term that is not used by the index
|
||||
** and which does not have an assigned truth probability, heuristics
|
||||
** described below are used to try to estimate the truth probability.
|
||||
** TODO --> Perhaps this is something that could be improved by better
|
||||
** table statistics.
|
||||
**
|
||||
** Heuristic 1: Estimate the truth probability as 93.75%. The 93.75%
|
||||
** value corresponds to -1 in LogEst notation, so this means decrement
|
||||
** the WhereLoop.nOut field for every such WHERE clause term.
|
||||
**
|
||||
** Heuristic 2: If there exists one or more WHERE clause terms of the
|
||||
** form "x==EXPR" and EXPR is not a constant 0 or 1, then make sure the
|
||||
** final output row estimate is no greater than 1/4 of the total number
|
||||
** of rows in the table. In other words, assume that x==EXPR will filter
|
||||
** out at least 3 out of 4 rows. If EXPR is -1 or 0 or 1, then maybe the
|
||||
** "x" column is boolean or else -1 or 0 or 1 is a common default value
|
||||
** on the "x" column and so in that case only cap the output row estimate
|
||||
** at 1/2 instead of 1/4.
|
||||
*/
|
||||
static void whereLoopOutputAdjust(
|
||||
WhereClause *pWC, /* The WHERE clause */
|
||||
@@ -4250,9 +4322,10 @@ static void whereLoopOutputAdjust(
|
||||
){
|
||||
WhereTerm *pTerm, *pX;
|
||||
Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf);
|
||||
int i, j;
|
||||
int nEq = 0; /* Number of = constraints not within likely()/unlikely() */
|
||||
int i, j, k;
|
||||
LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */
|
||||
|
||||
assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
|
||||
for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){
|
||||
if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break;
|
||||
if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
|
||||
@@ -4265,20 +4338,26 @@ static void whereLoopOutputAdjust(
|
||||
}
|
||||
if( j<0 ){
|
||||
if( pTerm->truthProb<=0 ){
|
||||
/* If a truth probability is specified using the likelihood() hints,
|
||||
** then use the probability provided by the application. */
|
||||
pLoop->nOut += pTerm->truthProb;
|
||||
}else{
|
||||
/* In the absence of explicit truth probabilities, use heuristics to
|
||||
** guess a reasonable truth probability. */
|
||||
pLoop->nOut--;
|
||||
if( pTerm->eOperator&WO_EQ ) nEq++;
|
||||
if( pTerm->eOperator&WO_EQ ){
|
||||
Expr *pRight = pTerm->pExpr->pRight;
|
||||
if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){
|
||||
k = 10;
|
||||
}else{
|
||||
k = 20;
|
||||
}
|
||||
if( iReduce<k ) iReduce = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* TUNING: If there is at least one equality constraint in the WHERE
|
||||
** clause that does not have a likelihood() explicitly assigned to it
|
||||
** then do not let the estimated number of output rows exceed half
|
||||
** the number of rows in the table. */
|
||||
if( nEq && pLoop->nOut>nRow-10 ){
|
||||
pLoop->nOut = nRow - 10;
|
||||
}
|
||||
if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4535,6 +4614,7 @@ static int whereLoopAddBtreeIndex(
|
||||
assert( 42==sqlite3LogEst(18) );
|
||||
if( saved_nEq==saved_nSkip
|
||||
&& saved_nEq+1<pProbe->nKeyCol
|
||||
&& pProbe->noSkipScan==0
|
||||
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
|
||||
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
|
||||
){
|
||||
@@ -6417,7 +6497,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
*/
|
||||
notReady = ~(Bitmask)0;
|
||||
for(ii=0; ii<nTabList; ii++){
|
||||
int addrExplain;
|
||||
int wsFlags;
|
||||
pLevel = &pWInfo->a[ii];
|
||||
wsFlags = pLevel->pWLoop->wsFlags;
|
||||
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
|
||||
constructAutomaticIndex(pParse, &pWInfo->sWC,
|
||||
@@ -6425,10 +6508,15 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( db->mallocFailed ) goto whereBeginError;
|
||||
}
|
||||
#endif
|
||||
explainOneScan(pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags);
|
||||
addrExplain = explainOneScan(
|
||||
pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
|
||||
);
|
||||
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
|
||||
notReady = codeOneLoopStart(pWInfo, ii, notReady);
|
||||
pWInfo->iContinue = pLevel->addrCont;
|
||||
if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){
|
||||
addScanStatus(v, pTabList, pLevel, addrExplain);
|
||||
}
|
||||
}
|
||||
|
||||
/* Done. */
|
||||
|
||||
@@ -85,6 +85,9 @@ struct WhereLevel {
|
||||
} u;
|
||||
struct WhereLoop *pWLoop; /* The selected WhereLoop object */
|
||||
Bitmask notReady; /* FROM entries not usable at this level */
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
int addrVisit; /* Address at which row is visited */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+4
-4
@@ -86,23 +86,23 @@ do_test 2.1 {
|
||||
# range.
|
||||
#
|
||||
# Test 3.2 is a little unstable. It depends on the planner estimating
|
||||
# that (b BETWEEN 50 AND 54) will match more rows than (c BETWEEN
|
||||
# that (b BETWEEN 30 AND 34) will match more rows than (c BETWEEN
|
||||
# 800000 AND 900000). Which is a pretty close call (50 vs. 32), so
|
||||
# the planner could get it wrong with an unlucky set of samples. This
|
||||
# case happens to work, but others ("b BETWEEN 40 AND 44" for example)
|
||||
# will fail.
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 50 AND 54;
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 30 AND 34;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 100000;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 800000 AND 900000;
|
||||
} {50 376 32}
|
||||
do_test 3.1 {
|
||||
eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000}
|
||||
eqp {SELECT * FROM t1 WHERE b BETWEEN 30 AND 34 AND c BETWEEN 0 AND 100000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
|
||||
do_test 3.2 {
|
||||
eqp {SELECT * FROM t1
|
||||
WHERE b BETWEEN 50 AND 54 AND c BETWEEN 800000 AND 900000}
|
||||
WHERE b BETWEEN 30 AND 34 AND c BETWEEN 800000 AND 900000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
|
||||
do_test 3.3 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 0 AND 100000}
|
||||
|
||||
@@ -224,48 +224,4 @@ do_execsql_test autoindex2-120 {
|
||||
# on t3 and reordering the tables so that t3 was in the outer loop and
|
||||
# implementing the ORDER BY clause using a B-Tree.
|
||||
|
||||
do_execsql_test autoindex2-120 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT
|
||||
t1_id,
|
||||
t1.did,
|
||||
param2,
|
||||
param3,
|
||||
t1.ptime,
|
||||
t1.trange,
|
||||
t1.exmass,
|
||||
t1.mass,
|
||||
t1.vstatus,
|
||||
type,
|
||||
subtype,
|
||||
t1.deviation,
|
||||
t1.formula,
|
||||
dparam1,
|
||||
reserve1,
|
||||
reserve2,
|
||||
param4,
|
||||
t1.last_operation,
|
||||
t1.admin_uuid,
|
||||
t1.previous_value,
|
||||
t1.job_id,
|
||||
client_did,
|
||||
t1.last_t1,
|
||||
t1.data_t1,
|
||||
t1.previous_date,
|
||||
param5,
|
||||
param6,
|
||||
mgr_uuid
|
||||
FROM
|
||||
t3,
|
||||
t2,
|
||||
t1
|
||||
WHERE
|
||||
t1.ptime > 1393520400
|
||||
AND param3<>9001
|
||||
AND t3.flg7 = 1
|
||||
AND t1.did = t2.did
|
||||
AND t2.uid = t3.uid
|
||||
ORDER BY t1.ptime desc LIMIT 500;
|
||||
} {0 0 2 {SEARCH TABLE t1 USING INDEX t1x1 (ptime>?)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2x0 (did=?)} 0 2 0 {SEARCH TABLE t3 USING INDEX t3x0 (uid=?)}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix autoindex3
|
||||
|
||||
# The t1b and t2d indexes are not very selective. It used to be that
|
||||
# the autoindex mechanism would create automatic indexes on t1(b) or
|
||||
@@ -54,5 +55,38 @@ do_execsql_test autoindex3-140 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d IN (5,b) AND x=y;
|
||||
} {/AUTO/}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 210 {
|
||||
CREATE TABLE v(b, d, e);
|
||||
CREATE TABLE u(a, b, c);
|
||||
ANALYZE sqlite_master;
|
||||
INSERT INTO "sqlite_stat1" VALUES('u','uab','40000 400 1');
|
||||
INSERT INTO "sqlite_stat1" VALUES('v','vbde','40000 400 1 1');
|
||||
INSERT INTO "sqlite_stat1" VALUES('v','ve','40000 21');
|
||||
|
||||
CREATE INDEX uab on u(a, b);
|
||||
CREATE INDEX ve on v(e);
|
||||
CREATE INDEX vbde on v(b,d,e);
|
||||
|
||||
DROP TABLE IF EXISTS sqlite_stat4;
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
|
||||
# At one point, SQLite was using the inferior plan:
|
||||
#
|
||||
# 0|0|1|SEARCH TABLE v USING INDEX ve (e>?)
|
||||
# 0|1|0|SEARCH TABLE u USING COVERING INDEX uab (ANY(a) AND b=?)
|
||||
#
|
||||
# on the basis that the real index "uab" must be better than the automatic
|
||||
# index. This is not right - a skip-scan is not necessarily better than an
|
||||
# automatic index scan.
|
||||
#
|
||||
do_eqp_test 220 {
|
||||
select count(*) from u, v where u.b = v.b and v.e > 34;
|
||||
} {
|
||||
0 0 1 {SEARCH TABLE v USING INDEX ve (e>?)}
|
||||
0 1 0 {SEARCH TABLE u USING AUTOMATIC COVERING INDEX (b=?)}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -217,6 +217,7 @@ foreach nPagePerStep {1 200} {
|
||||
INSERT INTO ${file_dest}.t1 VALUES(1, randstr(1000,1000))
|
||||
" $db_dest
|
||||
}
|
||||
execsql COMMIT $db_dest
|
||||
}
|
||||
|
||||
# Backup the source database.
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# 2014 November 13
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix backup5
|
||||
|
||||
forcedelete test2.db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t2 VALUES(1, 1);
|
||||
INSERT INTO t2 VALUES(2, 2);
|
||||
INSERT INTO t2 VALUES(3, 3);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
forcecopy test.db test.db2
|
||||
db eval {
|
||||
DROP TABLE t2;
|
||||
INSERT INTO t1 VALUES(zeroblob(1000), zeroblob(1000));
|
||||
INSERT INTO t1 VALUES(randomblob(1000), randomblob(1000));
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.2 {
|
||||
sqlite3 db2 test.db2
|
||||
set stmt [sqlite3_prepare_v2 db2 "SELECT * FROM t2" -1 dummy]
|
||||
sqlite3_step $stmt
|
||||
} {SQLITE_ROW}
|
||||
|
||||
do_test 1.3 {
|
||||
list [catch { sqlite3_backup B db2 main db main } msg] $msg
|
||||
} {1 {sqlite3_backup_init() failed}}
|
||||
|
||||
do_test 1.4 {
|
||||
sqlite3_errmsg db2
|
||||
} {destination database is in use}
|
||||
|
||||
do_test 1.5 {
|
||||
sqlite3_reset $stmt
|
||||
sqlite3_backup B db2 main db main
|
||||
B step 200
|
||||
B finish
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test 1.6 {
|
||||
list [sqlite3_step $stmt] [sqlite3_finalize $stmt]
|
||||
} {SQLITE_ERROR SQLITE_ERROR}
|
||||
|
||||
do_test 1.7 {
|
||||
sqlite3_errmsg db2
|
||||
} {no such table: t2}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,43 @@
|
||||
# 2014 November 26
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix bigsort
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# At one point there was an overflow problem if the product of the
|
||||
# cache-size and page-size was larger than 2^31. Causing an infinite
|
||||
# loop if the product was also an integer multiple of 2^32, or
|
||||
# inefficiency otherwise.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(a, b);
|
||||
BEGIN;
|
||||
WITH data(x,y) AS (
|
||||
SELECT 1, zeroblob(10000)
|
||||
UNION ALL
|
||||
SELECT x+1, y FROM data WHERE x < 300000
|
||||
)
|
||||
INSERT INTO t1 SELECT * FROM data;
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 1.1 {
|
||||
PRAGMA cache_size = 4194304;
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# 2014-11-27
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains test cases for b-tree logic.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix btree01
|
||||
|
||||
# The refactoring on the b-tree balance() routine in check-in
|
||||
# http://www.sqlite.org/src/info/face33bea1ba3a (2014-10-27)
|
||||
# caused the integrity_check on the following SQL to fail.
|
||||
#
|
||||
do_execsql_test btree01-1.1 {
|
||||
PRAGMA page_size=65536;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6500) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(3000);
|
||||
UPDATE t1 SET b=zeroblob(64000) WHERE a=2;
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
# The previous test is sufficient to prevent a regression. But we
|
||||
# add a number of additional tests to stress the balancer in similar
|
||||
# ways, looking for related problems.
|
||||
#
|
||||
for {set i 1} {$i<=30} {incr i} {
|
||||
do_test btree01-1.2.$i {
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6500) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(3000);
|
||||
UPDATE t1 SET b=zeroblob(64000) WHERE a=$::i;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
}
|
||||
for {set i 1} {$i<=30} {incr i} {
|
||||
do_test btree01-1.3.$i {
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6500) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(2000);
|
||||
UPDATE t1 SET b=zeroblob(64000) WHERE a=$::i;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
}
|
||||
for {set i 1} {$i<=30} {incr i} {
|
||||
do_test btree01-1.4.$i {
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6500) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(6499) WHERE (a%3)==0;
|
||||
UPDATE t1 SET b=zeroblob(6499) WHERE (a%3)==1;
|
||||
UPDATE t1 SET b=zeroblob(6499) WHERE (a%3)==2;
|
||||
UPDATE t1 SET b=zeroblob(64000) WHERE a=$::i;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
}
|
||||
for {set i 1} {$i<=30} {incr i} {
|
||||
do_test btree01-1.5.$i {
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6542) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(2331);
|
||||
UPDATE t1 SET b=zeroblob(65496) WHERE a=$::i;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
}
|
||||
for {set i 1} {$i<=30} {incr i} {
|
||||
do_test btree01-1.6.$i {
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6542) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(2332);
|
||||
UPDATE t1 SET b=zeroblob(65496) WHERE a=$::i;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
}
|
||||
for {set i 1} {$i<=30} {incr i} {
|
||||
do_test btree01-1.7.$i {
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<30)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6500) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(1);
|
||||
UPDATE t1 SET b=zeroblob(65000) WHERE a=$::i;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
}
|
||||
for {set i 1} {$i<=31} {incr i} {
|
||||
do_test btree01-1.8.$i {
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<31)
|
||||
INSERT INTO t1(a,b) SELECT i, zeroblob(6500) FROM c;
|
||||
UPDATE t1 SET b=zeroblob(4000);
|
||||
UPDATE t1 SET b=zeroblob(65000) WHERE a=$::i;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
}
|
||||
|
||||
finish_test
|
||||
+3
-3
@@ -912,7 +912,7 @@ do_test capi3-11.9.3 {
|
||||
} 1
|
||||
do_test capi3-11.10 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_ERROR}
|
||||
} {SQLITE_ROW}
|
||||
ifcapable !autoreset {
|
||||
# If SQLITE_OMIT_AUTORESET is defined, then the statement must be
|
||||
# reset() before it can be passed to step() again.
|
||||
@@ -921,11 +921,11 @@ ifcapable !autoreset {
|
||||
}
|
||||
do_test capi3-11.11 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_ROW}
|
||||
} {SQLITE_DONE}
|
||||
do_test capi3-11.12 {
|
||||
sqlite3_step $STMT
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_DONE}
|
||||
} {SQLITE_ROW}
|
||||
do_test capi3-11.13 {
|
||||
sqlite3_finalize $STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
+3
-3
@@ -863,7 +863,7 @@ do_test capi3c-11.9.3 {
|
||||
} 1
|
||||
do_test capi3c-11.10 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_ABORT}
|
||||
} {SQLITE_ROW}
|
||||
ifcapable !autoreset {
|
||||
# If SQLITE_OMIT_AUTORESET is defined, then the statement must be
|
||||
# reset() before it can be passed to step() again.
|
||||
@@ -872,11 +872,11 @@ ifcapable !autoreset {
|
||||
}
|
||||
do_test capi3c-11.11 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_ROW}
|
||||
} {SQLITE_DONE}
|
||||
do_test capi3c-11.12 {
|
||||
sqlite3_step $STMT
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_DONE}
|
||||
} {SQLITE_ROW}
|
||||
do_test capi3c-11.13 {
|
||||
sqlite3_finalize $STMT
|
||||
} {SQLITE_OK}
|
||||
|
||||
@@ -155,7 +155,6 @@ do_execsql_test capi3d-4.1 {
|
||||
}
|
||||
|
||||
do_test capi3d-4.2.1 {
|
||||
breakpoint
|
||||
set ::s1 [sqlite3_prepare_v2 db "ROLLBACK" -1 notused]
|
||||
sqlite3_step $::s1
|
||||
} {SQLITE_DONE}
|
||||
|
||||
@@ -64,7 +64,6 @@ do_test 1.2 {
|
||||
} {}
|
||||
|
||||
do_test 1.3 {
|
||||
breakpoint
|
||||
db eval { PRAGMA secure_delete=1 }
|
||||
list [catch {
|
||||
db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
|
||||
|
||||
@@ -222,4 +222,34 @@ do_execsql_test 4.1 {
|
||||
SELECT quote(x) FROM t2 ORDER BY 1;
|
||||
} {'xyzzy' X'0000000000'}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Ticket [c5ea805691bfc4204b1cb9e9aa0103bd48bc7d34] (2014-12-04)
|
||||
# Make sure that DISTINCT works together with ORDER BY and descending
|
||||
# indexes.
|
||||
#
|
||||
do_execsql_test 5.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1(x) VALUES(3),(1),(5),(2),(6),(4),(5),(1),(3);
|
||||
CREATE INDEX t1x ON t1(x DESC);
|
||||
SELECT DISTINCT x FROM t1 ORDER BY x ASC;
|
||||
} {1 2 3 4 5 6}
|
||||
do_execsql_test 5.2 {
|
||||
SELECT DISTINCT x FROM t1 ORDER BY x DESC;
|
||||
} {6 5 4 3 2 1}
|
||||
do_execsql_test 5.3 {
|
||||
SELECT DISTINCT x FROM t1 ORDER BY x;
|
||||
} {1 2 3 4 5 6}
|
||||
do_execsql_test 5.4 {
|
||||
DROP INDEX t1x;
|
||||
CREATE INDEX t1x ON t1(x ASC);
|
||||
SELECT DISTINCT x FROM t1 ORDER BY x ASC;
|
||||
} {1 2 3 4 5 6}
|
||||
do_execsql_test 5.5 {
|
||||
SELECT DISTINCT x FROM t1 ORDER BY x DESC;
|
||||
} {6 5 4 3 2 1}
|
||||
do_execsql_test 5.6 {
|
||||
SELECT DISTINCT x FROM t1 ORDER BY x;
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# 2014 October 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_blobbytes
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE q1(r INTEGER PRIMARY KEY, s TEXT);
|
||||
WITH d(a, b) AS (
|
||||
SELECT 0, ''
|
||||
UNION ALL
|
||||
SELECT a+1, b||'.' FROM d WHERE a<10000
|
||||
)
|
||||
INSERT INTO q1 SELECT * FROM d;
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-07796-55423 Returns the size in bytes of the BLOB
|
||||
# accessible via the successfully opened BLOB handle in its only
|
||||
# argument.
|
||||
#
|
||||
proc check_blob_size {tn rowid bytes} {
|
||||
uplevel [list do_test $tn [subst -nocommands {
|
||||
sqlite3_blob_open db main q1 s $rowid 0 B
|
||||
set res [sqlite3_blob_bytes [set B]]
|
||||
sqlite3_blob_close [set B]
|
||||
set res
|
||||
}] $bytes]
|
||||
}
|
||||
check_blob_size 1.1 43 43
|
||||
check_blob_size 1.2 391 391
|
||||
check_blob_size 1.3 6349 6349
|
||||
check_blob_size 1.4 2621 2621
|
||||
check_blob_size 1.5 7771 7771
|
||||
check_blob_size 1.6 7949 7949
|
||||
check_blob_size 1.7 4374 4374
|
||||
check_blob_size 1.8 2578 2578
|
||||
check_blob_size 1.9 7004 7004
|
||||
check_blob_size 1.10 2180 2180
|
||||
check_blob_size 1.11 3796 3796
|
||||
check_blob_size 1.12 7101 7101
|
||||
check_blob_size 1.13 7449 7449
|
||||
check_blob_size 1.14 7224 7224
|
||||
check_blob_size 1.15 3038 3038
|
||||
check_blob_size 1.16 1083 1083
|
||||
check_blob_size 1.17 5157 5157
|
||||
check_blob_size 1.18 6686 6686
|
||||
check_blob_size 1.19 6592 6592
|
||||
check_blob_size 1.20 0 0
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-53088-19343 The incremental blob I/O routines can only
|
||||
# read or overwriting existing blob content; they cannot change the size
|
||||
# of a blob.
|
||||
#
|
||||
# Also demonstrated in other e_blobXXX.test files.
|
||||
#
|
||||
do_test 2.1 {
|
||||
sqlite3_blob_open db main q1 s 86 1 B
|
||||
list [catch { sqlite3_blob_write $B 86 "1" 1 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
sqlite3_blob_close $B
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
# 2014 October 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_blobclose
|
||||
|
||||
set dots [string repeat . 40]
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE x1(a INTEGER PRIMARY KEY, b DOTS);
|
||||
INSERT INTO x1 VALUES(-1, $dots);
|
||||
INSERT INTO x1 VALUES(-10, $dots);
|
||||
INSERT INTO x1 VALUES(-100, $dots);
|
||||
INSERT INTO x1 VALUES(-1000, $dots);
|
||||
INSERT INTO x1 VALUES(-10000, $dots);
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-03145-46390 This function closes an open BLOB handle.
|
||||
#
|
||||
# It's not clear how to test that a blob handle really is closed.
|
||||
# Attempting to use a closed blob handle will likely crash the process.
|
||||
# Assume here that if the SHARED lock on the db file is released,
|
||||
# the blob handle has been closed.
|
||||
#
|
||||
do_execsql_test 1.1 { PRAGMA lock_status } {main unlocked temp closed}
|
||||
sqlite3_blob_open db main x1 b -1 0 B
|
||||
do_execsql_test 1.2 { PRAGMA lock_status } {main shared temp closed}
|
||||
sqlite3_blob_close $B
|
||||
do_execsql_test 1.3 { PRAGMA lock_status } {main unlocked temp closed}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-34027-00617 If the blob handle being closed was opened
|
||||
# for read-write access, and if the database is in auto-commit mode and
|
||||
# there are no other open read-write blob handles or active write
|
||||
# statements, the current transaction is committed.
|
||||
#
|
||||
# 2.1.*: Transaction is not committed if there are other open
|
||||
# read-write blob handles.
|
||||
#
|
||||
# 2.2.*: Transaction is not committed if not in auto-commit mode.
|
||||
#
|
||||
# 2.3.*: Active write statements.
|
||||
#
|
||||
do_test 2.1.1 {
|
||||
sqlite3_blob_open db main x1 b -100 1 B1
|
||||
sqlite3_blob_open db main x1 b -1000 1 B2
|
||||
sqlite3_blob_open db main x1 b -10000 1 B3
|
||||
sqlite3_blob_open db main x1 b -10000 0 B4 ;# B4 is read-only!
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main reserved temp closed}
|
||||
do_test 2.1.2 {
|
||||
sqlite3_blob_close $B1
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main reserved temp closed}
|
||||
do_test 2.1.3 {
|
||||
sqlite3_blob_close $B2
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main reserved temp closed}
|
||||
do_test 2.1.4 {
|
||||
sqlite3_blob_close $B3
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main shared temp closed}
|
||||
do_test 2.1.5 {
|
||||
sqlite3_blob_close $B4
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main unlocked temp closed}
|
||||
|
||||
do_test 2.2.1 {
|
||||
sqlite3_blob_open db main x1 b -100 1 B1
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main reserved temp closed}
|
||||
do_test 2.2.2 {
|
||||
execsql { BEGIN }
|
||||
sqlite3_blob_close $B1
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main reserved temp closed}
|
||||
do_test 2.2.3 {
|
||||
execsql { COMMIT }
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main unlocked temp closed}
|
||||
|
||||
proc val {} {
|
||||
sqlite3_blob_close $::B
|
||||
db eval { PRAGMA lock_status }
|
||||
}
|
||||
db func val val
|
||||
do_test 2.3.1 {
|
||||
sqlite3_blob_open db main x1 b -100 1 B
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main reserved temp closed}
|
||||
do_test 2.3.2 {
|
||||
execsql { INSERT INTO x1 VALUES(15, val()) }
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main unlocked temp closed}
|
||||
do_test 2.3.3 {
|
||||
execsql { SELECT * FROM x1 WHERE a = 15 }
|
||||
} {15 {main reserved temp closed}}
|
||||
|
||||
# A reader does not inhibit commit.
|
||||
do_test 2.3.4 {
|
||||
sqlite3_blob_open db main x1 b -100 1 B
|
||||
execsql { PRAGMA lock_status }
|
||||
} {main reserved temp closed}
|
||||
do_test 2.3.5 {
|
||||
execsql { SELECT a, val() FROM x1 LIMIT 1 }
|
||||
} {-10000 {main shared temp closed}}
|
||||
|
||||
|
||||
do_test 3.1 {
|
||||
sqlite3_blob_open db main x1 b -10 1 B
|
||||
execsql {
|
||||
INSERT INTO x1 VALUES(1, 'abc');
|
||||
SELECT * FROM x1 WHERE a=1;
|
||||
}
|
||||
} {1 abc}
|
||||
do_test 3.2 {
|
||||
sqlite3_blob_write $B 0 "abcdefghij" 10
|
||||
execsql { SELECT * FROM x1 WHERE a=-10 }
|
||||
} {-10 abcdefghij..............................}
|
||||
|
||||
do_test 3.3 {
|
||||
sqlite3 db2 test.db
|
||||
execsql { BEGIN ; SELECT * FROM x1 } db2
|
||||
sqlite3_blob_close $B
|
||||
} {SQLITE_BUSY}
|
||||
|
||||
# EVIDENCE-OF: R-41959-38737 Otherwise, if this function is passed a
|
||||
# valid open blob handle, the values returned by the sqlite3_errcode()
|
||||
# and sqlite3_errmsg() functions are set before returning.
|
||||
#
|
||||
do_test 3.4 {
|
||||
list [sqlite3_errcode db] [sqlite3_errmsg db]
|
||||
} {SQLITE_BUSY {database is locked}}
|
||||
|
||||
# EVIDENCE-OF: R-37801-37633 The BLOB handle is closed unconditionally.
|
||||
# Even if this routine returns an error code, the handle is still
|
||||
# closed.
|
||||
#
|
||||
# Test that the lock has been released. Assume this means the handle
|
||||
# is closed, even though blob_close() returned SQLITE_BUSY.
|
||||
#
|
||||
do_execsql_test 3.4 { PRAGMA lock_status } {main unlocked temp closed}
|
||||
|
||||
# EVIDENCE-OF: R-35111-05628 If an error occurs while committing the
|
||||
# transaction, an error code is returned and the transaction rolled
|
||||
# back.
|
||||
#
|
||||
# Row 1 is removed (it was inserted this transaction) and row -10
|
||||
# is restored to its original state. Transaction has been rolled back.
|
||||
#
|
||||
do_execsql_test 3.5 {
|
||||
SELECT * FROM x1 WHERE a IN (1, -10);
|
||||
} {-10 ........................................}
|
||||
|
||||
# EVIDENCE-OF: R-25894-51060 Calling this routine with a null pointer
|
||||
# (such as would be returned by a failed call to sqlite3_blob_open()) is
|
||||
# a harmless no-op.
|
||||
#
|
||||
do_test 4.0 { sqlite3_blob_close 0 } {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
# 2014 October 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_blobopen
|
||||
|
||||
forcedelete test.db2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
|
||||
CREATE TABLE main.t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TEMP TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
|
||||
CREATE TABLE main.x1(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TEMP TABLE x2(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
CREATE TABLE aux.x3(a INTEGER PRIMARY KEY, b TEXT, c BLOB);
|
||||
|
||||
INSERT INTO main.t1 VALUES(1, 'main one', X'0101');
|
||||
INSERT INTO main.t1 VALUES(2, 'main two', X'0102');
|
||||
INSERT INTO main.t1 VALUES(3, 'main three', X'0103');
|
||||
INSERT INTO main.t1 VALUES(4, 'main four', X'0104');
|
||||
INSERT INTO main.t1 VALUES(5, 'main five', X'0105');
|
||||
|
||||
INSERT INTO main.x1 VALUES(1, 'x main one', X'000101');
|
||||
INSERT INTO main.x1 VALUES(2, 'x main two', X'000102');
|
||||
INSERT INTO main.x1 VALUES(3, 'x main three', X'000103');
|
||||
INSERT INTO main.x1 VALUES(4, 'x main four', X'000104');
|
||||
INSERT INTO main.x1 VALUES(5, 'x main five', X'000105');
|
||||
|
||||
INSERT INTO temp.t1 VALUES(1, 'temp one', X'0201');
|
||||
INSERT INTO temp.t1 VALUES(2, 'temp two', X'0202');
|
||||
INSERT INTO temp.t1 VALUES(3, 'temp three', X'0203');
|
||||
INSERT INTO temp.t1 VALUES(4, 'temp four', X'0204');
|
||||
INSERT INTO temp.t1 VALUES(5, 'temp five', X'0205');
|
||||
|
||||
INSERT INTO temp.x2 VALUES(1, 'x temp one', X'000201');
|
||||
INSERT INTO temp.x2 VALUES(2, 'x temp two', X'000202');
|
||||
INSERT INTO temp.x2 VALUES(3, 'x temp three', X'000203');
|
||||
INSERT INTO temp.x2 VALUES(4, 'x temp four', X'000204');
|
||||
INSERT INTO temp.x2 VALUES(5, 'x temp five', X'000205');
|
||||
|
||||
INSERT INTO aux.t1 VALUES(1, 'aux one', X'0301');
|
||||
INSERT INTO aux.t1 VALUES(2, 'aux two', X'0302');
|
||||
INSERT INTO aux.t1 VALUES(3, 'aux three', X'0303');
|
||||
INSERT INTO aux.t1 VALUES(4, 'aux four', X'0304');
|
||||
INSERT INTO aux.t1 VALUES(5, 'aux five', X'0305');
|
||||
|
||||
INSERT INTO aux.x3 VALUES(1, 'x aux one', X'000301');
|
||||
INSERT INTO aux.x3 VALUES(2, 'x aux two', X'000302');
|
||||
INSERT INTO aux.x3 VALUES(3, 'x aux three', X'000303');
|
||||
INSERT INTO aux.x3 VALUES(4, 'x aux four', X'000304');
|
||||
INSERT INTO aux.x3 VALUES(5, 'x aux five', X'000305');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-37639-55938 This interfaces opens a handle to the BLOB
|
||||
# located in row iRow, column zColumn, table zTable in database zDb; in
|
||||
# other words, the same BLOB that would be selected by: SELECT zColumn
|
||||
# FROM zDb.zTable WHERE rowid = iRow;
|
||||
#
|
||||
proc read_blob {zDb zTab zCol iRow} {
|
||||
sqlite3_blob_open db $zDb $zTab $zCol $iRow 0 B
|
||||
set nByte [sqlite3_blob_bytes $B]
|
||||
set data [sqlite3_blob_read $B 0 $nByte]
|
||||
sqlite3_blob_close $B
|
||||
return $data
|
||||
}
|
||||
|
||||
do_test 1.1.1 { read_blob main t1 b 1 } "main one"
|
||||
do_test 1.1.2 { read_blob main t1 c 1 } "\01\01"
|
||||
do_test 1.1.3 { read_blob temp t1 b 1 } "temp one"
|
||||
do_test 1.1.4 { read_blob temp t1 c 1 } "\02\01"
|
||||
do_test 1.1.6 { read_blob aux t1 b 1 } "aux one"
|
||||
do_test 1.1.7 { read_blob aux t1 c 1 } "\03\01"
|
||||
|
||||
do_test 1.2.1 { read_blob main t1 b 4 } "main four"
|
||||
do_test 1.2.2 { read_blob main t1 c 4 } "\01\04"
|
||||
do_test 1.2.3 { read_blob temp t1 b 4 } "temp four"
|
||||
do_test 1.2.4 { read_blob temp t1 c 4 } "\02\04"
|
||||
do_test 1.2.6 { read_blob aux t1 b 4 } "aux four"
|
||||
do_test 1.2.7 { read_blob aux t1 c 4 } "\03\04"
|
||||
|
||||
do_test 1.3.1 { read_blob main x1 b 2 } "x main two"
|
||||
do_test 1.3.2 { read_blob main x1 c 2 } "\00\01\02"
|
||||
do_test 1.3.3 { read_blob temp x2 b 2 } "x temp two"
|
||||
do_test 1.3.4 { read_blob temp x2 c 2 } "\00\02\02"
|
||||
do_test 1.3.6 { read_blob aux x3 b 2 } "x aux two"
|
||||
do_test 1.3.7 { read_blob aux x3 c 2 } "\00\03\02"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-27234-05761 Parameter zDb is not the filename that
|
||||
# contains the database, but rather the symbolic name of the database.
|
||||
# For attached databases, this is the name that appears after the AS
|
||||
# keyword in the ATTACH statement. For the main database file, the
|
||||
# database name is "main". For TEMP tables, the database name is "temp".
|
||||
#
|
||||
# The test cases immediately above demonstrate that the database name
|
||||
# for the main db, for TEMP tables and for those in attached databases
|
||||
# is correct. The following tests check that filenames cannot be
|
||||
# used as well.
|
||||
#
|
||||
do_test 2.1 {
|
||||
list [catch { sqlite3_blob_open db "test.db" t1 b 1 0 B } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test 2.2 {
|
||||
list [catch { sqlite3_blob_open db "test.db2" t1 b 1 0 B } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-50854-53979 If the flags parameter is non-zero, then
|
||||
# the BLOB is opened for read and write access.
|
||||
#
|
||||
# EVIDENCE-OF: R-03922-41160 If the flags parameter is zero, the BLOB is
|
||||
# opened for read-only access.
|
||||
#
|
||||
foreach {tn iRow flags} {
|
||||
1 1 0
|
||||
2 2 1
|
||||
3 3 -1
|
||||
4 4 2147483647
|
||||
5 5 -2147483648
|
||||
} {
|
||||
do_test 3.$tn.1 {
|
||||
sqlite3_blob_open db main x1 c $iRow $flags B
|
||||
set n [sqlite3_blob_bytes $B]
|
||||
sqlite3_blob_read $B 0 $n
|
||||
} [binary format ccc 0 1 $iRow]
|
||||
|
||||
if {$flags==0} {
|
||||
# Blob was opened for read-only access - writing returns an error.
|
||||
do_test 3.$tn.2 {
|
||||
list [catch { sqlite3_blob_write $B 0 xxx 3 } msg] $msg
|
||||
} {1 SQLITE_READONLY}
|
||||
|
||||
do_execsql_test 3.$tn.3 {
|
||||
SELECT c FROM x1 WHERE a=$iRow;
|
||||
} [binary format ccc 0 1 $iRow]
|
||||
} else {
|
||||
# Blob was opened for read/write access - writing succeeds
|
||||
do_test 3.$tn.4 {
|
||||
list [catch { sqlite3_blob_write $B 0 xxx 3 } msg] $msg
|
||||
} {0 {}}
|
||||
|
||||
do_execsql_test 3.$tn.5 {
|
||||
SELECT c FROM x1 WHERE a=$iRow;
|
||||
} {xxx}
|
||||
}
|
||||
|
||||
sqlite3_blob_close $B
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES('abcd', 152);
|
||||
INSERT INTO t1 VALUES(NULL, X'00010203');
|
||||
INSERT INTO t1 VALUES('', 154.2);
|
||||
|
||||
CREATE TABLE t2(x PRIMARY KEY, y) WITHOUT ROWID;
|
||||
INSERT INTO t2 VALUES(1, 'blob');
|
||||
|
||||
CREATE TABLE t3(a PRIMARY KEY, b, c, d, e, f, UNIQUE(e, f));
|
||||
INSERT INTO t3 VALUES('aaaa', 'bbbb', 'cccc', 'dddd', 'eeee', 'ffff');
|
||||
CREATE INDEX t3b ON t3(b);
|
||||
|
||||
CREATE TABLE p1(x PRIMARY KEY);
|
||||
INSERT INTO p1 VALUES('abc');
|
||||
|
||||
CREATE TABLE c1(a INTEGER PRIMARY KEY, b REFERENCES p1);
|
||||
INSERT INTO c1 VALUES(45, 'abc');
|
||||
}
|
||||
|
||||
proc test_blob_open {tn zDb zTab zCol iRow flags errcode errmsg} {
|
||||
global B
|
||||
set B "0x1234"
|
||||
|
||||
if {$errcode=="SQLITE_OK"} {
|
||||
set expected "0 {}"
|
||||
} else {
|
||||
set expected "1 $errcode"
|
||||
}
|
||||
|
||||
set ::res [list [
|
||||
catch { sqlite3_blob_open db $zDb $zTab $zCol $iRow $flags B } msg
|
||||
] $msg]
|
||||
do_test 4.$tn.1 { set ::res } $expected
|
||||
|
||||
# EVIDENCE-OF: R-08940-21305 Unless it returns SQLITE_MISUSE, this
|
||||
# function sets the database connection error code and message
|
||||
# accessible via sqlite3_errcode() and sqlite3_errmsg() and related
|
||||
# functions.
|
||||
#
|
||||
# This proc (test_blob_open) is used below to test various error and
|
||||
# non-error conditions. But never SQLITE_MISUSE conditions. So these
|
||||
# test cases are considered as partly verifying the requirement above.
|
||||
# See below for a test of the SQLITE_MISUSE case.
|
||||
#
|
||||
do_test 4.$tn.2 {
|
||||
sqlite3_errcode db
|
||||
} $errcode
|
||||
do_test 4.$tn.3 {
|
||||
sqlite3_errmsg db
|
||||
} $errmsg
|
||||
|
||||
# EVIDENCE-OF: R-31086-35521 On success, SQLITE_OK is returned and the
|
||||
# new BLOB handle is stored in *ppBlob. Otherwise an error code is
|
||||
# returned and, unless the error code is SQLITE_MISUSE, *ppBlob is set
|
||||
# to NULL.
|
||||
#
|
||||
do_test 4.$tn.4 {
|
||||
expr {$B == "0"}
|
||||
} [expr {$errcode != "SQLITE_OK"}]
|
||||
|
||||
# EVIDENCE-OF: R-63421-15521 This means that, provided the API is not
|
||||
# misused, it is always safe to call sqlite3_blob_close() on *ppBlob
|
||||
# after this function it returns.
|
||||
do_test 4.$tn.5 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-31204-44780 Database zDb does not exist
|
||||
test_blob_open 1 nosuchdb t1 x 1 0 SQLITE_ERROR "no such table: nosuchdb.t1"
|
||||
|
||||
# EVIDENCE-OF: R-28676-08005 Table zTable does not exist within database zDb
|
||||
test_blob_open 2 main tt1 x 1 0 SQLITE_ERROR "no such table: main.tt1"
|
||||
|
||||
# EVIDENCE-OF: R-40134-30296 Table zTable is a WITHOUT ROWID table
|
||||
test_blob_open 3 main t2 y 1 0 SQLITE_ERROR \
|
||||
"cannot open table without rowid: t2"
|
||||
|
||||
# EVIDENCE-OF: R-56376-21261 Column zColumn does not exist
|
||||
test_blob_open 4 main t1 z 2 0 SQLITE_ERROR "no such column: \"z\""
|
||||
|
||||
# EVIDENCE-OF: R-28258-23166 Row iRow is not present in the table
|
||||
test_blob_open 5 main t1 y 6 0 SQLITE_ERROR "no such rowid: 6"
|
||||
|
||||
# EVIDENCE-OF: R-11683-62380 The specified column of row iRow contains a
|
||||
# value that is not a TEXT or BLOB value
|
||||
test_blob_open 6 main t1 x 2 0 SQLITE_ERROR "cannot open value of type null"
|
||||
test_blob_open 7 main t1 y 1 0 SQLITE_ERROR "cannot open value of type integer"
|
||||
test_blob_open 8 main t1 y 3 0 SQLITE_ERROR "cannot open value of type real"
|
||||
|
||||
# EVIDENCE-OF: R-34146-30782 Column zColumn is part of an index, PRIMARY
|
||||
# KEY or UNIQUE constraint and the blob is being opened for read/write
|
||||
# access
|
||||
#
|
||||
# Test cases 8.1.* show that such columns can be opened for read-access.
|
||||
# Tests 8.2.* show that read-write access is different. Columns "c" and "c"
|
||||
# are not part of an index, PK or UNIQUE constraint, so they work in both
|
||||
# cases.
|
||||
#
|
||||
test_blob_open 8.1.1 main t3 a 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.2 main t3 b 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.3 main t3 c 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.4 main t3 d 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.5 main t3 e 1 0 SQLITE_OK "not an error"
|
||||
test_blob_open 8.1.6 main t3 f 1 0 SQLITE_OK "not an error"
|
||||
|
||||
set cannot "cannot open indexed column for writing"
|
||||
test_blob_open 8.2.1 main t3 a 1 8 SQLITE_ERROR $cannot
|
||||
test_blob_open 8.2.2 main t3 b 1 8 SQLITE_ERROR $cannot
|
||||
test_blob_open 8.2.3 main t3 c 1 8 SQLITE_OK "not an error"
|
||||
test_blob_open 8.2.4 main t3 d 1 8 SQLITE_OK "not an error"
|
||||
test_blob_open 8.2.5 main t3 e 1 8 SQLITE_ERROR $cannot
|
||||
test_blob_open 8.2.6 main t3 f 1 8 SQLITE_ERROR $cannot
|
||||
|
||||
# EVIDENCE-OF: R-50117-55204 Foreign key constraints are enabled, column
|
||||
# zColumn is part of a child key definition and the blob is being opened
|
||||
# for read/write access
|
||||
#
|
||||
# 9.1: FK disabled, read-only access.
|
||||
# 9.2: FK disabled, read-only access.
|
||||
# 9.3: FK enabled, read/write access.
|
||||
# 9.4: FK enabled, read/write access.
|
||||
#
|
||||
test_blob_open 9.1 main c1 b 45 0 SQLITE_OK "not an error"
|
||||
test_blob_open 9.2 main c1 b 45 1 SQLITE_OK "not an error"
|
||||
execsql { PRAGMA foreign_keys = ON }
|
||||
test_blob_open 9.3 main c1 b 45 0 SQLITE_OK "not an error"
|
||||
test_blob_open 9.4 main c1 b 45 1 SQLITE_ERROR \
|
||||
"cannot open foreign key column for writing"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-08940-21305 Unless it returns SQLITE_MISUSE, this
|
||||
# function sets the database connection error code and message
|
||||
# accessible via sqlite3_errcode() and sqlite3_errmsg() and related
|
||||
# functions.
|
||||
#
|
||||
# This requirement is partially verified by the many uses of test
|
||||
# command [test_blob_open] above. All that is left is to verify the
|
||||
# SQLITE_MISUSE case.
|
||||
#
|
||||
# SQLITE_MISUSE is only returned if SQLITE_ENABLE_API_ARMOR is defined
|
||||
# during compilation.
|
||||
#
|
||||
ifcapable api_armor {
|
||||
sqlite3_blob_open db main t1 x 1 0 B
|
||||
|
||||
do_test 10.1.1 {
|
||||
list [catch {sqlite3_blob_open $B main t1 x 1 0 B2} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
do_test 10.1.2 {
|
||||
list [sqlite3_errcode db] [sqlite3_errmsg db]
|
||||
} {SQLITE_OK {not an error}}
|
||||
sqlite3_blob_close $B
|
||||
|
||||
do_test 10.2.1 {
|
||||
list [catch {sqlite3_blob_open db main {} x 1 0 B} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
do_test 10.2.2 {
|
||||
list [sqlite3_errcode db] [sqlite3_errmsg db]
|
||||
} {SQLITE_OK {not an error}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-50542-62589 If the row that a BLOB handle points to is
|
||||
# modified by an UPDATE, DELETE, or by ON CONFLICT side-effects then the
|
||||
# BLOB handle is marked as "expired". This is true if any column of the
|
||||
# row is changed, even a column other than the one the BLOB handle is
|
||||
# open on.
|
||||
#
|
||||
# EVIDENCE-OF: R-48367-20048 Calls to sqlite3_blob_read() and
|
||||
# sqlite3_blob_write() for an expired BLOB handle fail with a return
|
||||
# code of SQLITE_ABORT.
|
||||
#
|
||||
# 11.2: read-only handle, DELETE.
|
||||
# 11.3: read-only handle, UPDATE.
|
||||
# 11.4: read-only handle, REPLACE.
|
||||
# 11.5: read/write handle, DELETE.
|
||||
# 11.6: read/write handle, UPDATE.
|
||||
# 11.7: read/write handle, REPLACE.
|
||||
#
|
||||
do_execsql_test 11.1 {
|
||||
CREATE TABLE b1(a INTEGER PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO b1 VALUES(1, '1234567890', 1);
|
||||
INSERT INTO b1 VALUES(2, '1234567890', 2);
|
||||
INSERT INTO b1 VALUES(3, '1234567890', 3);
|
||||
INSERT INTO b1 VALUES(4, '1234567890', 4);
|
||||
INSERT INTO b1 VALUES(5, '1234567890', 5);
|
||||
INSERT INTO b1 VALUES(6, '1234567890', 6);
|
||||
|
||||
CREATE TABLE b2(a INTEGER PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO b2 VALUES(1, '1234567890', 1);
|
||||
INSERT INTO b2 VALUES(2, '1234567890', 2);
|
||||
INSERT INTO b2 VALUES(3, '1234567890', 3);
|
||||
INSERT INTO b2 VALUES(4, '1234567890', 4);
|
||||
INSERT INTO b2 VALUES(5, '1234567890', 5);
|
||||
INSERT INTO b2 VALUES(6, '1234567890', 6);
|
||||
}
|
||||
|
||||
do_test 11.2.1 {
|
||||
sqlite3_blob_open db main b1 b 2 0 B
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.2.2 {
|
||||
# Deleting a different row does not invalidate the blob handle.
|
||||
execsql { DELETE FROM b1 WHERE a = 1 }
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.2.3 {
|
||||
execsql { DELETE FROM b1 WHERE a = 2 }
|
||||
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.2.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.3.1 {
|
||||
sqlite3_blob_open db main b1 b 3 0 B
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.3.2 {
|
||||
# Updating a different row
|
||||
execsql { UPDATE b1 SET c = 42 WHERE a=4 }
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.3.3 {
|
||||
execsql { UPDATE b1 SET c = 43 WHERE a=3 }
|
||||
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.3.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.4.1 {
|
||||
sqlite3_blob_open db main b1 b 6 0 B
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.4.2 {
|
||||
# Replace a different row
|
||||
execsql { INSERT OR REPLACE INTO b1 VALUES(10, 'abcdefghij', 5) }
|
||||
sqlite3_blob_read $B 0 10
|
||||
} {1234567890}
|
||||
do_test 11.4.3 {
|
||||
execsql { INSERT OR REPLACE INTO b1 VALUES(11, 'abcdefghij', 6) }
|
||||
list [catch { sqlite3_blob_read $B 0 10 } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.4.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.4.1 {
|
||||
sqlite3_blob_open db main b2 b 2 1 B
|
||||
sqlite3_blob_write $B 0 "abcdefghij"
|
||||
} {}
|
||||
do_test 11.4.2 {
|
||||
# Deleting a different row does not invalidate the blob handle.
|
||||
execsql { DELETE FROM b2 WHERE a = 1 }
|
||||
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
|
||||
} {}
|
||||
do_test 11.4.3 {
|
||||
execsql { DELETE FROM b2 WHERE a = 2 }
|
||||
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.4.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.5.1 {
|
||||
sqlite3_blob_open db main b2 b 3 1 B
|
||||
sqlite3_blob_write $B 0 "abcdefghij"
|
||||
} {}
|
||||
do_test 11.5.2 {
|
||||
# Updating a different row
|
||||
execsql { UPDATE b2 SET c = 42 WHERE a=4 }
|
||||
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
|
||||
} {}
|
||||
do_test 11.5.3 {
|
||||
execsql { UPDATE b2 SET c = 43 WHERE a=3 }
|
||||
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.5.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
do_test 11.6.1 {
|
||||
sqlite3_blob_open db main b2 b 6 1 B
|
||||
sqlite3_blob_write $B 0 "abcdefghij"
|
||||
} {}
|
||||
do_test 11.6.2 {
|
||||
# Replace a different row
|
||||
execsql { INSERT OR REPLACE INTO b2 VALUES(10, 'abcdefghij', 5) }
|
||||
sqlite3_blob_write $B 0 "ABCDEFGHIJ"
|
||||
} {}
|
||||
do_test 11.6.3 {
|
||||
execsql { INSERT OR REPLACE INTO b2 VALUES(11, 'abcdefghij', 6) }
|
||||
list [catch { sqlite3_blob_write $B 0 "0987654321" } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test 11.6.4 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-45408-40694 Changes written into a BLOB prior to the
|
||||
# BLOB expiring are not rolled back by the expiration of the BLOB. Such
|
||||
# changes will eventually commit if the transaction continues to
|
||||
# completion.
|
||||
#
|
||||
do_execsql_test 12.1 {
|
||||
CREATE TABLE b3(x INTEGER PRIMARY KEY, y TEXT, z INTEGER);
|
||||
INSERT INTO b3 VALUES(22, '..........', NULL);
|
||||
}
|
||||
do_test 12.2 {
|
||||
sqlite3_blob_open db main b3 y 22 1 B
|
||||
sqlite3_blob_write $B 0 "xxxxx" 5
|
||||
} {}
|
||||
do_execsql_test 12.3 {
|
||||
UPDATE b3 SET z = 'not null';
|
||||
}
|
||||
do_test 12.4 {
|
||||
list [catch {sqlite3_blob_write $B 5 "xxxxx" 5} msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_execsql_test 12.5 {
|
||||
SELECT * FROM b3;
|
||||
} {22 xxxxx..... {not null}}
|
||||
do_test 12.5 {
|
||||
sqlite3_blob_close $B
|
||||
} {}
|
||||
do_execsql_test 12.6 {
|
||||
SELECT * FROM b3;
|
||||
} {22 xxxxx..... {not null}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-58813-55036 The sqlite3_bind_zeroblob() and
|
||||
# sqlite3_result_zeroblob() interfaces and the built-in zeroblob SQL
|
||||
# function may be used to create a zero-filled blob to read or write
|
||||
# using the incremental-blob interface.
|
||||
#
|
||||
do_execsql_test 13.1 {
|
||||
CREATE TABLE c2(i INTEGER PRIMARY KEY, j);
|
||||
INSERT INTO c2 VALUES(10, zeroblob(24));
|
||||
}
|
||||
|
||||
do_test 13.2 {
|
||||
set stmt [sqlite3_prepare_v2 db "INSERT INTO c2 VALUES(11, ?)" -1]
|
||||
sqlite3_bind_zeroblob $stmt 1 45
|
||||
sqlite3_step $stmt
|
||||
sqlite3_finalize $stmt
|
||||
} {SQLITE_OK}
|
||||
|
||||
# The blobs can be read:
|
||||
#
|
||||
do_test 13.3.1 {
|
||||
sqlite3_blob_open db main c2 j 10 1 B
|
||||
sqlite3_blob_open db main c2 j 11 1 B2
|
||||
list [sqlite3_blob_bytes $B] [sqlite3_blob_bytes $B2]
|
||||
} {24 45}
|
||||
do_test 13.3.2 {
|
||||
sqlite3_blob_read $B 0 24
|
||||
} [string repeat [binary format c 0] 24]
|
||||
do_test 13.3.3 {
|
||||
sqlite3_blob_read $B2 0 45
|
||||
} [string repeat [binary format c 0] 45]
|
||||
|
||||
# And also written:
|
||||
#
|
||||
do_test 13.4.1 {
|
||||
sqlite3_blob_write $B 0 [string repeat [binary format c 1] 24]
|
||||
} {}
|
||||
do_test 13.4.2 {
|
||||
sqlite3_blob_write $B2 0 [string repeat [binary format c 1] 45]
|
||||
} {}
|
||||
do_test 13.5 {
|
||||
sqlite3_blob_close $B
|
||||
sqlite3_blob_close $B2
|
||||
execsql { SELECT j FROM c2 }
|
||||
} [list \
|
||||
[string repeat [binary format c 1] 24] \
|
||||
[string repeat [binary format c 1] 45] \
|
||||
]
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
# 2014 October 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_blobwrite
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-62898-22698 This function is used to write data into an
|
||||
# open BLOB handle from a caller-supplied buffer. N bytes of data are
|
||||
# copied from the buffer Z into the open BLOB, starting at offset
|
||||
# iOffset.
|
||||
#
|
||||
set dots [string repeat . 40]
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, t TEXT);
|
||||
INSERT INTO t1 VALUES(-1, $dots);
|
||||
INSERT INTO t1 VALUES(-2, $dots);
|
||||
INSERT INTO t1 VALUES(-3, $dots);
|
||||
INSERT INTO t1 VALUES(-4, $dots);
|
||||
INSERT INTO t1 VALUES(-5, $dots);
|
||||
INSERT INTO t1 VALUES(-6, $dots);
|
||||
}
|
||||
|
||||
proc blob_write_test {tn id iOffset blob nData final} {
|
||||
sqlite3_blob_open db main t1 t $id 1 B
|
||||
|
||||
# EVIDENCE-OF: R-45864-01884 On success, sqlite3_blob_write() returns
|
||||
# SQLITE_OK. Otherwise, an error code or an extended error code is
|
||||
# returned.
|
||||
#
|
||||
# This block tests the SQLITE_OK case in the requirement above (the
|
||||
# Tcl sqlite3_blob_write() wrapper uses an empty string in place of
|
||||
# "SQLITE_OK"). The error cases are tested by the "blob_write_error_test"
|
||||
# tests below.
|
||||
#
|
||||
set res [sqlite3_blob_write $B $iOffset $blob $nData]
|
||||
uplevel [list do_test $tn.1 [list set {} $res] {}]
|
||||
|
||||
sqlite3_blob_close $B
|
||||
uplevel [list do_execsql_test $tn.3 "SELECT t FROM t1 WHERE a=$id" $final]
|
||||
}
|
||||
|
||||
set blob "0123456789012345678901234567890123456789"
|
||||
blob_write_test 1.1 -1 0 $blob 10 { 0123456789.............................. }
|
||||
blob_write_test 1.2 -2 8 $blob 10 { ........0123456789...................... }
|
||||
blob_write_test 1.3 -3 8 $blob 1 { ........0............................... }
|
||||
blob_write_test 1.4 -4 18 $blob 22 { ..................0123456789012345678901 }
|
||||
blob_write_test 1.5 -5 18 $blob 0 { ........................................ }
|
||||
blob_write_test 1.6 -6 0 $blob 40 { 0123456789012345678901234567890123456789 }
|
||||
|
||||
|
||||
proc blob_write_error_test {tn B iOffset blob nData errcode errmsg} {
|
||||
|
||||
# In cases where the underlying sqlite3_blob_write() function returns
|
||||
# SQLITE_OK, the Tcl wrapper returns an empty string. If the underlying
|
||||
# function returns an error, the Tcl wrapper throws an exception with
|
||||
# the error code as the Tcl exception message.
|
||||
#
|
||||
if {$errcode=="SQLITE_OK"} {
|
||||
set ret ""
|
||||
set isError 0
|
||||
} else {
|
||||
set ret $errcode
|
||||
set isError 1
|
||||
}
|
||||
|
||||
set cmd [list sqlite3_blob_write $B $iOffset $blob $nData]
|
||||
uplevel [list do_test $tn.1 [subst -nocommands {
|
||||
list [catch {$cmd} msg] [set msg]
|
||||
}] [list $isError $ret]]
|
||||
|
||||
# EVIDENCE-OF: R-34782-18311 Unless SQLITE_MISUSE is returned, this
|
||||
# function sets the database connection error code and message
|
||||
# accessible via sqlite3_errcode() and sqlite3_errmsg() and related
|
||||
# functions.
|
||||
#
|
||||
if {$errcode == "SQLITE_MISUSE"} { error "test proc misuse!" }
|
||||
uplevel [list do_test $tn.2 [list sqlite3_errcode db] $errcode]
|
||||
uplevel [list do_test $tn.3 [list sqlite3_errmsg db] $errmsg]
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t2(a TEXT, b INTEGER PRIMARY KEY);
|
||||
INSERT INTO t2 VALUES($dots, 43);
|
||||
INSERT INTO t2 VALUES($dots, 44);
|
||||
INSERT INTO t2 VALUES($dots, 45);
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-63341-57517 If the BLOB handle passed as the first
|
||||
# argument was not opened for writing (the flags parameter to
|
||||
# sqlite3_blob_open() was zero), this function returns SQLITE_READONLY.
|
||||
#
|
||||
sqlite3_blob_open db main t2 a 43 0 B
|
||||
blob_write_error_test 2.1 $B 0 $blob 10 \
|
||||
SQLITE_READONLY {attempt to write a readonly database}
|
||||
sqlite3_blob_close $B
|
||||
|
||||
# EVIDENCE-OF: R-29804-27366 If offset iOffset is less than N bytes from
|
||||
# the end of the BLOB, SQLITE_ERROR is returned and no data is written.
|
||||
#
|
||||
sqlite3_blob_open db main t2 a 44 3 B
|
||||
blob_write_error_test 2.2.1 $B 31 $blob 10 \
|
||||
SQLITE_ERROR {SQL logic error or missing database}
|
||||
|
||||
# Make a successful write to the blob handle. This shows that the
|
||||
# sqlite3_errcode() and sqlite3_errmsg() values are set even if the
|
||||
# blob_write() call succeeds (see requirement in the [blob_write_error_test]
|
||||
# proc).
|
||||
blob_write_error_test 2.2.1 $B 30 $blob 10 SQLITE_OK {not an error}
|
||||
|
||||
# EVIDENCE-OF: R-58570-38916 If N or iOffset are less than zero
|
||||
# SQLITE_ERROR is returned and no data is written.
|
||||
#
|
||||
blob_write_error_test 2.2.2 $B 31 $blob -1 \
|
||||
SQLITE_ERROR {SQL logic error or missing database}
|
||||
blob_write_error_test 2.2.3 $B 20 $blob 10 SQLITE_OK {not an error}
|
||||
blob_write_error_test 2.2.4 $B -1 $blob 10 \
|
||||
SQLITE_ERROR {SQL logic error or missing database}
|
||||
sqlite3_blob_close $B
|
||||
|
||||
# EVIDENCE-OF: R-20958-54138 An attempt to write to an expired BLOB
|
||||
# handle fails with an error code of SQLITE_ABORT.
|
||||
#
|
||||
do_test 2.3 {
|
||||
sqlite3_blob_open db main t2 a 43 0 B
|
||||
execsql { DELETE FROM t2 WHERE b=43 }
|
||||
} {}
|
||||
blob_write_error_test 2.3.1 $B 5 $blob 5 \
|
||||
SQLITE_ABORT {callback requested query abort}
|
||||
do_test 2.3.2 {
|
||||
execsql { SELECT 1, 2, 3 }
|
||||
sqlite3_errcode db
|
||||
} {SQLITE_OK}
|
||||
blob_write_error_test 2.3.3 $B 5 $blob 5 \
|
||||
SQLITE_ABORT {callback requested query abort}
|
||||
sqlite3_blob_close $B
|
||||
|
||||
# EVIDENCE-OF: R-08382-59936 Writes to the BLOB that occurred before the
|
||||
# BLOB handle expired are not rolled back by the expiration of the
|
||||
# handle, though of course those changes might have been overwritten by
|
||||
# the statement that expired the BLOB handle or by other independent
|
||||
# statements.
|
||||
#
|
||||
# 3.1.*: not rolled back,
|
||||
# 3.2.*: overwritten.
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t3(i INTEGER PRIMARY KEY, j TEXT, k TEXT);
|
||||
INSERT INTO t3 VALUES(1, $dots, $dots);
|
||||
INSERT INTO t3 VALUES(2, $dots, $dots);
|
||||
SELECT * FROM t3 WHERE i=1;
|
||||
} {
|
||||
1
|
||||
........................................
|
||||
........................................
|
||||
}
|
||||
sqlite3_blob_open db main t3 j 1 1 B
|
||||
blob_write_error_test 3.1.1 $B 5 $blob 10 SQLITE_OK {not an error}
|
||||
do_execsql_test 3.1.2 {
|
||||
UPDATE t3 SET k = 'xyz' WHERE i=1;
|
||||
SELECT * FROM t3 WHERE i=1;
|
||||
} {
|
||||
1 .....0123456789......................... xyz
|
||||
}
|
||||
blob_write_error_test 3.1.3 $B 15 $blob 10 \
|
||||
SQLITE_ABORT {callback requested query abort}
|
||||
sqlite3_blob_close $B
|
||||
do_execsql_test 3.1.4 {
|
||||
SELECT * FROM t3 WHERE i=1;
|
||||
} {
|
||||
1 .....0123456789......................... xyz
|
||||
}
|
||||
|
||||
sqlite3_blob_open db main t3 j 2 1 B
|
||||
blob_write_error_test 3.2.1 $B 5 $blob 10 SQLITE_OK {not an error}
|
||||
do_execsql_test 3.2.2 {
|
||||
UPDATE t3 SET j = 'xyz' WHERE i=2;
|
||||
SELECT * FROM t3 WHERE i=2;
|
||||
} {
|
||||
2 xyz ........................................
|
||||
}
|
||||
blob_write_error_test 3.2.3 $B 15 $blob 10 \
|
||||
SQLITE_ABORT {callback requested query abort}
|
||||
sqlite3_blob_close $B
|
||||
do_execsql_test 3.2.4 {
|
||||
SELECT * FROM t3 WHERE i=2;
|
||||
} {
|
||||
2 xyz ........................................
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
# 2011 October 28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_changes
|
||||
|
||||
# Like [do_execsql_test], except it appends the value returned by
|
||||
# [db changes] to the result of executing the SQL script.
|
||||
#
|
||||
proc do_changes_test {tn sql res} {
|
||||
uplevel [list \
|
||||
do_test $tn "concat \[execsql {$sql}\] \[db changes\]" $res
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-15996-49369 This function returns the number of rows
|
||||
# modified, inserted or deleted by the most recently completed INSERT,
|
||||
# UPDATE or DELETE statement on the database connection specified by the
|
||||
# only parameter.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(x, y, PRIMARY KEY(x, y)) WITHOUT ROWID;
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t2(y);
|
||||
}
|
||||
foreach {tn schema} {
|
||||
1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
2 {
|
||||
CREATE TABLE t1(a, b, PRIMARY KEY(a, b)) WITHOUT ROWID;
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
} {
|
||||
reset_db
|
||||
execsql $schema
|
||||
|
||||
# Insert 1 row.
|
||||
do_changes_test 1.$tn.1 { INSERT INTO t1 VALUES(0, 0) } 1
|
||||
|
||||
# Insert 10 rows.
|
||||
do_changes_test 1.$tn.2 {
|
||||
WITH rows(i, j) AS (
|
||||
SELECT 1, 1 UNION ALL SELECT i+1, j+i FROM rows WHERE i<10
|
||||
)
|
||||
INSERT INTO t1 SELECT * FROM rows
|
||||
} 10
|
||||
|
||||
# Modify 5 rows.
|
||||
do_changes_test 1.$tn.3 {
|
||||
UPDATE t1 SET b=b+1 WHERE a<5;
|
||||
} 5
|
||||
|
||||
# Delete 4 rows
|
||||
do_changes_test 1.$tn.4 {
|
||||
DELETE FROM t1 WHERE a>6
|
||||
} 4
|
||||
|
||||
# Check the "on the database connecton specified" part of hte
|
||||
# requirement - changes made by other connections do not show up in
|
||||
# the return value of sqlite3_changes().
|
||||
do_test 1.$tn.5 {
|
||||
sqlite3 db2 test.db
|
||||
execsql { INSERT INTO t1 VALUES(-1, -1) } db2
|
||||
db2 changes
|
||||
} 1
|
||||
do_test 1.$tn.6 {
|
||||
db changes
|
||||
} 4
|
||||
db2 close
|
||||
|
||||
# Test that statements that modify no rows because they hit UNIQUE
|
||||
# constraints set the sqlite3_changes() value to 0. Regardless of
|
||||
# whether or not they are executed inside an explicit transaction.
|
||||
#
|
||||
# 1.$tn.8-9: outside of a transaction
|
||||
# 1.$tn.10-12: inside a transaction
|
||||
#
|
||||
do_changes_test 1.$tn.7 {
|
||||
CREATE UNIQUE INDEX i2 ON t1(a);
|
||||
} 4
|
||||
do_catchsql_test 1.$tn.8 {
|
||||
INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test 1.$tn.9 { db changes } 0
|
||||
do_catchsql_test 1.$tn.10 {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test 1.$tn.11 { db changes } 0
|
||||
do_changes_test 1.$tn.12 COMMIT 0
|
||||
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-44877-05564 Executing any other type of SQL statement
|
||||
# does not modify the value returned by this function.
|
||||
#
|
||||
reset_db
|
||||
do_changes_test 2.1 { CREATE TABLE t1(x) } 0
|
||||
do_changes_test 2.2 {
|
||||
WITH d(y) AS (SELECT 1 UNION ALL SELECT y+1 FROM d WHERE y<47)
|
||||
INSERT INTO t1 SELECT y FROM d;
|
||||
} 47
|
||||
|
||||
# The statement above set changes() to 47. Check that none of the following
|
||||
# modify this.
|
||||
do_changes_test 2.3 { SELECT count(x) FROM t1 } {47 47}
|
||||
do_changes_test 2.4 { DROP TABLE t1 } 47
|
||||
do_changes_test 2.5 { CREATE TABLE t1(x) } 47
|
||||
do_changes_test 2.6 { ALTER TABLE t1 ADD COLUMN b } 47
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-53938-27527 Only changes made directly by the INSERT,
|
||||
# UPDATE or DELETE statement are considered - auxiliary changes caused
|
||||
# by triggers, foreign key actions or REPLACE constraint resolution are
|
||||
# not counted.
|
||||
#
|
||||
# 3.1.*: triggers
|
||||
# 3.2.*: foreign key actions
|
||||
# 3.3.*: replace constraints
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1.0 {
|
||||
CREATE TABLE log(x);
|
||||
CREATE TABLE p1(one PRIMARY KEY, two);
|
||||
|
||||
CREATE TRIGGER tr_ai AFTER INSERT ON p1 BEGIN
|
||||
INSERT INTO log VALUES('insert');
|
||||
END;
|
||||
CREATE TRIGGER tr_bd BEFORE DELETE ON p1 BEGIN
|
||||
INSERT INTO log VALUES('delete');
|
||||
END;
|
||||
CREATE TRIGGER tr_au AFTER UPDATE ON p1 BEGIN
|
||||
INSERT INTO log VALUES('update');
|
||||
END;
|
||||
|
||||
}
|
||||
|
||||
do_changes_test 3.1.1 {
|
||||
INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
|
||||
} 3
|
||||
do_changes_test 3.1.2 {
|
||||
UPDATE p1 SET two = two||two;
|
||||
} 3
|
||||
do_changes_test 3.1.3 {
|
||||
DELETE FROM p1 WHERE one IN ('a', 'c');
|
||||
} 2
|
||||
do_execsql_test 3.1.4 {
|
||||
-- None of the inserts on table log were counted.
|
||||
SELECT count(*) FROM log
|
||||
} 8
|
||||
|
||||
do_execsql_test 3.2.0 {
|
||||
DELETE FROM p1;
|
||||
INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
|
||||
|
||||
CREATE TABLE c1(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET NULL);
|
||||
CREATE TABLE c2(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET DEFAULT);
|
||||
CREATE TABLE c3(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE CASCADE);
|
||||
INSERT INTO c1 VALUES('a', 'aaa');
|
||||
INSERT INTO c2 VALUES('b', 'bbb');
|
||||
INSERT INTO c3 VALUES('c', 'ccc');
|
||||
|
||||
INSERT INTO p1 VALUES('d', 'D'), ('e', 'E'), ('f', 'F');
|
||||
CREATE TABLE c4(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET NULL);
|
||||
CREATE TABLE c5(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET DEFAULT);
|
||||
CREATE TABLE c6(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE CASCADE);
|
||||
INSERT INTO c4 VALUES('d', 'aaa');
|
||||
INSERT INTO c5 VALUES('e', 'bbb');
|
||||
INSERT INTO c6 VALUES('f', 'ccc');
|
||||
|
||||
PRAGMA foreign_keys = ON;
|
||||
}
|
||||
|
||||
do_changes_test 3.2.1 { DELETE FROM p1 WHERE one = 'a' } 1
|
||||
do_changes_test 3.2.2 { DELETE FROM p1 WHERE one = 'b' } 1
|
||||
do_changes_test 3.2.3 { DELETE FROM p1 WHERE one = 'c' } 1
|
||||
do_execsql_test 3.2.4 {
|
||||
SELECT * FROM c1;
|
||||
SELECT * FROM c2;
|
||||
SELECT * FROM c3;
|
||||
} {{} aaa {} bbb}
|
||||
|
||||
do_changes_test 3.2.5 { UPDATE p1 SET one = 'g' WHERE one = 'd' } 1
|
||||
do_changes_test 3.2.6 { UPDATE p1 SET one = 'h' WHERE one = 'e' } 1
|
||||
do_changes_test 3.2.7 { UPDATE p1 SET one = 'i' WHERE one = 'f' } 1
|
||||
do_execsql_test 3.2.8 {
|
||||
SELECT * FROM c4;
|
||||
SELECT * FROM c5;
|
||||
SELECT * FROM c6;
|
||||
} {{} aaa {} bbb i ccc}
|
||||
|
||||
do_execsql_test 3.3.0 {
|
||||
CREATE TABLE r1(a UNIQUE, b UNIQUE);
|
||||
INSERT INTO r1 VALUES('i', 'i');
|
||||
INSERT INTO r1 VALUES('ii', 'ii');
|
||||
INSERT INTO r1 VALUES('iii', 'iii');
|
||||
INSERT INTO r1 VALUES('iv', 'iv');
|
||||
INSERT INTO r1 VALUES('v', 'v');
|
||||
INSERT INTO r1 VALUES('vi', 'vi');
|
||||
INSERT INTO r1 VALUES('vii', 'vii');
|
||||
}
|
||||
|
||||
do_changes_test 3.3.1 { INSERT OR REPLACE INTO r1 VALUES('i', 1) } 1
|
||||
do_changes_test 3.3.2 { INSERT OR REPLACE INTO r1 VALUES('iv', 'v') } 1
|
||||
do_changes_test 3.3.3 { UPDATE OR REPLACE r1 SET b='v' WHERE a='iii' } 1
|
||||
do_changes_test 3.3.4 { UPDATE OR REPLACE r1 SET b='vi',a='vii' WHERE a='ii' } 1
|
||||
do_execsql_test 3.3.5 {
|
||||
SELECT * FROM r1 ORDER BY a;
|
||||
} {i 1 iii v vii vi}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-09813-48563 The value returned by sqlite3_changes()
|
||||
# immediately after an INSERT, UPDATE or DELETE statement run on a view
|
||||
# is always zero.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE log(log);
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER v1_i INSTEAD OF INSERT ON v1 BEGIN
|
||||
INSERT INTO log VALUES('insert');
|
||||
END;
|
||||
CREATE TRIGGER v1_u INSTEAD OF UPDATE ON v1 BEGIN
|
||||
INSERT INTO log VALUES('update'), ('update');
|
||||
END;
|
||||
CREATE TRIGGER v1_d INSTEAD OF DELETE ON v1 BEGIN
|
||||
INSERT INTO log VALUES('delete'), ('delete'), ('delete');
|
||||
END;
|
||||
}
|
||||
|
||||
do_changes_test 4.2.1 { INSERT INTO t1 SELECT * FROM t1 } 3
|
||||
do_changes_test 4.2.2 { INSERT INTO v1 VALUES(1, 2) } 0
|
||||
|
||||
do_changes_test 4.3.1 { INSERT INTO t1 SELECT * FROM t1 } 6
|
||||
do_changes_test 4.3.2 { UPDATE v1 SET y='xyz' WHERE x=1 } 0
|
||||
|
||||
do_changes_test 4.4.1 { INSERT INTO t1 SELECT * FROM t1 } 12
|
||||
do_changes_test 4.4.2 { DELETE FROM v1 WHERE x=5 } 0
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-32918-61474 Before entering a trigger program the value
|
||||
# returned by sqlite3_changes() function is saved. After the trigger
|
||||
# program has finished, the original value is restored.
|
||||
#
|
||||
reset_db
|
||||
db func my_changes my_changes
|
||||
set ::changes [list]
|
||||
proc my_changes {x} {
|
||||
set res [db changes]
|
||||
lappend ::changes $x $res
|
||||
return $res
|
||||
}
|
||||
|
||||
do_execsql_test 5.1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(x);
|
||||
INSERT INTO t1 VALUES(1, NULL);
|
||||
INSERT INTO t1 VALUES(2, NULL);
|
||||
INSERT INTO t1 VALUES(3, NULL);
|
||||
CREATE TRIGGER AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES('a'), ('b'), ('c');
|
||||
SELECT my_changes('trigger');
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 5.1.1 {
|
||||
INSERT INTO t2 VALUES('a'), ('b');
|
||||
UPDATE t1 SET b = my_changes('update');
|
||||
SELECT * FROM t1;
|
||||
} {1 2 2 2 3 2}
|
||||
|
||||
# Value is being restored to "2" when the trigger program exits.
|
||||
do_test 5.1.2 {
|
||||
set ::changes
|
||||
} {update 2 trigger 3 update 2 trigger 3 update 2 trigger 3}
|
||||
|
||||
|
||||
reset_db
|
||||
do_execsql_test 5.2.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE log(x);
|
||||
INSERT INTO t1 VALUES(1, 0);
|
||||
INSERT INTO t1 VALUES(2, 0);
|
||||
INSERT INTO t1 VALUES(3, 0);
|
||||
CREATE TRIGGER t1_a_u AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO log VALUES(old.b || ' -> ' || new.b || ' c = ' || changes() );
|
||||
END;
|
||||
CREATE TABLE t2(a);
|
||||
INSERT INTO t2 VALUES(1), (2), (3);
|
||||
UPDATE t1 SET b = changes();
|
||||
}
|
||||
do_execsql_test 5.2.1 {
|
||||
SELECT * FROM t1;
|
||||
} {1 3 2 3 3 3}
|
||||
do_execsql_test 5.2.2 {
|
||||
SELECT * FROM log;
|
||||
} {{0 -> 3 c = 3} {0 -> 3 c = 3} {0 -> 3 c = 3}}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-17146-37073 Within a trigger program each INSERT,
|
||||
# UPDATE and DELETE statement sets the value returned by
|
||||
# sqlite3_changes() upon completion as normal. Of course, this value
|
||||
# will not include any changes performed by sub-triggers, as the
|
||||
# sqlite3_changes() value will be saved and restored after each
|
||||
# sub-trigger has run.
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE t3(a, b);
|
||||
CREATE TABLE log(x);
|
||||
|
||||
CREATE TRIGGER t1_i BEFORE INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.a, new.b), (new.a, new.b);
|
||||
INSERT INTO log VALUES('t2->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t2_i AFTER INSERT ON t2 BEGIN
|
||||
INSERT INTO t3 VALUES(new.a, new.b), (new.a, new.b), (new.a, new.b);
|
||||
INSERT INTO log VALUES('t3->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_u AFTER UPDATE ON t1 BEGIN
|
||||
UPDATE t2 SET b=new.b WHERE a=old.a;
|
||||
INSERT INTO log VALUES('t2->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t2_u BEFORE UPDATE ON t2 BEGIN
|
||||
UPDATE t3 SET b=new.b WHERE a=old.a;
|
||||
INSERT INTO log VALUES('t3->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_d AFTER DELETE ON t1 BEGIN
|
||||
DELETE FROM t2 WHERE a=old.a AND b=old.b;
|
||||
INSERT INTO log VALUES('t2->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t2_d BEFORE DELETE ON t2 BEGIN
|
||||
DELETE FROM t3 WHERE a=old.a AND b=old.b;
|
||||
INSERT INTO log VALUES('t3->' || changes());
|
||||
END;
|
||||
}
|
||||
|
||||
do_changes_test 6.1 {
|
||||
INSERT INTO t1 VALUES('+', 'o');
|
||||
SELECT * FROM log;
|
||||
} {t3->3 t3->3 t2->2 1}
|
||||
|
||||
do_changes_test 6.2 {
|
||||
DELETE FROM log;
|
||||
UPDATE t1 SET b='*';
|
||||
SELECT * FROM log;
|
||||
} {t3->6 t3->6 t2->2 1}
|
||||
|
||||
do_changes_test 6.3 {
|
||||
DELETE FROM log;
|
||||
DELETE FROM t1;
|
||||
SELECT * FROM log;
|
||||
} {t3->6 t3->0 t2->2 1}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-43399-09409 This means that if the changes() SQL
|
||||
# function (or similar) is used by the first INSERT, UPDATE or DELETE
|
||||
# statement within a trigger, it returns the value as set when the
|
||||
# calling statement began executing.
|
||||
#
|
||||
# EVIDENCE-OF: R-53215-27584 If it is used by the second or subsequent
|
||||
# such statement within a trigger program, the value returned reflects
|
||||
# the number of rows modified by the previous INSERT, UPDATE or DELETE
|
||||
# statement within the same trigger.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE q1(t);
|
||||
CREATE TABLE q2(u, v);
|
||||
CREATE TABLE q3(w);
|
||||
|
||||
CREATE TRIGGER q2_insert BEFORE INSERT ON q2 BEGIN
|
||||
|
||||
/* changes() returns value from previous I/U/D in callers context */
|
||||
INSERT INTO q1 VALUES('1:' || changes());
|
||||
|
||||
/* changes() returns value of previous I/U/D in this context */
|
||||
INSERT INTO q3 VALUES(changes()), (2), (3);
|
||||
INSERT INTO q1 VALUES('2:' || changes());
|
||||
INSERT INTO q3 VALUES(changes() + 3), (changes()+4);
|
||||
SELECT 'this does not affect things!';
|
||||
INSERT INTO q1 VALUES('3:' || changes());
|
||||
UPDATE q3 SET w = w+10 WHERE w%2;
|
||||
INSERT INTO q1 VALUES('4:' || changes());
|
||||
DELETE FROM q3;
|
||||
INSERT INTO q1 VALUES('5:' || changes());
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
INSERT INTO q2 VALUES('x', 'y');
|
||||
SELECT * FROM q1;
|
||||
} {
|
||||
1:0 2:3 3:2 4:3 5:5
|
||||
}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
DELETE FROM q1;
|
||||
INSERT INTO q2 VALUES('x', 'y');
|
||||
SELECT * FROM q1;
|
||||
} {
|
||||
1:5 2:3 3:2 4:3 5:5
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,213 @@
|
||||
# 2011 May 06
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_totalchanges
|
||||
|
||||
# Like [do_execsql_test], except it appends the value returned by
|
||||
# [db total_changes] to the result of executing the SQL script.
|
||||
#
|
||||
proc do_tc_test {tn sql res} {
|
||||
uplevel [list \
|
||||
do_test $tn "concat \[execsql {$sql}\] \[db total_changes\]" $res
|
||||
]
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX t1_b ON t1(b);
|
||||
CREATE TABLE t2(x, y, PRIMARY KEY(x, y)) WITHOUT ROWID;
|
||||
CREATE INDEX t2_y ON t2(y);
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-65438-26258 This function returns the total number of
|
||||
# rows inserted, modified or deleted by all INSERT, UPDATE or DELETE
|
||||
# statements completed since the database connection was opened,
|
||||
# including those executed as part of trigger programs.
|
||||
#
|
||||
# 1.1.*: different types of I/U/D statements,
|
||||
# 1.2.*: trigger programs.
|
||||
#
|
||||
do_tc_test 1.1.1 {
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
UPDATE t1 SET a = a+1;
|
||||
DELETE FROM t1;
|
||||
} {6}
|
||||
do_tc_test 1.1.2 {
|
||||
DELETE FROM t1
|
||||
} {6}
|
||||
|
||||
do_tc_test 1.1.3 {
|
||||
WITH data(a,b) AS (
|
||||
SELECT 0, 0 UNION ALL SELECT a+1, b+1 FROM data WHERE a<99
|
||||
)
|
||||
INSERT INTO t1 SELECT * FROM data;
|
||||
} {106}
|
||||
|
||||
do_tc_test 1.1.4 {
|
||||
INSERT INTO t2 SELECT * FROM t1 WHERE a<50;
|
||||
UPDATE t2 SET y=y+1;
|
||||
} {206}
|
||||
|
||||
do_tc_test 1.1.5 {
|
||||
DELETE FROM t2 WHERE y<=25
|
||||
} {231}
|
||||
|
||||
do_execsql_test 1.2.1 {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
}
|
||||
sqlite3 db test.db ; # To reset total_changes
|
||||
do_tc_test 1.2.2 {
|
||||
CREATE TABLE log(detail);
|
||||
CREATE TRIGGER t1_after_insert AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO log VALUES('inserted into t1');
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_before_delete BEFORE DELETE ON t1 BEGIN
|
||||
INSERT INTO log VALUES('deleting from t1');
|
||||
INSERT INTO log VALUES('here we go!');
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_after_update AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO log VALUES('update');
|
||||
DELETE FROM log;
|
||||
END;
|
||||
|
||||
INSERT INTO t1 VALUES('a', 'b'); -- 1 + 1
|
||||
UPDATE t1 SET b='c'; -- 1 + 1 + 2
|
||||
DELETE FROM t1; -- 1 + 1 + 1
|
||||
} {9}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-61766-15253 Executing any other type of SQL statement
|
||||
# does not affect the value returned by sqlite3_total_changes().
|
||||
do_tc_test 2.1 {
|
||||
INSERT INTO t1 VALUES(1, 2), (3, 4);
|
||||
INSERT INTO t2 VALUES(1, 2), (3, 4);
|
||||
} {15}
|
||||
do_tc_test 2.2 {
|
||||
SELECT count(*) FROM t1;
|
||||
} {2 15}
|
||||
do_tc_test 2.3 {
|
||||
CREATE TABLE t4(a, b);
|
||||
ALTER TABLE t4 ADD COLUMN c;
|
||||
CREATE INDEX i4 ON t4(c);
|
||||
ALTER TABLE t4 RENAME TO t5;
|
||||
ANALYZE;
|
||||
BEGIN;
|
||||
DROP TABLE t2;
|
||||
ROLLBACK;
|
||||
VACUUM;
|
||||
} {15}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-36043-10590 Changes made as part of foreign key
|
||||
# actions are included in the count, but those made as part of REPLACE
|
||||
# constraint resolution are not.
|
||||
#
|
||||
# 3.1.*: foreign key actions
|
||||
# 3.2.*: REPLACE constraints.
|
||||
#
|
||||
sqlite3 db test.db ; # To reset total_changes
|
||||
do_tc_test 3.1.1 {
|
||||
CREATE TABLE p1(c PRIMARY KEY, d);
|
||||
CREATE TABLE c1(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET NULL);
|
||||
CREATE TABLE c2(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE CASCADE);
|
||||
CREATE TABLE c3(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET DEFAULT);
|
||||
|
||||
INSERT INTO p1 VALUES(1, 'one');
|
||||
INSERT INTO p1 VALUES(2, 'two');
|
||||
INSERT INTO p1 VALUES(3, 'three');
|
||||
INSERT INTO p1 VALUES(4, 'four');
|
||||
|
||||
INSERT INTO c1 VALUES(1, 'i');
|
||||
INSERT INTO c2 VALUES(2, 'ii');
|
||||
INSERT INTO c3 VALUES(3, 'iii');
|
||||
PRAGMA foreign_keys = ON;
|
||||
} {7}
|
||||
|
||||
do_tc_test 3.1.2 { DELETE FROM p1 WHERE c=1; } {9}
|
||||
do_tc_test 3.1.3 { DELETE FROM p1 WHERE c=2; } {11}
|
||||
do_tc_test 3.1.4 { DELETE FROM p1 WHERE c=3; } {13}
|
||||
do_tc_test 3.1.5 { DELETE FROM p1 WHERE c=4; } {14} ; # only 1 this time.
|
||||
|
||||
sqlite3 db test.db ; # To reset total_changes
|
||||
do_tc_test 3.1.6 {
|
||||
DROP TABLE c1;
|
||||
DROP TABLE c2;
|
||||
DROP TABLE c3;
|
||||
CREATE TABLE c1(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET NULL);
|
||||
CREATE TABLE c2(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE CASCADE);
|
||||
CREATE TABLE c3(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET DEFAULT);
|
||||
|
||||
INSERT INTO p1 VALUES(1, 'one');
|
||||
INSERT INTO p1 VALUES(2, 'two');
|
||||
INSERT INTO p1 VALUES(3, 'three');
|
||||
INSERT INTO p1 VALUES(4, 'four');
|
||||
|
||||
INSERT INTO c1 VALUES(1, 'i');
|
||||
INSERT INTO c2 VALUES(2, 'ii');
|
||||
INSERT INTO c3 VALUES(3, 'iii');
|
||||
PRAGMA foreign_keys = ON;
|
||||
} {7}
|
||||
|
||||
do_tc_test 3.1.7 { UPDATE p1 SET c=c+4 WHERE c=1; } {9}
|
||||
do_tc_test 3.1.8 { UPDATE p1 SET c=c+4 WHERE c=2; } {11}
|
||||
do_tc_test 3.1.9 { UPDATE p1 SET c=c+4 WHERE c=3; } {13}
|
||||
do_tc_test 3.1.10 { UPDATE p1 SET c=c+4 WHERE c=4; } {14} ; # only 1 this time.
|
||||
|
||||
sqlite3 db test.db ; # To reset total_changes
|
||||
do_tc_test 3.2.1 {
|
||||
CREATE TABLE t3(a UNIQUE, b UNIQUE);
|
||||
INSERT INTO t3 VALUES('one', 'one');
|
||||
INSERT INTO t3 VALUES('two', 'two');
|
||||
INSERT OR REPLACE INTO t3 VALUES('one', 'two');
|
||||
} {3}
|
||||
|
||||
do_tc_test 3.2.2 {
|
||||
INSERT INTO t3 VALUES('three', 'one');
|
||||
UPDATE OR REPLACE t3 SET b='two' WHERE b='one';
|
||||
SELECT * FROM t3;
|
||||
} {three two 5}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-54872-08741 Changes to a view that are intercepted by
|
||||
# INSTEAD OF triggers are not counted.
|
||||
#
|
||||
sqlite3 db test.db ; # To reset total_changes
|
||||
do_tc_test 4.1 {
|
||||
CREATE TABLE t6(x);
|
||||
CREATE VIEW v1 AS SELECT * FROM t6;
|
||||
CREATE TRIGGER v1_tr1 INSTEAD OF INSERT ON v1 BEGIN
|
||||
SELECT 'no-op';
|
||||
END;
|
||||
|
||||
INSERT INTO v1 VALUES('a');
|
||||
INSERT INTO v1 VALUES('b');
|
||||
} {0}
|
||||
do_tc_test 4.2 {
|
||||
CREATE TRIGGER v1_tr2 INSTEAD OF INSERT ON v1 BEGIN
|
||||
INSERT INTO t6 VALUES(new.x);
|
||||
END;
|
||||
|
||||
INSERT INTO v1 VALUES('c');
|
||||
INSERT INTO v1 VALUES('d');
|
||||
} {2}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -50,5 +50,22 @@ do_tblsread_test 1.3 { UPDATE par SET a=? WHERE b=? } {c1 c2 par}
|
||||
do_tblsread_test 1.4 { UPDATE par SET c=? WHERE b=? } {c3 par}
|
||||
do_tblsread_test 1.5 { UPDATE par SET a=?,b=?,c=? WHERE b=? } {c1 c2 c3 par s1}
|
||||
|
||||
ifcapable incrblob {
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE pX(x PRIMARY KEY);
|
||||
CREATE TABLE cX(a INTEGER PRIMARY KEY, b REFERENCES pX);
|
||||
}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
INSERT INTO cX VALUES(11, zeroblob(40));
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test 2.2 {
|
||||
set stmt [sqlite3_prepare_v2 db "INSERT INTO cX VALUES(11, ?)" -1]
|
||||
sqlite3_bind_zeroblob $stmt 1 45
|
||||
sqlite3_step $stmt
|
||||
sqlite3_finalize $stmt
|
||||
} {SQLITE_CONSTRAINT}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix in5
|
||||
|
||||
do_test in5-1.1 {
|
||||
execsql {
|
||||
@@ -135,4 +136,51 @@ do_test in5-5.3 {
|
||||
}]
|
||||
} {0}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# At one point SQLite was removing the DISTINCT keyword from expressions
|
||||
# similar to:
|
||||
#
|
||||
# <expr1> IN (SELECT DISTINCT <expr2> FROM...)
|
||||
#
|
||||
# However, there are a few obscure cases where this is incorrect. For
|
||||
# example, if the SELECT features a LIMIT clause, or if the collation
|
||||
# sequence or affinity used by the DISTINCT does not match the one used
|
||||
# by the IN(...) expression.
|
||||
#
|
||||
do_execsql_test 6.1.1 {
|
||||
CREATE TABLE t1(a COLLATE nocase);
|
||||
INSERT INTO t1 VALUES('one');
|
||||
INSERT INTO t1 VALUES('ONE');
|
||||
}
|
||||
do_execsql_test 6.1.2 {
|
||||
SELECT count(*) FROM t1 WHERE a COLLATE BINARY IN (SELECT DISTINCT a FROM t1)
|
||||
} {1}
|
||||
|
||||
do_execsql_test 6.2.1 {
|
||||
CREATE TABLE t3(a, b);
|
||||
INSERT INTO t3 VALUES(1, 1);
|
||||
INSERT INTO t3 VALUES(1, 2);
|
||||
INSERT INTO t3 VALUES(1, 3);
|
||||
INSERT INTO t3 VALUES(2, 4);
|
||||
INSERT INTO t3 VALUES(2, 5);
|
||||
INSERT INTO t3 VALUES(2, 6);
|
||||
INSERT INTO t3 VALUES(3, 7);
|
||||
INSERT INTO t3 VALUES(3, 8);
|
||||
INSERT INTO t3 VALUES(3, 9);
|
||||
}
|
||||
do_execsql_test 6.2.2 {
|
||||
SELECT count(*) FROM t3 WHERE b IN (SELECT DISTINCT a FROM t3 LIMIT 5);
|
||||
} {3}
|
||||
do_execsql_test 6.2.3 {
|
||||
SELECT count(*) FROM t3 WHERE b IN (SELECT a FROM t3 LIMIT 5);
|
||||
} {2}
|
||||
|
||||
do_execsql_test 6.3.1 {
|
||||
CREATE TABLE x1(a);
|
||||
CREATE TABLE x2(b);
|
||||
INSERT INTO x1 VALUES(1), (1), (2);
|
||||
INSERT INTO x2 VALUES(1), (2);
|
||||
SELECT count(*) FROM x2 WHERE b IN (SELECT DISTINCT a FROM x1 LIMIT 2);
|
||||
} {2}
|
||||
|
||||
finish_test
|
||||
|
||||
+7
-1
@@ -112,6 +112,12 @@ foreach bPersist [list 0 1] {
|
||||
}
|
||||
}
|
||||
|
||||
# When this test was written, an IO error within the UPDATE statement caused
|
||||
# a rollback, which tripped all read-cursors, causing the outer SELECT to
|
||||
# fail with "abort due to ROLLBACK". Now, the loop continues until the UPDATE
|
||||
# is run successfully. At this point the next IO error occurs within the
|
||||
# SELECT - throwing the "disk I/O error" that the test case now expects.
|
||||
#
|
||||
do_test ioerr2-5 {
|
||||
execsql {
|
||||
CREATE TABLE t2 AS SELECT * FROM t1;
|
||||
@@ -130,7 +136,7 @@ do_test ioerr2-5 {
|
||||
}
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {abort due to ROLLBACK}}
|
||||
} {1 {disk I/O error}} ;# used to be "{1 {abort due to ROLLBACK}}"
|
||||
|
||||
if {$::tcl_platform(platform) == "unix"} {
|
||||
# Cause the call to xAccess used by [pragma temp_store_directory] to
|
||||
|
||||
@@ -106,5 +106,59 @@ do_test join5-2.12 {
|
||||
execsql {SELECT * FROM xy LEFT JOIN ab ON NULL WHERE NULL}
|
||||
} {}
|
||||
|
||||
# Ticket https://www.sqlite.org/src/tktview/6f2222d550f5b0ee7ed37601
|
||||
# Incorrect output on a LEFT JOIN.
|
||||
#
|
||||
do_execsql_test join5-3.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
DROP TABLE IF EXISTS t3;
|
||||
CREATE TABLE x1(a);
|
||||
INSERT INTO x1 VALUES(1);
|
||||
CREATE TABLE x2(b NOT NULL);
|
||||
CREATE TABLE x3(c, d);
|
||||
INSERT INTO x3 VALUES('a', NULL);
|
||||
INSERT INTO x3 VALUES('b', NULL);
|
||||
INSERT INTO x3 VALUES('c', NULL);
|
||||
SELECT * FROM x1 LEFT JOIN x2 LEFT JOIN x3 ON x3.d = x2.b;
|
||||
} {1 {} {} {}}
|
||||
do_execsql_test join5-3.2 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
DROP TABLE IF EXISTS t3;
|
||||
DROP TABLE IF EXISTS t4;
|
||||
DROP TABLE IF EXISTS t5;
|
||||
CREATE TABLE t1(x text NOT NULL, y text);
|
||||
CREATE TABLE t2(u text NOT NULL, x text NOT NULL);
|
||||
CREATE TABLE t3(w text NOT NULL, v text);
|
||||
CREATE TABLE t4(w text NOT NULL, z text NOT NULL);
|
||||
CREATE TABLE t5(z text NOT NULL, m text);
|
||||
INSERT INTO t1 VALUES('f6d7661f-4efe-4c90-87b5-858e61cd178b',NULL);
|
||||
INSERT INTO t1 VALUES('f6ea82c3-2cad-45ce-ae8f-3ddca4fb2f48',NULL);
|
||||
INSERT INTO t1 VALUES('f6f47499-ecb4-474b-9a02-35be73c235e5',NULL);
|
||||
INSERT INTO t1 VALUES('56f47499-ecb4-474b-9a02-35be73c235e5',NULL);
|
||||
INSERT INTO t3 VALUES('007f2033-cb20-494c-b135-a1e4eb66130c',
|
||||
'f6d7661f-4efe-4c90-87b5-858e61cd178b');
|
||||
SELECT *
|
||||
FROM t3
|
||||
INNER JOIN t1 ON t1.x= t3.v AND t1.y IS NULL
|
||||
LEFT JOIN t4 ON t4.w = t3.w
|
||||
LEFT JOIN t5 ON t5.z = t4.z
|
||||
LEFT JOIN t2 ON t2.u = t5.m
|
||||
LEFT JOIN t1 xyz ON xyz.y = t2.x;
|
||||
} {007f2033-cb20-494c-b135-a1e4eb66130c f6d7661f-4efe-4c90-87b5-858e61cd178b f6d7661f-4efe-4c90-87b5-858e61cd178b {} {} {} {} {} {} {} {} {}}
|
||||
do_execsql_test join5-3.3 {
|
||||
DROP TABLE IF EXISTS x1;
|
||||
DROP TABLE IF EXISTS x2;
|
||||
DROP TABLE IF EXISTS x3;
|
||||
CREATE TABLE x1(a);
|
||||
INSERT INTO x1 VALUES(1);
|
||||
CREATE TABLE x2(b NOT NULL);
|
||||
CREATE TABLE x3(c, d);
|
||||
INSERT INTO x3 VALUES('a', NULL);
|
||||
INSERT INTO x3 VALUES('b', NULL);
|
||||
INSERT INTO x3 VALUES('c', NULL);
|
||||
SELECT * FROM x1 LEFT JOIN x2 JOIN x3 WHERE x3.d = x2.b;
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# 2014-11-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 regression tests for SQLite library.
|
||||
# The focus of this script is testing the "eval.c" loadable extension.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
load_static_extension db eval
|
||||
do_execsql_test misc8-1.0 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
INSERT INTO t1 VALUES(1,2,3),(4,5,6);
|
||||
SELECT quote(eval('SELECT * FROM t1 ORDER BY a','-abc-'));
|
||||
} {'1-abc-2-abc-3-abc-4-abc-5-abc-6'}
|
||||
do_execsql_test misc8-1.1 {
|
||||
SELECT quote(eval('SELECT * FROM t1 ORDER BY a'));
|
||||
} {{'1 2 3 4 5 6'}}
|
||||
do_catchsql_test misc8-1.2 {
|
||||
SELECT quote(eval('SELECT d FROM t1 ORDER BY a'));
|
||||
} {1 {no such column: d}}
|
||||
do_execsql_test misc8-1.3 {
|
||||
INSERT INTO t1 VALUES(7,null,9);
|
||||
SELECT eval('SELECT * FROM t1 ORDER BY a',',');
|
||||
} {1,2,3,4,5,6,7,,9}
|
||||
do_catchsql_test misc8-1.4 {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(10,11,12);
|
||||
SELECT a, coalesce(b, eval('ROLLBACK; SELECT ''bam'';')), c
|
||||
FROM t1 ORDER BY a;
|
||||
} {0 {1 2 3 4 5 6 7 bam 9}}
|
||||
do_catchsql_test misc8-1.5 {
|
||||
INSERT INTO t1 VALUES(10,11,12);
|
||||
SELECT a, coalesce(b, eval('SELECT ''bam''')), c
|
||||
FROM t1
|
||||
ORDER BY rowid;
|
||||
} {0 {1 2 3 4 5 6 7 bam 9 10 11 12}}
|
||||
do_catchsql_test misc8-1.6 {
|
||||
SELECT a, coalesce(b, eval('DELETE FROM t1; SELECT ''bam''')), c
|
||||
FROM t1
|
||||
ORDER BY rowid;
|
||||
} {0 {1 2 3 4 5 6 7 bam {}}}
|
||||
do_catchsql_test misc8-1.7 {
|
||||
INSERT INTO t1 VALUES(1,2,3),(4,5,6),(7,null,9);
|
||||
BEGIN;
|
||||
CREATE TABLE t2(x);
|
||||
SELECT a, coalesce(b, eval('ROLLBACK; SELECT ''bam''')), c
|
||||
FROM t1
|
||||
ORDER BY rowid;
|
||||
} {1 {abort due to ROLLBACK}}
|
||||
|
||||
|
||||
reset_db
|
||||
|
||||
proc dbeval {sql} { db eval $sql }
|
||||
db func eval dbeval
|
||||
|
||||
do_execsql_test misc8-2.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER) WITHOUT ROWID;
|
||||
CREATE TABLE t2(c INTEGER PRIMARY KEY, d INTEGER, x BLOB);
|
||||
INSERT INTO t1 VALUES(0,0);
|
||||
INSERT INTO t1 VALUES(10,10);
|
||||
INSERT INTO t2 VALUES(1,1,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(2,2,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(3,3,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(4,4,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(5,5,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(6,6,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(7,7,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(8,8,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(9,9,zeroblob(200));
|
||||
INSERT INTO t2 VALUES(10,10,zeroblob(200));
|
||||
SELECT a, c, eval(
|
||||
printf('DELETE FROM t2 WHERE c=%d AND %d>5', a+c, a+c)
|
||||
) FROM t1, t2;
|
||||
} {
|
||||
0 1 {} 10 1 {}
|
||||
0 2 {} 10 2 {}
|
||||
0 3 {} 10 3 {}
|
||||
0 4 {} 10 4 {}
|
||||
0 5 {} 10 5 {}
|
||||
0 6 {} 10 {} {}
|
||||
0 7 {} 10 {} {}
|
||||
0 8 {} 10 {} {}
|
||||
0 9 {} 10 {} {}
|
||||
0 10 {} 10 {} {}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
+44
-42
@@ -33,7 +33,7 @@ proc register_rblob_code {dbname seed} {
|
||||
set ::rcnt $seed
|
||||
proc rblob {n} {
|
||||
set ::rcnt [expr (([set ::rcnt] << 3) + [set ::rcnt] + 456) & 0xFFFFFFFF]
|
||||
set str [format %.8x [expr [set ::rcnt] ^ 0xbdf20da3]]
|
||||
set str [format %.8x [expr [set ::rcnt] ^ 0xbdf20da3]]
|
||||
string range [string repeat [set str] [expr [set n]/4]] 1 [set n]
|
||||
}
|
||||
$dbname func rblob rblob
|
||||
@@ -42,7 +42,7 @@ proc register_rblob_code {dbname seed} {
|
||||
|
||||
# For cases 1.1 and 1.4, the number of pages read using xRead() is 4 on
|
||||
# unix and 9 on windows. The difference is that windows only ever maps
|
||||
# an integer number of OS pages (i.e. creates mappings that are a multiple
|
||||
# an integer number of OS pages (i.e. creates mappings that are a multiple
|
||||
# of 4KB in size). Whereas on unix any sized mapping may be created.
|
||||
#
|
||||
foreach {t mmap_size nRead c2init} {
|
||||
@@ -106,50 +106,52 @@ foreach {t mmap_size nRead c2init} {
|
||||
set ::rcnt 0
|
||||
proc rblob {n} {
|
||||
set ::rcnt [expr (($::rcnt << 3) + $::rcnt + 456) & 0xFFFFFFFF]
|
||||
set str [format %.8x [expr $::rcnt ^ 0xbdf20da3]]
|
||||
set str [format %.8x [expr $::rcnt ^ 0xbdf20da3]]
|
||||
string range [string repeat $str [expr $n/4]] 1 $n
|
||||
}
|
||||
|
||||
reset_db
|
||||
db func rblob rblob
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
PRAGMA auto_vacuum = 1;
|
||||
PRAGMA mmap_size = 67108864;
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t1(a, b, UNIQUE(a, b));
|
||||
INSERT INTO t1 VALUES(rblob(500), rblob(500));
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 2
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 4
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 8
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 16
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 32
|
||||
PRAGMA wal_checkpoint;
|
||||
} {67108864 wal 0 103 103}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
PRAGMA auto_vacuum;
|
||||
SELECT count(*) FROM t1;
|
||||
} {1 32}
|
||||
|
||||
if {[permutation] != "inmemory_journal"} {
|
||||
do_test 2.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 func rblob rblob
|
||||
db2 eval {
|
||||
DELETE FROM t1 WHERE (rowid%4);
|
||||
PRAGMA wal_checkpoint;
|
||||
}
|
||||
db2 eval {
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 16
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
} {16}
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
ifcapable wal {
|
||||
do_execsql_test 2.1 {
|
||||
PRAGMA auto_vacuum = 1;
|
||||
PRAGMA mmap_size = 67108864;
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t1(a, b, UNIQUE(a, b));
|
||||
INSERT INTO t1 VALUES(rblob(500), rblob(500));
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 2
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 4
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 8
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 16
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 32
|
||||
PRAGMA wal_checkpoint;
|
||||
} {0 24 24}
|
||||
db2 close
|
||||
} {67108864 wal 0 103 103}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
PRAGMA auto_vacuum;
|
||||
SELECT count(*) FROM t1;
|
||||
} {1 32}
|
||||
|
||||
if {[permutation] != "inmemory_journal"} {
|
||||
do_test 2.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 func rblob rblob
|
||||
db2 eval {
|
||||
DELETE FROM t1 WHERE (rowid%4);
|
||||
PRAGMA wal_checkpoint;
|
||||
}
|
||||
db2 eval {
|
||||
INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; -- 16
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
} {16}
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
PRAGMA wal_checkpoint;
|
||||
} {0 24 24}
|
||||
db2 close
|
||||
}
|
||||
}
|
||||
|
||||
reset_db
|
||||
@@ -227,7 +229,7 @@ do_test 4.4 {
|
||||
do_execsql_test 4.5 { COMMIT }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Ensure that existing cursors holding xFetch() references are not
|
||||
# Ensure that existing cursors holding xFetch() references are not
|
||||
# confused if those pages are moved to make way for the root page of a
|
||||
# new table or index.
|
||||
#
|
||||
@@ -296,7 +298,7 @@ foreach {tn1 mmap1 mmap2} {
|
||||
sql1 "PRAGMA mmap_size = $mmap1"
|
||||
sql2 "PRAGMA mmap_size = $mmap2"
|
||||
|
||||
do_test $tn1.$tn {
|
||||
do_test $tn1.$tn {
|
||||
for {set i 1} {$i <= 100} {incr i} {
|
||||
if {$i % 2} {
|
||||
set c1 sql1
|
||||
@@ -311,7 +313,7 @@ foreach {tn1 mmap1 mmap2} {
|
||||
UPDATE t2 SET x = (SELECT md5sum(a) FROM t1);
|
||||
}
|
||||
|
||||
set res [$c2 {
|
||||
set res [$c2 {
|
||||
SELECT count(*) FROM t1;
|
||||
SELECT x == (SELECT md5sum(a) FROM t1) FROM t2;
|
||||
PRAGMA integrity_check;
|
||||
|
||||
@@ -113,6 +113,7 @@ set allquicktests [test_set $alltests -exclude {
|
||||
vtab_err.test walslow.test walcrash.test walcrash3.test
|
||||
walthread.test rtree3.test indexfault.test securedel2.test
|
||||
sort3.test sort4.test fts4growth.test fts4growth2.test
|
||||
bigsort.test
|
||||
}]
|
||||
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
|
||||
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
|
||||
|
||||
+2
-2
@@ -1190,13 +1190,13 @@ ifcapable schema_pragmas {
|
||||
execsql2 {
|
||||
pragma collation_list;
|
||||
}
|
||||
} {seq 0 name NOCASE seq 1 name RTRIM seq 2 name BINARY}
|
||||
} {seq 0 name RTRIM seq 1 name NOCASE seq 2 name BINARY}
|
||||
do_test pragma-11.2 {
|
||||
db collate New_Collation blah...
|
||||
execsql {
|
||||
pragma collation_list;
|
||||
}
|
||||
} {0 New_Collation 1 NOCASE 2 RTRIM 3 BINARY}
|
||||
} {0 New_Collation 1 RTRIM 2 NOCASE 3 BINARY}
|
||||
}
|
||||
|
||||
ifcapable schema_pragmas&&tempdb {
|
||||
|
||||
@@ -95,5 +95,25 @@ do_execsql_test printf2-2.3 {
|
||||
SELECT printf('%s=(%d/%g/%s)',a) FROM t1 ORDER BY a;
|
||||
} {-1=(0/0/) 1=(0/0/) 1.5=(0/0/) abc=(0/0/)}
|
||||
|
||||
# The precision of the %c conversion causes the character to repeat.
|
||||
#
|
||||
do_execsql_test printf2-3.1 {
|
||||
SELECT printf('|%110.100c|','*');
|
||||
} {{| ****************************************************************************************************|}}
|
||||
do_execsql_test printf2-3.2 {
|
||||
SELECT printf('|%-110.100c|','*');
|
||||
} {{|**************************************************************************************************** |}}
|
||||
do_execsql_test printf2-3.3 {
|
||||
SELECT printf('|%9.8c|%-9.8c|','*','*');
|
||||
} {{| ********|******** |}}
|
||||
do_execsql_test printf2-3.4 {
|
||||
SELECT printf('|%8.8c|%-8.8c|','*','*');
|
||||
} {|********|********|}
|
||||
do_execsql_test printf2-3.5 {
|
||||
SELECT printf('|%7.8c|%-7.8c|','*','*');
|
||||
} {|********|********|}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+2
-2
@@ -60,11 +60,11 @@ ifcapable conflict {
|
||||
#
|
||||
do_test rollback-1.5 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_ERROR}
|
||||
} {SQLITE_ROW}
|
||||
|
||||
# Restart the SELECT statement
|
||||
#
|
||||
do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_ABORT}
|
||||
do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_OK}
|
||||
} else {
|
||||
do_test rollback-1.6 { sqlite3_reset $STMT } {SQLITE_OK}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
# 2014 November 12
|
||||
#
|
||||
# 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 containst tests to verify that ROLLBACK or ROLLBACK TO
|
||||
# operations interact correctly with ongoing SELECT statements.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix rollback2
|
||||
|
||||
proc int2hex {i} { format %.2X $i }
|
||||
db func int2hex int2hex
|
||||
do_execsql_test 1.0 {
|
||||
SELECT int2hex(0), int2hex(100), int2hex(255)
|
||||
} {00 64 FF}
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(i, h);
|
||||
CREATE INDEX i1 ON t1(h);
|
||||
WITH data(a, b) AS (
|
||||
SELECT 1, int2hex(1)
|
||||
UNION ALL
|
||||
SELECT a+1, int2hex(a+1) FROM data WHERE a<40
|
||||
)
|
||||
INSERT INTO t1 SELECT * FROM data;
|
||||
} {}
|
||||
|
||||
|
||||
# do_rollback_test ID SWITCHES
|
||||
#
|
||||
# where SWITCHES are:
|
||||
#
|
||||
# -setup SQL script to open transaction and begin writing.
|
||||
# -select SELECT to execute after -setup script
|
||||
# -result Expected result of -select statement
|
||||
# -rollback Use this SQL command ("ROLLBACK" or "ROLLBACK TO ...") to
|
||||
# rollback the transaction in the middle of the -select statment
|
||||
# execution.
|
||||
#
|
||||
proc do_rollback_test {tn args} {
|
||||
set A(-setup) ""
|
||||
set A(-select) ""
|
||||
set A(-result) ""
|
||||
set A(-rollback) ROLLBACK
|
||||
|
||||
array set O $args
|
||||
foreach k [array names O] {
|
||||
if {[info exists A($k)]==0} { error "unknown option: $k" }
|
||||
set A($k) $O($k)
|
||||
}
|
||||
|
||||
for {set iRollback 0} 1 {incr iRollback} {
|
||||
catch { db eval ROLLBACK }
|
||||
set res [list]
|
||||
db eval $A(-setup)
|
||||
|
||||
set i 0
|
||||
db eval $A(-select) x {
|
||||
if {$i==$iRollback} { db eval $A(-rollback) }
|
||||
foreach k $x(*) { lappend res $x($k) }
|
||||
incr i
|
||||
}
|
||||
|
||||
do_test $tn.$iRollback [list set {} $res] [list {*}$A(-result)]
|
||||
if {$i < $iRollback} break
|
||||
}
|
||||
}
|
||||
|
||||
do_rollback_test 2.1 -setup {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE (i%2)==1;
|
||||
} -select {
|
||||
SELECT i FROM t1 WHERE (i%2)==0
|
||||
} -result {
|
||||
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
|
||||
}
|
||||
|
||||
do_rollback_test 2.2 -setup {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE (i%4)==1;
|
||||
SAVEPOINT one;
|
||||
DELETE FROM t1 WHERE (i%2)==1;
|
||||
} -rollback {
|
||||
ROLLBACK TO one;
|
||||
} -select {
|
||||
SELECT i FROM t1 WHERE (i%2)==0
|
||||
} -result {
|
||||
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Try with some index scans
|
||||
#
|
||||
do_eqp_test 3.1 {
|
||||
SELECT i FROM t1 WHERE (i%2)==0 ORDER BY h DESC;
|
||||
} {0 0 0 {SCAN TABLE t1 USING INDEX i1}}
|
||||
do_rollback_test 3.2 -setup {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE (i%2)==1;
|
||||
} -select {
|
||||
SELECT i FROM t1 WHERE (i%2)==0 ORDER BY h DESC;
|
||||
} -result {
|
||||
40 38 36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2
|
||||
}
|
||||
do_rollback_test 3.3 -setup {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE (i%4)==1;
|
||||
SAVEPOINT one;
|
||||
DELETE FROM t1 WHERE (i%2)==1;
|
||||
} -rollback {
|
||||
ROLLBACK TO one;
|
||||
} -select {
|
||||
SELECT i FROM t1 WHERE (i%2)==0 ORDER BY h DESC;
|
||||
} -result {
|
||||
40 38 36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Now with some index scans that feature overflow keys.
|
||||
#
|
||||
set leader [string repeat "abcdefghij" 70]
|
||||
do_execsql_test 4.1 { UPDATE t1 SET h = $leader || h; }
|
||||
|
||||
do_eqp_test 4.2 {
|
||||
SELECT i FROM t1 WHERE (i%2)==0 ORDER BY h ASC;
|
||||
} {0 0 0 {SCAN TABLE t1 USING INDEX i1}}
|
||||
do_rollback_test 4.3 -setup {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE (i%2)==1;
|
||||
} -select {
|
||||
SELECT i FROM t1 WHERE (i%2)==0 ORDER BY h ASC;
|
||||
} -result {
|
||||
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
|
||||
}
|
||||
do_rollback_test 4.4 -setup {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE (i%4)==1;
|
||||
SAVEPOINT one;
|
||||
DELETE FROM t1 WHERE (i%2)==1;
|
||||
} -rollback {
|
||||
ROLLBACK TO one;
|
||||
} -select {
|
||||
SELECT i FROM t1 WHERE (i%2)==0 ORDER BY h ASC;
|
||||
} -result {
|
||||
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# 2014-11-12
|
||||
#
|
||||
# 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 that errors encountered during a ROLLBACK operation correctly
|
||||
# affect ongoing SQL statements.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix rollbackfault
|
||||
|
||||
|
||||
proc int2hex {i} { format %.2X $i }
|
||||
db func int2hex int2hex
|
||||
do_execsql_test 1.0 {
|
||||
SELECT int2hex(0), int2hex(100), int2hex(255)
|
||||
} {00 64 FF}
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(i, h);
|
||||
CREATE INDEX i1 ON t1(h);
|
||||
WITH data(a, b) AS (
|
||||
SELECT 1, int2hex(1)
|
||||
UNION ALL
|
||||
SELECT a+1, int2hex(a+1) FROM data WHERE a<40
|
||||
)
|
||||
INSERT INTO t1 SELECT * FROM data;
|
||||
} {}
|
||||
|
||||
foreach f {oom ioerr} {
|
||||
do_faultsim_test 1.2 -faults $f* -prep {
|
||||
set sql1 { SELECT i FROM t1 WHERE (i%2)==0 }
|
||||
set sql2 { SELECT i FROM t1 WHERE (i%2)==0 ORDER BY h }
|
||||
set ::s1 [sqlite3_prepare db $sql1 -1 dummy]
|
||||
set ::s2 [sqlite3_prepare db $sql2 -1 dummy]
|
||||
|
||||
for {set i 0} {$i < 10} {incr i} { sqlite3_step $::s1 }
|
||||
for {set i 0} {$i < 3} {incr i} { sqlite3_step $::s2 }
|
||||
|
||||
execsql {
|
||||
BEGIN; DELETE FROM t1 WHERE (i%2)
|
||||
}
|
||||
} -body {
|
||||
execsql { ROLLBACK }
|
||||
} -test {
|
||||
|
||||
set res1 [list]
|
||||
set res2 [list]
|
||||
while {"SQLITE_ROW" == [sqlite3_step $::s1]} {
|
||||
lappend res1 [sqlite3_column_text $::s1 0]
|
||||
}
|
||||
while {"SQLITE_ROW" == [sqlite3_step $::s2]} {
|
||||
lappend res2 [sqlite3_column_text $::s2 0]
|
||||
}
|
||||
set rc1 [sqlite3_finalize $::s1]
|
||||
set rc2 [sqlite3_finalize $::s2]
|
||||
|
||||
catchsql { ROLLBACK }
|
||||
|
||||
if {$rc1=="SQLITE_OK" && $rc2=="SQLITE_OK"
|
||||
&& $res1=="22 24 26 28 30 32 34 36 38 40"
|
||||
&& $res2=="8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40"
|
||||
} {
|
||||
# This is Ok.
|
||||
} elseif {$rc1!="SQLITE_OK" && $rc2!="SQLITE_OK" && $res1=="" &&$res2==""} {
|
||||
# Also Ok.
|
||||
} else {
|
||||
error "statements don't look right"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+1
-1
@@ -315,7 +315,7 @@ ifcapable incrblob {
|
||||
do_test savepoint-5.3.2.3 {
|
||||
set rc [catch {seek $fd 0; read $fd} res]
|
||||
set rc
|
||||
} {1}
|
||||
} {0}
|
||||
do_test savepoint-5.3.3 {
|
||||
catchsql {RELEASE def}
|
||||
} {0 {}}
|
||||
|
||||
@@ -30,6 +30,7 @@ do_test savepoint7-1.1 {
|
||||
db eval {SELECT * FROM t1} {
|
||||
db eval {
|
||||
SAVEPOINT x2;
|
||||
CREATE TABLE IF NOT EXISTS t3(xyz);
|
||||
INSERT INTO t2 VALUES($a,$b,$c);
|
||||
RELEASE x2;
|
||||
}
|
||||
@@ -46,7 +47,7 @@ do_test savepoint7-1.2 {
|
||||
RELEASE x2;
|
||||
}
|
||||
}
|
||||
db eval {SELECT * FROM t2}
|
||||
db eval {SELECT * FROM t2;}
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
|
||||
do_test savepoint7-1.3 {
|
||||
@@ -65,7 +66,7 @@ do_test savepoint7-1.3 {
|
||||
# queries in outer contexts.
|
||||
#
|
||||
do_test savepoint7-2.1 {
|
||||
db eval {DELETE FROM t2; SAVEPOINT x1;}
|
||||
db eval {DELETE FROM t2; SAVEPOINT x1; CREATE TABLE t4(abc);}
|
||||
set rc [catch {
|
||||
db eval {SELECT * FROM t1} {
|
||||
db eval {
|
||||
@@ -77,7 +78,7 @@ do_test savepoint7-2.1 {
|
||||
} msg]
|
||||
db eval {RELEASE x1}
|
||||
list $rc $msg [db eval {SELECT * FROM t2}]
|
||||
} {1 {callback requested query abort} {}}
|
||||
} {1 {abort due to ROLLBACK} {}}
|
||||
|
||||
do_test savepoint7-2.2 {
|
||||
db eval {DELETE FROM t2;}
|
||||
@@ -85,12 +86,13 @@ do_test savepoint7-2.2 {
|
||||
db eval {SELECT * FROM t1} {
|
||||
db eval {
|
||||
SAVEPOINT x2;
|
||||
CREATE TABLE t5(pqr);
|
||||
INSERT INTO t2 VALUES($a,$b,$c);
|
||||
ROLLBACK TO x2;
|
||||
}
|
||||
}
|
||||
} msg]
|
||||
list $rc $msg [db eval {SELECT * FROM t2}]
|
||||
} {1 {callback requested query abort} {}}
|
||||
} {1 {abort due to ROLLBACK} {}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
# 2014 November 1
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix scanstatus
|
||||
|
||||
ifcapable !scanstatus {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t2 VALUES('a', 'b');
|
||||
INSERT INTO t2 VALUES('c', 'd');
|
||||
INSERT INTO t2 VALUES('e', 'f');
|
||||
}
|
||||
|
||||
proc do_scanstatus_test {tn res} {
|
||||
set stmt [db_last_stmt_ptr db]
|
||||
set idx 0
|
||||
set ret [list]
|
||||
while {1} {
|
||||
set r [sqlite3_stmt_scanstatus $stmt $idx]
|
||||
if {[llength $r]==0} break
|
||||
lappend ret {*}$r
|
||||
incr idx
|
||||
}
|
||||
|
||||
uplevel [list do_test $tn [list set {} $ret] [list {*}$res]]
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 { SELECT count(*) FROM t1, t2; } 6
|
||||
do_scanstatus_test 1.2 {
|
||||
nLoop 1 nVisit 2 nEst 1048576.0 zName t1 zExplain {SCAN TABLE t1}
|
||||
nLoop 2 nVisit 6 nEst 1048576.0 zName t2 zExplain {SCAN TABLE t2}
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
ANALYZE;
|
||||
SELECT count(*) FROM t1, t2;
|
||||
} 6
|
||||
do_scanstatus_test 1.4 {
|
||||
nLoop 1 nVisit 2 nEst 2.0 zName t1 zExplain {SCAN TABLE t1}
|
||||
nLoop 2 nVisit 6 nEst 3.0 zName t2 zExplain {SCAN TABLE t2}
|
||||
}
|
||||
|
||||
do_execsql_test 1.5 { ANALYZE }
|
||||
do_execsql_test 1.6 {
|
||||
SELECT count(*) FROM t1, t2 WHERE t2.rowid>1;
|
||||
} 4
|
||||
do_scanstatus_test 1.7 {
|
||||
nLoop 1 nVisit 2 nEst 2.0 zName t2 zExplain
|
||||
{SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid>?)}
|
||||
nLoop 2 nVisit 4 nEst 2.0 zName t1 zExplain {SCAN TABLE t1}
|
||||
}
|
||||
|
||||
do_execsql_test 1.8 {
|
||||
SELECT count(*) FROM t1, t2 WHERE t2.rowid>1;
|
||||
} 4
|
||||
|
||||
do_scanstatus_test 1.9 {
|
||||
nLoop 2 nVisit 4 nEst 2.0 zName t2 zExplain
|
||||
{SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid>?)}
|
||||
nLoop 4 nVisit 8 nEst 2.0 zName t1 zExplain {SCAN TABLE t1}
|
||||
}
|
||||
|
||||
do_test 1.9 {
|
||||
sqlite3_stmt_scanstatus_reset [db_last_stmt_ptr db]
|
||||
} {}
|
||||
|
||||
do_scanstatus_test 1.10 {
|
||||
nLoop 0 nVisit 0 nEst 2.0 zName t2 zExplain
|
||||
{SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid>?)}
|
||||
nLoop 0 nVisit 0 nEst 2.0 zName t1 zExplain {SCAN TABLE t1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Try a few different types of scans.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE x1(i INTEGER PRIMARY KEY, j);
|
||||
INSERT INTO x1 VALUES(1, 'one');
|
||||
INSERT INTO x1 VALUES(2, 'two');
|
||||
INSERT INTO x1 VALUES(3, 'three');
|
||||
INSERT INTO x1 VALUES(4, 'four');
|
||||
CREATE INDEX x1j ON x1(j);
|
||||
|
||||
SELECT * FROM x1 WHERE i=2;
|
||||
} {2 two}
|
||||
|
||||
do_scanstatus_test 2.2 {
|
||||
nLoop 1 nVisit 1 nEst 1.0 zName x1
|
||||
zExplain {SEARCH TABLE x1 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.3.1 {
|
||||
SELECT * FROM x1 WHERE j='two'
|
||||
} {2 two}
|
||||
do_scanstatus_test 2.3.2 {
|
||||
nLoop 1 nVisit 1 nEst 10.0 zName x1j
|
||||
zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j=?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.4.1 {
|
||||
SELECT * FROM x1 WHERE j<'two'
|
||||
} {4 four 1 one 3 three}
|
||||
do_scanstatus_test 2.4.2 {
|
||||
nLoop 1 nVisit 3 nEst 262144.0 zName x1j
|
||||
zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j<?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.5.1 {
|
||||
SELECT * FROM x1 WHERE j>='two'
|
||||
} {2 two}
|
||||
do_scanstatus_test 2.5.2 {
|
||||
nLoop 1 nVisit 1 nEst 262144.0 zName x1j
|
||||
zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j>?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.6.1 {
|
||||
SELECT * FROM x1 WHERE j BETWEEN 'three' AND 'two'
|
||||
} {3 three 2 two}
|
||||
do_scanstatus_test 2.6.2 {
|
||||
nLoop 1 nVisit 2 nEst 16384.0 zName x1j
|
||||
zExplain {SEARCH TABLE x1 USING COVERING INDEX x1j (j>? AND j<?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.7.1 {
|
||||
CREATE TABLE x2(i INTEGER, j, k);
|
||||
INSERT INTO x2 SELECT i, j, i || ' ' || j FROM x1;
|
||||
CREATE INDEX x2j ON x2(j);
|
||||
CREATE INDEX x2ij ON x2(i, j);
|
||||
SELECT * FROM x2 WHERE j BETWEEN 'three' AND 'two'
|
||||
} {3 three {3 three} 2 two {2 two}}
|
||||
|
||||
do_scanstatus_test 2.7.2 {
|
||||
nLoop 1 nVisit 2 nEst 16384.0 zName x2j
|
||||
zExplain {SEARCH TABLE x2 USING INDEX x2j (j>? AND j<?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.8.1 {
|
||||
SELECT * FROM x2 WHERE i=1 AND j='two'
|
||||
}
|
||||
do_scanstatus_test 2.8.2 {
|
||||
nLoop 1 nVisit 0 nEst 8.0 zName x2ij
|
||||
zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.9.1 {
|
||||
SELECT * FROM x2 WHERE i=5 AND j='two'
|
||||
}
|
||||
do_scanstatus_test 2.9.2 {
|
||||
nLoop 1 nVisit 0 nEst 8.0 zName x2ij
|
||||
zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
|
||||
}
|
||||
|
||||
do_execsql_test 2.10.1 {
|
||||
SELECT * FROM x2 WHERE i=3 AND j='three'
|
||||
} {3 three {3 three}}
|
||||
do_scanstatus_test 2.10.2 {
|
||||
nLoop 1 nVisit 1 nEst 8.0 zName x2ij
|
||||
zExplain {SEARCH TABLE x2 USING INDEX x2ij (i=? AND j=?)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Try with queries that use the OR optimization.
|
||||
#
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE a1(a, b, c, d);
|
||||
CREATE INDEX a1a ON a1(a);
|
||||
CREATE INDEX a1bc ON a1(b, c);
|
||||
|
||||
WITH d(x) AS (SELECT 1 UNION ALL SELECT x+1 AS n FROM d WHERE n<=100)
|
||||
INSERT INTO a1 SELECT x, x, x, x FROM d;
|
||||
}
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
SELECT d FROM a1 WHERE (a=4 OR b=13)
|
||||
} {4 13}
|
||||
do_scanstatus_test 3.2.2 {
|
||||
nLoop 1 nVisit 1 nEst 10.0 zName a1a
|
||||
zExplain {SEARCH TABLE a1 USING INDEX a1a (a=?)}
|
||||
nLoop 1 nVisit 1 nEst 10.0 zName a1bc
|
||||
zExplain {SEARCH TABLE a1 USING INDEX a1bc (b=?)}
|
||||
}
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
SELECT count(*) FROM a1 WHERE (a BETWEEN 4 AND 12) OR (b BETWEEN 40 AND 60)
|
||||
} {30}
|
||||
do_scanstatus_test 3.2.2 {
|
||||
nLoop 1 nVisit 9 nEst 16384.0 zName a1a
|
||||
zExplain {SEARCH TABLE a1 USING INDEX a1a (a>? AND a<?)}
|
||||
nLoop 1 nVisit 21 nEst 16384.0 zName a1bc
|
||||
zExplain {SEARCH TABLE a1 USING INDEX a1bc (b>? AND b<?)}
|
||||
}
|
||||
|
||||
do_execsql_test 3.3.1 {
|
||||
SELECT count(*) FROM a1 AS x, a1 AS y
|
||||
WHERE (x.a BETWEEN 4 AND 12) AND (y.b BETWEEN 1 AND 10)
|
||||
} {90}
|
||||
do_scanstatus_test 3.2.2 {
|
||||
nLoop 1 nVisit 10 nEst 16384.0 zName a1bc
|
||||
zExplain {SEARCH TABLE a1 AS y USING COVERING INDEX a1bc (b>? AND b<?)}
|
||||
nLoop 10 nVisit 90 nEst 16384.0 zName a1a
|
||||
zExplain {SEARCH TABLE a1 AS x USING COVERING INDEX a1a (a>? AND a<?)}
|
||||
}
|
||||
|
||||
do_execsql_test 3.4.1 {
|
||||
SELECT count(*) FROM a1 WHERE a IN (1, 5, 10, 15);
|
||||
} {4}
|
||||
do_scanstatus_test 3.4.2 {
|
||||
nLoop 1 nVisit 4 nEst 40.0 zName a1a
|
||||
zExplain {SEARCH TABLE a1 USING COVERING INDEX a1a (a=?)}
|
||||
}
|
||||
|
||||
do_execsql_test 3.4.1 {
|
||||
SELECT count(*) FROM a1 WHERE rowid IN (1, 5, 10, 15);
|
||||
} {4}
|
||||
do_scanstatus_test 3.4.2 {
|
||||
nLoop 1 nVisit 4 nEst 4.0 zName a1
|
||||
zExplain {SEARCH TABLE a1 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that scanstatus() data is not available for searches performed
|
||||
# by triggers.
|
||||
#
|
||||
# It is available for searches performed as part of FK processing, but
|
||||
# not FK action processing.
|
||||
#
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t2(x PRIMARY KEY, y, z);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT * FROM t2 WHERE x BETWEEN 20 AND 40;
|
||||
END;
|
||||
WITH d(x) AS (SELECT 1 UNION ALL SELECT x+1 AS n FROM d WHERE n<=100)
|
||||
INSERT INTO t2 SELECT x, x*2, x*3 FROM d;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1.1 { INSERT INTO t1 VALUES(1, 2, 3); }
|
||||
do_scanstatus_test 4.1.2 { }
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
CREATE TABLE p1(x PRIMARY KEY);
|
||||
INSERT INTO p1 VALUES(1), (2), (3), (4);
|
||||
CREATE TABLE c1(y REFERENCES p1);
|
||||
INSERT INTO c1 VALUES(1), (2), (3);
|
||||
PRAGMA foreign_keys=on;
|
||||
}
|
||||
do_execsql_test 4.2.1 { DELETE FROM p1 WHERE x=4 }
|
||||
do_scanstatus_test 4.2.2 {
|
||||
nLoop 1 nVisit 1 nEst 1.0 zName sqlite_autoindex_p1_1
|
||||
zExplain {SEARCH TABLE p1 USING INDEX sqlite_autoindex_p1_1 (x=?)}
|
||||
|
||||
nLoop 1 nVisit 3 nEst 262144.0 zName c1 zExplain {SCAN TABLE c1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Further tests of different scan types.
|
||||
#
|
||||
reset_db
|
||||
proc tochar {i} {
|
||||
set alphabet {a b c d e f g h i j k l m n o p q r s t u v w x y z}
|
||||
return [lindex $alphabet [expr $i % [llength $alphabet]]]
|
||||
}
|
||||
db func tochar tochar
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(0, 1, 'a');
|
||||
INSERT INTO t1 VALUES(1, 0, 'b');
|
||||
INSERT INTO t1 VALUES(2, 1, 'c');
|
||||
INSERT INTO t1 VALUES(3, 0, 'd');
|
||||
INSERT INTO t1 VALUES(4, 1, 'e');
|
||||
INSERT INTO t1 VALUES(5, 0, 'a');
|
||||
INSERT INTO t1 VALUES(6, 1, 'b');
|
||||
INSERT INTO t1 VALUES(7, 0, 'c');
|
||||
INSERT INTO t1 VALUES(8, 1, 'd');
|
||||
INSERT INTO t1 VALUES(9, 0, 'e');
|
||||
CREATE INDEX t1bc ON t1(b, c);
|
||||
|
||||
CREATE TABLE t2(x, y);
|
||||
CREATE INDEX t2xy ON t2(x, y);
|
||||
WITH data(i, x, y) AS (
|
||||
SELECT 0, 0, tochar(0)
|
||||
UNION ALL
|
||||
SELECT i+1, (i+1)%2, tochar(i+1) FROM data WHERE i<500
|
||||
) INSERT INTO t2 SELECT x, y FROM data;
|
||||
|
||||
CREATE TABLE t3(x, y);
|
||||
INSERT INTO t3 SELECT * FROM t2;
|
||||
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
do_execsql_test 5.1.1 {
|
||||
SELECT count(*) FROM t1 WHERE a IN (SELECT b FROM t1 AS ii)
|
||||
} {2}
|
||||
do_scanstatus_test 5.1.2 {
|
||||
nLoop 1 nVisit 10 nEst 10.0 zName t1bc
|
||||
zExplain {SCAN TABLE t1 AS ii USING COVERING INDEX t1bc}
|
||||
nLoop 1 nVisit 2 nEst 8.0 zName sqlite_autoindex_t1_1
|
||||
zExplain {SEARCH TABLE t1 USING COVERING INDEX sqlite_autoindex_t1_1 (a=?)}
|
||||
}
|
||||
|
||||
do_execsql_test 5.2.1 {
|
||||
SELECT count(*) FROM t1 WHERE a IN (0, 1)
|
||||
} {2}
|
||||
do_scanstatus_test 5.2.2 {
|
||||
nLoop 1 nVisit 2 nEst 2.0 zName sqlite_autoindex_t1_1
|
||||
zExplain {SEARCH TABLE t1 USING COVERING INDEX sqlite_autoindex_t1_1 (a=?)}
|
||||
}
|
||||
|
||||
do_eqp_test 5.3.1 {
|
||||
SELECT count(*) FROM t2 WHERE y = 'j';
|
||||
} {0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)}}
|
||||
do_execsql_test 5.3.2 {
|
||||
SELECT count(*) FROM t2 WHERE y = 'j';
|
||||
} {19}
|
||||
do_scanstatus_test 5.3.3 {
|
||||
nLoop 1 nVisit 19 nEst 56.0 zName t2xy zExplain
|
||||
{SEARCH TABLE t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)}
|
||||
}
|
||||
|
||||
do_eqp_test 5.4.1 {
|
||||
SELECT count(*) FROM t1, t2 WHERE y = c;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 USING COVERING INDEX t1bc}
|
||||
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)}
|
||||
}
|
||||
do_execsql_test 5.4.2 {
|
||||
SELECT count(*) FROM t1, t2 WHERE y = c;
|
||||
} {200}
|
||||
do_scanstatus_test 5.4.3 {
|
||||
nLoop 1 nVisit 10 nEst 10.0 zName t1bc
|
||||
zExplain {SCAN TABLE t1 USING COVERING INDEX t1bc}
|
||||
nLoop 10 nVisit 200 nEst 56.0 zName t2xy
|
||||
zExplain {SEARCH TABLE t2 USING COVERING INDEX t2xy (ANY(x) AND y=?)}
|
||||
}
|
||||
|
||||
do_eqp_test 5.5.1 {
|
||||
SELECT count(*) FROM t1, t3 WHERE y = c;
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t3}
|
||||
0 1 0 {SEARCH TABLE t1 USING AUTOMATIC COVERING INDEX (c=?)}
|
||||
}
|
||||
do_execsql_test 5.5.2 {
|
||||
SELECT count(*) FROM t1, t3 WHERE y = c;
|
||||
} {200}
|
||||
do_scanstatus_test 5.5.3 {
|
||||
nLoop 1 nVisit 501 nEst 480.0 zName t3 zExplain {SCAN TABLE t3}
|
||||
nLoop 501 nVisit 200 nEst 20.0 zName auto-index zExplain
|
||||
{SEARCH TABLE t1 USING AUTOMATIC COVERING INDEX (c=?)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Virtual table scans
|
||||
#
|
||||
ifcapable fts3 {
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts4;
|
||||
INSERT INTO ft1 VALUES('a d c f g h e i f c');
|
||||
INSERT INTO ft1 VALUES('g c h b g b f f f g');
|
||||
INSERT INTO ft1 VALUES('h h c c h f a e d d');
|
||||
INSERT INTO ft1 VALUES('e j i j i e b c f g');
|
||||
INSERT INTO ft1 VALUES('g f b g j c h a d f');
|
||||
INSERT INTO ft1 VALUES('j i a e g f a i a c');
|
||||
INSERT INTO ft1 VALUES('f d g g j j c a h g');
|
||||
INSERT INTO ft1 VALUES('b d h a d j j j b i');
|
||||
INSERT INTO ft1 VALUES('j e a b j e c b c i');
|
||||
INSERT INTO ft1 VALUES('a d e f b j j c g d');
|
||||
}
|
||||
do_execsql_test 6.1.1 {
|
||||
SELECT count(*) FROM ft1 WHERE ft1 MATCH 'd'
|
||||
} {6}
|
||||
do_scanstatus_test 6.1.2 {
|
||||
nLoop 1 nVisit 6 nEst 24.0 zName ft1 zExplain
|
||||
{SCAN TABLE ft1 VIRTUAL TABLE INDEX 3:}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,60 @@
|
||||
# 2014-12-05
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Open two database connections on the same database in shared cache
|
||||
# mode. Hold one open while repeatedly closing, reopening, and using
|
||||
# the second.
|
||||
#
|
||||
# This test is designed to demostrate that the fix for ticket
|
||||
# [e4a18565a36884b00edf66541f38c693827968ab] works.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
if {[run_thread_tests]==0} { finish_test ; return }
|
||||
db close
|
||||
set ::testprefix sharedB
|
||||
|
||||
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_test 1.1 {
|
||||
sqlite3 db1 test.db
|
||||
sqlite3 db2 test.db
|
||||
|
||||
db1 eval {
|
||||
CREATE TABLE t1(x,y TEXT COLLATE nocase);
|
||||
WITH RECURSIVE
|
||||
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<100)
|
||||
INSERT INTO t1(x,y) SELECT i, printf('x%03dy',i) FROM c;
|
||||
CREATE INDEX t1yx ON t1(y,x);
|
||||
}
|
||||
db2 eval {
|
||||
SELECT x FROM t1 WHERE y='X014Y';
|
||||
}
|
||||
} {14}
|
||||
|
||||
for {set j 1} {$j<=100} {incr j} {
|
||||
do_test 1.2.$j {
|
||||
db2 close
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {
|
||||
SELECT x FROM t1 WHERE y='X014Y';
|
||||
}
|
||||
} {14}
|
||||
}
|
||||
|
||||
db1 close
|
||||
db2 close
|
||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||
finish_test
|
||||
@@ -446,9 +446,15 @@ do_malloc_test shared_err-8 -tclprep {
|
||||
} {1}
|
||||
db2 close
|
||||
}
|
||||
|
||||
# When this test case was written, OOM errors in write statements would
|
||||
# cause transaction rollback, which would trip cursors in other statements,
|
||||
# aborting them. This no longer happens.
|
||||
#
|
||||
do_test shared_malloc-8.X {
|
||||
# Test that one or more queries were aborted due to the malloc() failure.
|
||||
expr $::aborted>=1
|
||||
# expr $::aborted>=1
|
||||
expr $::aborted==0
|
||||
} {1}
|
||||
|
||||
# This test is designed to catch a specific bug that was present during
|
||||
|
||||
+16
-15
@@ -45,20 +45,21 @@ do_test shell1-1.1.1 {
|
||||
list $rc \
|
||||
[regexp {Error: unknown option: -bad} $res]
|
||||
} {1 1}
|
||||
do_test shell1-1.1.1b {
|
||||
set res [catchcmd "test.db -bad" ""]
|
||||
set rc [lindex $res 0]
|
||||
list $rc \
|
||||
[regexp {Error: unknown option: -bad} $res]
|
||||
} {1 1}
|
||||
# error on extra options
|
||||
do_test shell1-1.1.2 {
|
||||
set res [catchcmd "-bad test.db \"select 3\" \"select 4\"" ""]
|
||||
set rc [lindex $res 0]
|
||||
list $rc \
|
||||
[regexp {Error: too many options: "select 4"} $res]
|
||||
} {1 1}
|
||||
catchcmd "test.db \"select 3\" \"select 4\"" ""
|
||||
} {0 {3
|
||||
4}}
|
||||
# error on extra options
|
||||
do_test shell1-1.1.3 {
|
||||
set res [catchcmd "-bad FOO test.db BAD" ".quit"]
|
||||
set rc [lindex $res 0]
|
||||
list $rc \
|
||||
[regexp {Error: too many options: "BAD"} $res]
|
||||
} {1 1}
|
||||
catchcmd "test.db FOO test.db BAD" ".quit"
|
||||
} {1 {Error: near "FOO": syntax error}}
|
||||
|
||||
# -help
|
||||
do_test shell1-1.2.1 {
|
||||
@@ -75,11 +76,11 @@ do_test shell1-1.3.1 {
|
||||
catchcmd "-init FOO test.db" ""
|
||||
} {0 {}}
|
||||
do_test shell1-1.3.2 {
|
||||
set res [catchcmd "-init FOO test.db .quit BAD" ""]
|
||||
set rc [lindex $res 0]
|
||||
list $rc \
|
||||
[regexp {Error: too many options: "BAD"} $res]
|
||||
} {1 1}
|
||||
catchcmd "-init FOO test.db .quit BAD" ""
|
||||
} {0 {}}
|
||||
do_test shell1-1.3.3 {
|
||||
catchcmd "-init FOO test.db BAD .quit" ""
|
||||
} {1 {Error: near "BAD": syntax error}}
|
||||
|
||||
# -echo print commands before execution
|
||||
do_test shell1-1.4.1 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user