Merge branch 'sqlite-release' into v2-integration

Conflicts:
	Makefile.in
This commit is contained in:
Stephen Lombardo 2011-02-16 16:24:07 -05:00
commit f003dfad13
227 changed files with 27389 additions and 4361 deletions

View File

@ -27,7 +27,7 @@ BCC = @BUILD_CC@ @BUILD_CFLAGS@
# will run on the target platform. (BCC and TCC are usually the
# same unless your are cross-compiling.)
#
TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src
TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src -I${TOP}/ext/rtree
# Define this for the autoconf-based build, so that the code knows it can
# include the generated config.h
@ -60,10 +60,6 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
#
TCC += -DSQLITE_THREADSAFE=@SQLITE_THREADSAFE@
# Do threads override each others locks by default (1), or do we test (-1)
#
TCC += -DSQLITE_THREAD_OVERRIDE_LOCK=@THREADSOVERRIDELOCKS@
# Any target libraries which libsqlite must be linked against
#
TLIBS = @LIBS@
@ -177,7 +173,8 @@ USE_AMALGAMATION = @USE_AMALGAMATION@
#
LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
callback.lo complete.lo ctime.lo date.lo delete.lo expr.lo fault.lo fkey.lo \
callback.lo complete.lo ctime.lo date.lo delete.lo \
expr.lo fault.lo fkey.lo \
fts3.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \
fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo fts3_write.lo \
func.lo global.lo hash.lo \
@ -374,12 +371,16 @@ TESTSRC = \
$(TOP)/src/test_intarray.c \
$(TOP)/src/test_journal.c \
$(TOP)/src/test_malloc.c \
$(TOP)/src/test_multiplex.c \
$(TOP)/src/test_mutex.c \
$(TOP)/src/test_onefile.c \
$(TOP)/src/test_osinst.c \
$(TOP)/src/test_pcache.c \
$(TOP)/src/test_quota.c \
$(TOP)/src/test_rtree.c \
$(TOP)/src/test_schema.c \
$(TOP)/src/test_server.c \
$(TOP)/src/test_superlock.c \
$(TOP)/src/test_stat.c \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
@ -471,6 +472,8 @@ EXTHDR += \
$(TOP)/ext/rtree/rtree.h
EXTHDR += \
$(TOP)/ext/icu/sqliteicu.h
EXTHDR += \
$(TOP)/ext/rtree/sqlite3rtree.h
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.

View File

@ -1 +1 @@
3.7.2
3.7.5

76
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.62 for sqlite 3.7.2.
# Generated by GNU Autoconf 2.62 for sqlite 3.7.5.
#
# 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.7.2'
PACKAGE_STRING='sqlite 3.7.2'
PACKAGE_VERSION='3.7.5'
PACKAGE_STRING='sqlite 3.7.5'
PACKAGE_BUGREPORT=''
# Factoring default headers for most tests.
@ -869,7 +869,6 @@ VERSION_NUMBER
BUILD_CC
SQLITE_THREADSAFE
XTHREADCONNECT
THREADSOVERRIDELOCKS
ALLOWRELEASE
TEMP_STORE
BUILD_EXEEXT
@ -912,7 +911,6 @@ enable_largefile
with_hints
enable_threadsafe
enable_cross_thread_connections
enable_threads_override_locks
enable_releasemode
enable_tempstore
enable_tcl
@ -1487,7 +1485,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.7.2 to adapt to many kinds of systems.
\`configure' configures sqlite 3.7.5 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1552,7 +1550,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.7.2:";;
short | recursive ) echo "Configuration of sqlite 3.7.5:";;
esac
cat <<\_ACEOF
@ -1569,8 +1567,6 @@ Optional Features:
--enable-threadsafe Support threadsafe operation
--enable-cross-thread-connections
Allow connection sharing across threads
--enable-threads-override-locks
Threads can override each others locks
--enable-releasemode Support libtool link to release mode
--enable-tempstore Use an in-ram database for temporary tables
(never,no,yes,always)
@ -1670,7 +1666,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.7.2
sqlite configure 3.7.5
generated by GNU Autoconf 2.62
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@ -1684,7 +1680,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.7.2, which was
It was created by sqlite $as_me 3.7.5, which was
generated by GNU Autoconf 2.62. Invocation command line was
$ $0 $@
@ -3738,13 +3734,13 @@ if test "${lt_cv_nm_interface+set}" = set; then
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:3741: $ac_compile\"" >&5)
(eval echo "\"\$as_me:3737: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:3744: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval echo "\"\$as_me:3740: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:3747: output\"" >&5)
(eval echo "\"\$as_me:3743: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@ -4966,7 +4962,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 4969 "configure"' > conftest.$ac_ext
echo '#line 4965 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@ -6835,11 +6831,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:6838: $lt_compile\"" >&5)
(eval echo "\"\$as_me:6834: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:6842: \$? = $ac_status" >&5
echo "$as_me:6838: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -7174,11 +7170,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7177: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7173: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7181: \$? = $ac_status" >&5
echo "$as_me:7177: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -7279,11 +7275,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7282: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7278: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7286: \$? = $ac_status" >&5
echo "$as_me:7282: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -7334,11 +7330,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7337: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7333: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7341: \$? = $ac_status" >&5
echo "$as_me:7337: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -10147,7 +10143,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 10150 "configure"
#line 10146 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -10243,7 +10239,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 10246 "configure"
#line 10242 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -12580,32 +12576,6 @@ $as_echo "yes" >&6; }
fi
##########
# Do we want to set threadsOverrideEachOthersLocks variable to be 1 (true) by
# default. Normally, a test at runtime is performed to determine the
# appropriate value of this variable. Use this option only if you're sure that
# threads can safely override each others locks in all runtime situations.
#
# Check whether --enable-threads-override-locks was given.
if test "${enable_threads_override_locks+set}" = set; then
enableval=$enable_threads_override_locks;
else
enable_threads_override_locks=no
fi
{ $as_echo "$as_me:$LINENO: checking whether threads can override each others locks" >&5
$as_echo_n "checking whether threads can override each others locks... " >&6; }
if test "$enable_threads_override_locks" = "no"; then
THREADSOVERRIDELOCKS='-1'
{ $as_echo "$as_me:$LINENO: result: no" >&5
$as_echo "no" >&6; }
else
THREADSOVERRIDELOCKS='1'
{ $as_echo "$as_me:$LINENO: result: yes" >&5
$as_echo "yes" >&6; }
fi
##########
# Do we want to support release
#
@ -13972,7 +13942,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.7.2, which was
This file was extended by sqlite $as_me 3.7.5, which was
generated by GNU Autoconf 2.62. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -14025,7 +13995,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_version="\\
sqlite config.status 3.7.2
sqlite config.status 3.7.5
configured by $0, generated by GNU Autoconf 2.62,
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

View File

@ -268,24 +268,6 @@ else
fi
AC_SUBST(XTHREADCONNECT)
##########
# Do we want to set threadsOverrideEachOthersLocks variable to be 1 (true) by
# default. Normally, a test at runtime is performed to determine the
# appropriate value of this variable. Use this option only if you're sure that
# threads can safely override each others locks in all runtime situations.
#
AC_ARG_ENABLE(threads-override-locks,
AC_HELP_STRING([--enable-threads-override-locks],[Threads can override each others locks]),,enable_threads_override_locks=no)
AC_MSG_CHECKING([whether threads can override each others locks])
if test "$enable_threads_override_locks" = "no"; then
THREADSOVERRIDELOCKS='-1'
AC_MSG_RESULT([no])
else
THREADSOVERRIDELOCKS='1'
AC_MSG_RESULT([yes])
fi
AC_SUBST(THREADSOVERRIDELOCKS)
##########
# Do we want to support release
#

View File

@ -306,8 +306,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "fts2.h"
#include "fts2_hash.h"
#include "fts2_tokenizer.h"
@ -345,13 +343,13 @@ SQLITE_EXTENSION_INIT1
*/
/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */
static int safe_isspace(char c){
return (c&0x80)==0 ? isspace(c) : 0;
return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
}
static int safe_tolower(char c){
return (c&0x80)==0 ? tolower(c) : c;
return (c>='A' && c<='Z') ? (c - 'A' + 'a') : c;
}
static int safe_isalnum(char c){
return (c&0x80)==0 ? isalnum(c) : 0;
return (c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z');
}
typedef enum DocListType {

View File

@ -29,7 +29,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "fts2_tokenizer.h"

View File

@ -29,7 +29,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "fts2_tokenizer.h"
@ -89,7 +88,8 @@ static int simpleCreate(
/* Mark non-alphanumeric ASCII characters as delimiters */
int i;
for(i=1; i<0x80; i++){
t->delim[i] = !isalnum(i);
t->delim[i] = !((i>='0' && i<='9') || (i>='A' && i<='Z') ||
(i>='a' && i<='z'));
}
}
@ -191,7 +191,7 @@ static int simpleNext(
** case-insensitivity.
*/
unsigned char ch = p[iStartOffset+i];
c->pToken[i] = ch<0x80 ? tolower(ch) : ch;
c->pToken[i] = (ch>='A' && ch<='Z') ? (ch - 'A' + 'a') : ch;
}
*ppToken = c->pToken;
*pnBytes = n;

File diff suppressed because it is too large Load Diff

View File

@ -77,8 +77,14 @@
** Macros indicating that conditional expressions are always true or
** false.
*/
#ifdef SQLITE_COVERAGE_TEST
# define ALWAYS(x) (1)
# define NEVER(X) (0)
#else
# define ALWAYS(x) (x)
# define NEVER(X) (x)
#endif
/*
** Internal types used by SQLite.
*/
@ -96,8 +102,12 @@ typedef struct Fts3Table Fts3Table;
typedef struct Fts3Cursor Fts3Cursor;
typedef struct Fts3Expr Fts3Expr;
typedef struct Fts3Phrase Fts3Phrase;
typedef struct Fts3SegReader Fts3SegReader;
typedef struct Fts3PhraseToken Fts3PhraseToken;
typedef struct Fts3SegFilter Fts3SegFilter;
typedef struct Fts3DeferredToken Fts3DeferredToken;
typedef struct Fts3SegReader Fts3SegReader;
typedef struct Fts3SegReaderArray Fts3SegReaderArray;
/*
** A connection to a fulltext index is an instance of the following
@ -118,22 +128,14 @@ struct Fts3Table {
/* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call.
*/
sqlite3_stmt *aStmt[25];
/* Pointer to string containing the SQL:
**
** "SELECT block FROM %_segments WHERE blockid BETWEEN ? AND ?
** ORDER BY blockid"
*/
char *zSelectLeaves;
int nLeavesStmt; /* Valid statements in aLeavesStmt */
int nLeavesTotal; /* Total number of prepared leaves stmts */
int nLeavesAlloc; /* Allocated size of aLeavesStmt */
sqlite3_stmt **aLeavesStmt; /* Array of prepared zSelectLeaves stmts */
sqlite3_stmt *aStmt[24];
int nNodeSize; /* Soft limit for node size */
u8 bHasContent; /* True if %_content table exists */
u8 bHasStat; /* True if %_stat table exists */
u8 bHasDocsize; /* True if %_docsize table exists */
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
/* The following hash table is used to buffer pending index updates during
** transactions. Variable nPendingData estimates the memory size of the
@ -160,14 +162,25 @@ struct Fts3Cursor {
u8 isRequireSeek; /* True if must seek pStmt to %_content row */
sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
Fts3Expr *pExpr; /* Parsed MATCH query string */
int nPhrase; /* Number of matchable phrases in query */
Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */
sqlite3_int64 iPrevId; /* Previous id read from aDoclist */
char *pNextId; /* Pointer into the body of aDoclist */
char *aDoclist; /* List of docids for full-text queries */
int nDoclist; /* Size of buffer at aDoclist */
int eEvalmode; /* An FTS3_EVAL_XX constant */
int nRowAvg; /* Average size of database rows, in pages */
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
u32 *aMatchinfo; /* Information about most recent match */
int nMatchinfo; /* Number of elements in aMatchinfo[] */
char *zMatchinfo; /* Matchinfo specification */
};
#define FTS3_EVAL_FILTER 0
#define FTS3_EVAL_NEXT 1
#define FTS3_EVAL_MATCHINFO 2
/*
** The Fts3Cursor.eSearch member is always set to one of the following.
** Actualy, Fts3Cursor.eSearch can be greater than or equal to
@ -190,18 +203,30 @@ struct Fts3Cursor {
/*
** A "phrase" is a sequence of one or more tokens that must match in
** sequence. A single token is the base case and the most common case.
** For a sequence of tokens contained in "...", nToken will be the number
** of tokens in the string.
** For a sequence of tokens contained in double-quotes (i.e. "one two three")
** nToken will be the number of tokens in the string.
**
** The nDocMatch and nMatch variables contain data that may be used by the
** matchinfo() function. They are populated when the full-text index is
** queried for hits on the phrase. If one or more tokens in the phrase
** are deferred, the nDocMatch and nMatch variables are populated based
** on the assumption that the
*/
struct Fts3PhraseToken {
char *z; /* Text of the token */
int n; /* Number of bytes in buffer z */
int isPrefix; /* True if token ends with a "*" character */
int bFulltext; /* True if full-text index was used */
Fts3SegReaderArray *pArray; /* Segment-reader for this token */
Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
};
struct Fts3Phrase {
/* Variables populated by fts3_expr.c when parsing a MATCH expression */
int nToken; /* Number of tokens in the phrase */
int iColumn; /* Index of column this phrase must match */
int isNot; /* Phrase prefixed by unary not (-) operator */
struct PhraseToken {
char *z; /* Text of the token */
int n; /* Number of bytes in buffer pointed to by z */
int isPrefix; /* True if token ends in with a "*" character */
} aToken[1]; /* One entry for each token in the phrase */
Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
};
/*
@ -251,28 +276,34 @@ struct Fts3Expr {
#define FTSQUERY_PHRASE 5
/* fts3_init.c */
int sqlite3Fts3DeleteVtab(int, sqlite3_vtab *);
int sqlite3Fts3InitVtab(int, sqlite3*, void*, int, const char*const*,
sqlite3_vtab **, char **);
/* fts3_write.c */
int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
int sqlite3Fts3PendingTermsFlush(Fts3Table *);
void sqlite3Fts3PendingTermsClear(Fts3Table *);
int sqlite3Fts3Optimize(Fts3Table *);
int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64,
int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**);
void sqlite3Fts3SegReaderFree(Fts3Table *, Fts3SegReader *);
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
int sqlite3Fts3SegReaderIterate(
Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *,
int (*)(Fts3Table *, void *, char *, int, char *, int), void *
);
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char const**, int*);
int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *);
int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **);
int sqlite3Fts3MatchinfoDocsizeLocal(Fts3Cursor*, u32*);
int sqlite3Fts3MatchinfoDocsizeGlobal(Fts3Cursor*, u32*);
int sqlite3Fts3ReadLock(Fts3Table *);
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*);
int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **);
void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *);
int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *);
void sqlite3Fts3SegmentsClose(Fts3Table *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
@ -296,22 +327,24 @@ int sqlite3Fts3VarintLen(sqlite3_uint64);
void sqlite3Fts3Dequote(char *);
char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int);
int sqlite3Fts3ExprLoadDoclist(Fts3Table *, Fts3Expr *);
int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *, Fts3Expr *);
int sqlite3Fts3ExprLoadFtDoclist(Fts3Cursor *, Fts3Expr *, char **, int *);
int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int);
/* fts3_tokenizer.c */
const char *sqlite3Fts3NextToken(const char *, int *);
int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
int sqlite3Fts3InitTokenizer(Fts3Hash *pHash,
const char *, sqlite3_tokenizer **, const char **, char **
int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
sqlite3_tokenizer **, char **
);
int sqlite3Fts3IsIdChar(char);
/* fts3_snippet.c */
void sqlite3Fts3Offsets(sqlite3_context*, Fts3Cursor*);
void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const char *,
const char *, const char *, int, int
);
void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *);
void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
/* fts3_expr.c */
int sqlite3Fts3ExprParse(sqlite3_tokenizer *,

View File

@ -106,6 +106,18 @@ static int fts3isspace(char c){
return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
}
/*
** Allocate nByte bytes of memory using sqlite3_malloc(). If successful,
** zero the memory before returning a pointer to it. If unsuccessful,
** return NULL.
*/
static void *fts3MallocZero(int nByte){
void *pRet = sqlite3_malloc(nByte);
if( pRet ) memset(pRet, 0, nByte);
return pRet;
}
/*
** Extract the next token from buffer z (length n) using the tokenizer
** and other information (column names etc.) in pParse. Create an Fts3Expr
@ -143,11 +155,10 @@ static int getNextToken(
if( rc==SQLITE_OK ){
nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
pRet = (Fts3Expr *)sqlite3_malloc(nByte);
pRet = (Fts3Expr *)fts3MallocZero(nByte);
if( !pRet ){
rc = SQLITE_NOMEM;
}else{
memset(pRet, 0, nByte);
pRet->eType = FTSQUERY_PHRASE;
pRet->pPhrase = (Fts3Phrase *)&pRet[1];
pRet->pPhrase->nToken = 1;
@ -223,7 +234,7 @@ static int getNextString(
rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
p = fts3ReallocOrFree(p, nByte+ii*sizeof(struct PhraseToken));
p = fts3ReallocOrFree(p, nByte+ii*sizeof(Fts3PhraseToken));
zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken);
if( !p || !zTemp ){
goto no_mem;
@ -233,6 +244,7 @@ static int getNextString(
p->pPhrase = (Fts3Phrase *)&p[1];
}
p->pPhrase = (Fts3Phrase *)&p[1];
memset(&p->pPhrase->aToken[ii], 0, sizeof(Fts3PhraseToken));
p->pPhrase->nToken = ii+1;
p->pPhrase->aToken[ii].n = nToken;
memcpy(&zTemp[nTemp], zToken, nToken);
@ -254,7 +266,7 @@ static int getNextString(
char *zNew = NULL;
int nNew = 0;
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken);
nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(Fts3PhraseToken);
p = fts3ReallocOrFree(p, nByte + nTemp);
if( !p ){
goto no_mem;
@ -372,11 +384,10 @@ static int getNextNode(
if( fts3isspace(cNext)
|| cNext=='"' || cNext=='(' || cNext==')' || cNext==0
){
pRet = (Fts3Expr *)sqlite3_malloc(sizeof(Fts3Expr));
pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr));
if( !pRet ){
return SQLITE_NOMEM;
}
memset(pRet, 0, sizeof(Fts3Expr));
pRet->eType = pKey->eType;
pRet->nNear = nNear;
*ppExpr = pRet;
@ -394,7 +405,6 @@ static int getNextNode(
if( sqlite3_fts3_enable_parentheses ){
if( *zInput=='(' ){
int nConsumed;
int rc;
pParse->nNest++;
rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed);
if( rc==SQLITE_OK && !*ppExpr ){
@ -552,13 +562,12 @@ static int fts3ExprParse(
&& p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot
){
/* Create an implicit NOT operator. */
Fts3Expr *pNot = sqlite3_malloc(sizeof(Fts3Expr));
Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
if( !pNot ){
sqlite3Fts3ExprFree(p);
rc = SQLITE_NOMEM;
goto exprparse_out;
}
memset(pNot, 0, sizeof(Fts3Expr));
pNot->eType = FTSQUERY_NOT;
pNot->pRight = p;
if( pNotBranch ){
@ -586,13 +595,12 @@ static int fts3ExprParse(
/* Insert an implicit AND operator. */
Fts3Expr *pAnd;
assert( pRet && pPrev );
pAnd = sqlite3_malloc(sizeof(Fts3Expr));
pAnd = fts3MallocZero(sizeof(Fts3Expr));
if( !pAnd ){
sqlite3Fts3ExprFree(p);
rc = SQLITE_NOMEM;
goto exprparse_out;
}
memset(pAnd, 0, sizeof(Fts3Expr));
pAnd->eType = FTSQUERY_AND;
insertBinaryOperator(&pRet, pPrev, pAnd);
pPrev = pAnd;
@ -777,47 +785,53 @@ static int queryTestTokenizer(
}
/*
** This function is part of the test interface for the query parser. It
** writes a text representation of the query expression pExpr into the
** buffer pointed to by argument zBuf. It is assumed that zBuf is large
** enough to store the required text representation.
** Return a pointer to a buffer containing a text representation of the
** expression passed as the first argument. The buffer is obtained from
** sqlite3_malloc(). It is the responsibility of the caller to use
** sqlite3_free() to release the memory. If an OOM condition is encountered,
** NULL is returned.
**
** If the second argument is not NULL, then its contents are prepended to
** the returned expression text and then freed using sqlite3_free().
*/
static void exprToString(Fts3Expr *pExpr, char *zBuf){
static char *exprToString(Fts3Expr *pExpr, char *zBuf){
switch( pExpr->eType ){
case FTSQUERY_PHRASE: {
Fts3Phrase *pPhrase = pExpr->pPhrase;
int i;
zBuf += sprintf(zBuf, "PHRASE %d %d", pPhrase->iColumn, pPhrase->isNot);
for(i=0; i<pPhrase->nToken; i++){
zBuf += sprintf(zBuf," %.*s",pPhrase->aToken[i].n,pPhrase->aToken[i].z);
zBuf += sprintf(zBuf,"%s", (pPhrase->aToken[i].isPrefix?"+":""));
zBuf = sqlite3_mprintf(
"%zPHRASE %d %d", zBuf, pPhrase->iColumn, pPhrase->isNot);
for(i=0; zBuf && i<pPhrase->nToken; i++){
zBuf = sqlite3_mprintf("%z %.*s%s", zBuf,
pPhrase->aToken[i].n, pPhrase->aToken[i].z,
(pPhrase->aToken[i].isPrefix?"+":"")
);
}
return;
return zBuf;
}
case FTSQUERY_NEAR:
zBuf += sprintf(zBuf, "NEAR/%d ", pExpr->nNear);
zBuf = sqlite3_mprintf("%zNEAR/%d ", zBuf, pExpr->nNear);
break;
case FTSQUERY_NOT:
zBuf += sprintf(zBuf, "NOT ");
zBuf = sqlite3_mprintf("%zNOT ", zBuf);
break;
case FTSQUERY_AND:
zBuf += sprintf(zBuf, "AND ");
zBuf = sqlite3_mprintf("%zAND ", zBuf);
break;
case FTSQUERY_OR:
zBuf += sprintf(zBuf, "OR ");
zBuf = sqlite3_mprintf("%zOR ", zBuf);
break;
}
zBuf += sprintf(zBuf, "{");
exprToString(pExpr->pLeft, zBuf);
zBuf += strlen(zBuf);
zBuf += sprintf(zBuf, "} ");
if( zBuf ) zBuf = sqlite3_mprintf("%z{", zBuf);
if( zBuf ) zBuf = exprToString(pExpr->pLeft, zBuf);
if( zBuf ) zBuf = sqlite3_mprintf("%z} {", zBuf);
zBuf += sprintf(zBuf, "{");
exprToString(pExpr->pRight, zBuf);
zBuf += strlen(zBuf);
zBuf += sprintf(zBuf, "}");
if( zBuf ) zBuf = exprToString(pExpr->pRight, zBuf);
if( zBuf ) zBuf = sqlite3_mprintf("%z}", zBuf);
return zBuf;
}
/*
@ -848,6 +862,7 @@ static void fts3ExprTest(
int nCol;
int ii;
Fts3Expr *pExpr;
char *zBuf = 0;
sqlite3 *db = sqlite3_context_db_handle(context);
if( argc<3 ){
@ -890,18 +905,17 @@ static void fts3ExprTest(
rc = sqlite3Fts3ExprParse(
pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr
);
if( rc==SQLITE_NOMEM ){
sqlite3_result_error_nomem(context);
goto exprtest_out;
}else if( rc==SQLITE_OK ){
char zBuf[4096];
exprToString(pExpr, zBuf);
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
sqlite3Fts3ExprFree(pExpr);
}else{
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
sqlite3_result_error(context, "Error parsing expression", -1);
}else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
sqlite3_result_error_nomem(context);
}else{
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
sqlite3_free(zBuf);
}
sqlite3Fts3ExprFree(pExpr);
exprtest_out:
if( pModule && pTokenizer ){
rc = pModule->xDestroy(pTokenizer);

View File

@ -343,7 +343,7 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
int i, j;
char zReverse[28];
char *z, *z2;
if( nIn<3 || nIn>=sizeof(zReverse)-7 ){
if( nIn<3 || nIn>=(int)sizeof(zReverse)-7 ){
/* The word is too big or too small for the porter stemmer.
** Fallback to the copy stemmer */
copy_stemmer(zIn, nIn, zOut, pnOut);

View File

@ -17,6 +17,22 @@
#include <string.h>
#include <assert.h>
/*
** Characters that may appear in the second argument to matchinfo().
*/
#define FTS3_MATCHINFO_NPHRASE 'p' /* 1 value */
#define FTS3_MATCHINFO_NCOL 'c' /* 1 value */
#define FTS3_MATCHINFO_NDOC 'n' /* 1 value */
#define FTS3_MATCHINFO_AVGLENGTH 'a' /* nCol values */
#define FTS3_MATCHINFO_LENGTH 'l' /* nCol values */
#define FTS3_MATCHINFO_LCS 's' /* nCol values */
#define FTS3_MATCHINFO_HITS 'x' /* 3*nCol*nPhrase values */
/*
** The default value for the second argument to matchinfo().
*/
#define FTS3_MATCHINFO_DEFAULT "pcx"
/*
** Used as an fts3ExprIterate() context when loading phrase doclists to
@ -24,7 +40,7 @@
*/
typedef struct LoadDoclistCtx LoadDoclistCtx;
struct LoadDoclistCtx {
Fts3Table *pTab; /* FTS3 Table */
Fts3Cursor *pCsr; /* FTS3 Cursor */
int nPhrase; /* Number of phrases seen so far */
int nToken; /* Number of tokens seen so far */
};
@ -70,6 +86,8 @@ typedef struct MatchInfo MatchInfo;
struct MatchInfo {
Fts3Cursor *pCursor; /* FTS3 Cursor */
int nCol; /* Number of columns in table */
int nPhrase; /* Number of matchable phrases in query */
sqlite3_int64 nDoc; /* Number of docs in database */
u32 *aMatchinfo; /* Pre-allocated buffer */
};
@ -208,7 +226,7 @@ static int fts3ExprNearTrim(Fts3Expr *pExpr){
** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
** fts3ExprLoadDoclists().
*/
static int fts3ExprLoadDoclistsCb1(Fts3Expr *pExpr, int iPhrase, void *ctx){
static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
int rc = SQLITE_OK;
LoadDoclistCtx *p = (LoadDoclistCtx *)ctx;
@ -218,7 +236,7 @@ static int fts3ExprLoadDoclistsCb1(Fts3Expr *pExpr, int iPhrase, void *ctx){
p->nToken += pExpr->pPhrase->nToken;
if( pExpr->isLoaded==0 ){
rc = sqlite3Fts3ExprLoadDoclist(p->pTab, pExpr);
rc = sqlite3Fts3ExprLoadDoclist(p->pCsr, pExpr);
pExpr->isLoaded = 1;
if( rc==SQLITE_OK ){
rc = fts3ExprNearTrim(pExpr);
@ -228,22 +246,6 @@ static int fts3ExprLoadDoclistsCb1(Fts3Expr *pExpr, int iPhrase, void *ctx){
return rc;
}
/*
** This is an fts3ExprIterate() callback used while loading the doclists
** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
** fts3ExprLoadDoclists().
*/
static int fts3ExprLoadDoclistsCb2(Fts3Expr *pExpr, int iPhrase, void *ctx){
UNUSED_PARAMETER(iPhrase);
UNUSED_PARAMETER(ctx);
if( pExpr->aDoclist ){
pExpr->pCurrent = pExpr->aDoclist;
pExpr->iCurrent = 0;
pExpr->pCurrent += sqlite3Fts3GetVarint(pExpr->pCurrent, &pExpr->iCurrent);
}
return SQLITE_OK;
}
/*
** Load the doclists for each phrase in the query associated with FTS3 cursor
** pCsr.
@ -261,16 +263,25 @@ static int fts3ExprLoadDoclists(
){
int rc; /* Return Code */
LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */
sCtx.pTab = (Fts3Table *)pCsr->base.pVtab;
rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb1, (void *)&sCtx);
if( rc==SQLITE_OK ){
(void)fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb2, 0);
}
sCtx.pCsr = pCsr;
rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx);
if( pnPhrase ) *pnPhrase = sCtx.nPhrase;
if( pnToken ) *pnToken = sCtx.nToken;
return rc;
}
static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
(*(int *)ctx)++;
UNUSED_PARAMETER(pExpr);
UNUSED_PARAMETER(iPhrase);
return SQLITE_OK;
}
static int fts3ExprPhraseCount(Fts3Expr *pExpr){
int nPhrase = 0;
(void)fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase);
return nPhrase;
}
/*
** Advance the position list iterator specified by the first two
** arguments so that it points to the first element with a value greater
@ -783,38 +794,87 @@ static void fts3LoadColumnlistCounts(char **pp, u32 *aOut, int isGlobal){
/*
** fts3ExprIterate() callback used to collect the "global" matchinfo stats
** for a single query. The "global" stats are those elements of the matchinfo
** array that are constant for all rows returned by the current query.
** for a single query.
**
** fts3ExprIterate() callback to load the 'global' elements of a
** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements
** of the matchinfo array that are constant for all rows returned by the
** current query.
**
** Argument pCtx is actually a pointer to a struct of type MatchInfo. This
** function populates Matchinfo.aMatchinfo[] as follows:
**
** for(iCol=0; iCol<nCol; iCol++){
** aMatchinfo[3*iPhrase*nCol + 3*iCol + 1] = X;
** aMatchinfo[3*iPhrase*nCol + 3*iCol + 2] = Y;
** }
**
** where X is the number of matches for phrase iPhrase is column iCol of all
** rows of the table. Y is the number of rows for which column iCol contains
** at least one instance of phrase iPhrase.
**
** If the phrase pExpr consists entirely of deferred tokens, then all X and
** Y values are set to nDoc, where nDoc is the number of documents in the
** file system. This is done because the full-text index doclist is required
** to calculate these values properly, and the full-text index doclist is
** not available for deferred tokens.
*/
static int fts3ExprGlobalMatchinfoCb(
static int fts3ExprGlobalHitsCb(
Fts3Expr *pExpr, /* Phrase expression node */
int iPhrase, /* Phrase number (numbered from zero) */
void *pCtx /* Pointer to MatchInfo structure */
){
MatchInfo *p = (MatchInfo *)pCtx;
char *pCsr;
Fts3Cursor *pCsr = p->pCursor;
char *pIter;
char *pEnd;
const int iStart = 2 + (iPhrase * p->nCol * 3) + 1;
char *pFree = 0;
u32 *aOut = &p->aMatchinfo[3*iPhrase*p->nCol];
assert( pExpr->isLoaded );
assert( pExpr->eType==FTSQUERY_PHRASE );
/* Fill in the global hit count matrix row for this phrase. */
pCsr = pExpr->aDoclist;
pEnd = &pExpr->aDoclist[pExpr->nDoclist];
while( pCsr<pEnd ){
while( *pCsr++ & 0x80 ); /* Skip past docid. */
fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 1);
if( pCsr->pDeferred ){
Fts3Phrase *pPhrase = pExpr->pPhrase;
int ii;
for(ii=0; ii<pPhrase->nToken; ii++){
if( pPhrase->aToken[ii].bFulltext ) break;
}
if( ii<pPhrase->nToken ){
int nFree = 0;
int rc = sqlite3Fts3ExprLoadFtDoclist(pCsr, pExpr, &pFree, &nFree);
if( rc!=SQLITE_OK ) return rc;
pIter = pFree;
pEnd = &pFree[nFree];
}else{
int iCol; /* Column index */
for(iCol=0; iCol<p->nCol; iCol++){
aOut[iCol*3 + 1] = (u32)p->nDoc;
aOut[iCol*3 + 2] = (u32)p->nDoc;
}
return SQLITE_OK;
}
}else{
pIter = pExpr->aDoclist;
pEnd = &pExpr->aDoclist[pExpr->nDoclist];
}
/* Fill in the global hit count matrix row for this phrase. */
while( pIter<pEnd ){
while( *pIter++ & 0x80 ); /* Skip past docid. */
fts3LoadColumnlistCounts(&pIter, &aOut[1], 1);
}
sqlite3_free(pFree);
return SQLITE_OK;
}
/*
** fts3ExprIterate() callback used to collect the "local" matchinfo stats
** for a single query. The "local" stats are those elements of the matchinfo
** fts3ExprIterate() callback used to collect the "local" part of the
** FTS3_MATCHINFO_HITS array. The local stats are those elements of the
** array that are different for each row returned by the query.
*/
static int fts3ExprLocalMatchinfoCb(
static int fts3ExprLocalHitsCb(
Fts3Expr *pExpr, /* Phrase expression node */
int iPhrase, /* Phrase number */
void *pCtx /* Pointer to MatchInfo structure */
@ -823,7 +883,7 @@ static int fts3ExprLocalMatchinfoCb(
if( pExpr->aDoclist ){
char *pCsr;
int iStart = 2 + (iPhrase * p->nCol * 3);
int iStart = iPhrase * p->nCol * 3;
int i;
for(i=0; i<p->nCol; i++) p->aMatchinfo[iStart+i*3] = 0;
@ -837,67 +897,400 @@ static int fts3ExprLocalMatchinfoCb(
return SQLITE_OK;
}
static int fts3MatchinfoCheck(
Fts3Table *pTab,
char cArg,
char **pzErr
){
if( (cArg==FTS3_MATCHINFO_NPHRASE)
|| (cArg==FTS3_MATCHINFO_NCOL)
|| (cArg==FTS3_MATCHINFO_NDOC && pTab->bHasStat)
|| (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bHasStat)
|| (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize)
|| (cArg==FTS3_MATCHINFO_LCS)
|| (cArg==FTS3_MATCHINFO_HITS)
){
return SQLITE_OK;
}
*pzErr = sqlite3_mprintf("unrecognized matchinfo request: %c", cArg);
return SQLITE_ERROR;
}
static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
int nVal; /* Number of integers output by cArg */
switch( cArg ){
case FTS3_MATCHINFO_NDOC:
case FTS3_MATCHINFO_NPHRASE:
case FTS3_MATCHINFO_NCOL:
nVal = 1;
break;
case FTS3_MATCHINFO_AVGLENGTH:
case FTS3_MATCHINFO_LENGTH:
case FTS3_MATCHINFO_LCS:
nVal = pInfo->nCol;
break;
default:
assert( cArg==FTS3_MATCHINFO_HITS );
nVal = pInfo->nCol * pInfo->nPhrase * 3;
break;
}
return nVal;
}
static int fts3MatchinfoSelectDoctotal(
Fts3Table *pTab,
sqlite3_stmt **ppStmt,
sqlite3_int64 *pnDoc,
const char **paLen
){
sqlite3_stmt *pStmt;
const char *a;
sqlite3_int64 nDoc;
if( !*ppStmt ){
int rc = sqlite3Fts3SelectDoctotal(pTab, ppStmt);
if( rc!=SQLITE_OK ) return rc;
}
pStmt = *ppStmt;
assert( sqlite3_data_count(pStmt)==1 );
a = sqlite3_column_blob(pStmt, 0);
a += sqlite3Fts3GetVarint(a, &nDoc);
*pnDoc = (u32)nDoc;
if( paLen ) *paLen = a;
return SQLITE_OK;
}
/*
** An instance of the following structure is used to store state while
** iterating through a multi-column position-list corresponding to the
** hits for a single phrase on a single row in order to calculate the
** values for a matchinfo() FTS3_MATCHINFO_LCS request.
*/
typedef struct LcsIterator LcsIterator;
struct LcsIterator {
Fts3Expr *pExpr; /* Pointer to phrase expression */
char *pRead; /* Cursor used to iterate through aDoclist */
int iPosOffset; /* Tokens count up to end of this phrase */
int iCol; /* Current column number */
int iPos; /* Current position */
};
/*
** If LcsIterator.iCol is set to the following value, the iterator has
** finished iterating through all offsets for all columns.
*/
#define LCS_ITERATOR_FINISHED 0x7FFFFFFF;
static int fts3MatchinfoLcsCb(
Fts3Expr *pExpr, /* Phrase expression node */
int iPhrase, /* Phrase number (numbered from zero) */
void *pCtx /* Pointer to MatchInfo structure */
){
LcsIterator *aIter = (LcsIterator *)pCtx;
aIter[iPhrase].pExpr = pExpr;
return SQLITE_OK;
}
/*
** Advance the iterator passed as an argument to the next position. Return
** 1 if the iterator is at EOF or if it now points to the start of the
** position list for the next column.
*/
static int fts3LcsIteratorAdvance(LcsIterator *pIter){
char *pRead = pIter->pRead;
sqlite3_int64 iRead;
int rc = 0;
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
if( iRead==0 ){
pIter->iCol = LCS_ITERATOR_FINISHED;
rc = 1;
}else{
if( iRead==1 ){
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
pIter->iCol = (int)iRead;
pIter->iPos = pIter->iPosOffset;
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
rc = 1;
}
pIter->iPos += (int)(iRead-2);
}
pIter->pRead = pRead;
return rc;
}
/*
** This function implements the FTS3_MATCHINFO_LCS matchinfo() flag.
**
** If the call is successful, the longest-common-substring lengths for each
** column are written into the first nCol elements of the pInfo->aMatchinfo[]
** array before returning. SQLITE_OK is returned in this case.
**
** Otherwise, if an error occurs, an SQLite error code is returned and the
** data written to the first nCol elements of pInfo->aMatchinfo[] is
** undefined.
*/
static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
LcsIterator *aIter;
int i;
int iCol;
int nToken = 0;
/* Allocate and populate the array of LcsIterator objects. The array
** contains one element for each matchable phrase in the query.
**/
aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase);
if( !aIter ) return SQLITE_NOMEM;
memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase);
(void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
nToken -= pIter->pExpr->pPhrase->nToken;
pIter->iPosOffset = nToken;
pIter->pRead = sqlite3Fts3FindPositions(pIter->pExpr, pCsr->iPrevId, -1);
if( pIter->pRead ){
pIter->iPos = pIter->iPosOffset;
fts3LcsIteratorAdvance(&aIter[i]);
}else{
pIter->iCol = LCS_ITERATOR_FINISHED;
}
}
for(iCol=0; iCol<pInfo->nCol; iCol++){
int nLcs = 0; /* LCS value for this column */
int nLive = 0; /* Number of iterators in aIter not at EOF */
/* Loop through the iterators in aIter[]. Set nLive to the number of
** iterators that point to a position-list corresponding to column iCol.
*/
for(i=0; i<pInfo->nPhrase; i++){
assert( aIter[i].iCol>=iCol );
if( aIter[i].iCol==iCol ) nLive++;
}
/* The following loop runs until all iterators in aIter[] have finished
** iterating through positions in column iCol. Exactly one of the
** iterators is advanced each time the body of the loop is run.
*/
while( nLive>0 ){
LcsIterator *pAdv = 0; /* The iterator to advance by one position */
int nThisLcs = 0; /* LCS for the current iterator positions */
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
if( iCol!=pIter->iCol ){
/* This iterator is already at EOF for this column. */
nThisLcs = 0;
}else{
if( pAdv==0 || pIter->iPos<pAdv->iPos ){
pAdv = pIter;
}
if( nThisLcs==0 || pIter->iPos==pIter[-1].iPos ){
nThisLcs++;
}else{
nThisLcs = 1;
}
if( nThisLcs>nLcs ) nLcs = nThisLcs;
}
}
if( fts3LcsIteratorAdvance(pAdv) ) nLive--;
}
pInfo->aMatchinfo[iCol] = nLcs;
}
sqlite3_free(aIter);
return SQLITE_OK;
}
/*
** Populate the buffer pInfo->aMatchinfo[] with an array of integers to
** be returned by the matchinfo() function. Argument zArg contains the
** format string passed as the second argument to matchinfo (or the
** default value "pcx" if no second argument was specified). The format
** string has already been validated and the pInfo->aMatchinfo[] array
** is guaranteed to be large enough for the output.
**
** If bGlobal is true, then populate all fields of the matchinfo() output.
** If it is false, then assume that those fields that do not change between
** rows (i.e. FTS3_MATCHINFO_NPHRASE, NCOL, NDOC, AVGLENGTH and part of HITS)
** have already been populated.
**
** Return SQLITE_OK if successful, or an SQLite error code if an error
** occurs. If a value other than SQLITE_OK is returned, the state the
** pInfo->aMatchinfo[] buffer is left in is undefined.
*/
static int fts3MatchinfoValues(
Fts3Cursor *pCsr, /* FTS3 cursor object */
int bGlobal, /* True to grab the global stats */
MatchInfo *pInfo, /* Matchinfo context object */
const char *zArg /* Matchinfo format string */
){
int rc = SQLITE_OK;
int i;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
sqlite3_stmt *pSelect = 0;
for(i=0; rc==SQLITE_OK && zArg[i]; i++){
switch( zArg[i] ){
case FTS3_MATCHINFO_NPHRASE:
if( bGlobal ) pInfo->aMatchinfo[0] = pInfo->nPhrase;
break;
case FTS3_MATCHINFO_NCOL:
if( bGlobal ) pInfo->aMatchinfo[0] = pInfo->nCol;
break;
case FTS3_MATCHINFO_NDOC:
if( bGlobal ){
sqlite3_int64 nDoc;
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0);
pInfo->aMatchinfo[0] = (u32)nDoc;
}
break;
case FTS3_MATCHINFO_AVGLENGTH:
if( bGlobal ){
sqlite3_int64 nDoc; /* Number of rows in table */
const char *a; /* Aggregate column length array */
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a);
if( rc==SQLITE_OK ){
int iCol;
for(iCol=0; iCol<pInfo->nCol; iCol++){
sqlite3_int64 nToken;
a += sqlite3Fts3GetVarint(a, &nToken);
pInfo->aMatchinfo[iCol] = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc);
}
}
}
break;
case FTS3_MATCHINFO_LENGTH: {
sqlite3_stmt *pSelectDocsize = 0;
rc = sqlite3Fts3SelectDocsize(pTab, pCsr->iPrevId, &pSelectDocsize);
if( rc==SQLITE_OK ){
int iCol;
const char *a = sqlite3_column_blob(pSelectDocsize, 0);
for(iCol=0; iCol<pInfo->nCol; iCol++){
sqlite3_int64 nToken;
a += sqlite3Fts3GetVarint(a, &nToken);
pInfo->aMatchinfo[iCol] = (u32)nToken;
}
}
sqlite3_reset(pSelectDocsize);
break;
}
case FTS3_MATCHINFO_LCS:
rc = fts3ExprLoadDoclists(pCsr, 0, 0);
if( rc==SQLITE_OK ){
rc = fts3MatchinfoLcs(pCsr, pInfo);
}
break;
default: {
Fts3Expr *pExpr;
assert( zArg[i]==FTS3_MATCHINFO_HITS );
pExpr = pCsr->pExpr;
rc = fts3ExprLoadDoclists(pCsr, 0, 0);
if( rc!=SQLITE_OK ) break;
if( bGlobal ){
if( pCsr->pDeferred ){
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc, 0);
if( rc!=SQLITE_OK ) break;
}
rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
if( rc!=SQLITE_OK ) break;
}
(void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
break;
}
}
pInfo->aMatchinfo += fts3MatchinfoSize(pInfo, zArg[i]);
}
sqlite3_reset(pSelect);
return rc;
}
/*
** Populate pCsr->aMatchinfo[] with data for the current row. The
** 'matchinfo' data is an array of 32-bit unsigned integers (C type u32).
*/
static int fts3GetMatchinfo(Fts3Cursor *pCsr){
static int fts3GetMatchinfo(
Fts3Cursor *pCsr, /* FTS3 Cursor object */
const char *zArg /* Second argument to matchinfo() function */
){
MatchInfo sInfo;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
int rc = SQLITE_OK;
int bGlobal = 0; /* Collect 'global' stats as well as local */
memset(&sInfo, 0, sizeof(MatchInfo));
sInfo.pCursor = pCsr;
sInfo.nCol = pTab->nColumn;
/* If there is cached matchinfo() data, but the format string for the
** cache does not match the format string for this request, discard
** the cached data. */
if( pCsr->zMatchinfo && strcmp(pCsr->zMatchinfo, zArg) ){
assert( pCsr->aMatchinfo );
sqlite3_free(pCsr->aMatchinfo);
pCsr->zMatchinfo = 0;
pCsr->aMatchinfo = 0;
}
/* If Fts3Cursor.aMatchinfo[] is NULL, then this is the first time the
** matchinfo function has been called for this query. In this case
** allocate the array used to accumulate the matchinfo data and
** initialize those elements that are constant for every row.
*/
if( pCsr->aMatchinfo==0 ){
/* If Fts3Cursor.aMatchinfo[] is NULL, then this is the first time the
** matchinfo function has been called for this query. In this case
** allocate the array used to accumulate the matchinfo data and
** initialize those elements that are constant for every row.
*/
int nPhrase; /* Number of phrases */
int nMatchinfo; /* Number of u32 elements in match-info */
int nMatchinfo = 0; /* Number of u32 elements in match-info */
int nArg; /* Bytes in zArg */
int i; /* Used to iterate through zArg */
/* Load doclists for each phrase in the query. */
rc = fts3ExprLoadDoclists(pCsr, &nPhrase, 0);
if( rc!=SQLITE_OK ){
return rc;
}
nMatchinfo = 2 + 3*sInfo.nCol*nPhrase;
if( pTab->bHasDocsize ){
nMatchinfo += 1 + 2*pTab->nColumn;
/* Determine the number of phrases in the query */
pCsr->nPhrase = fts3ExprPhraseCount(pCsr->pExpr);
sInfo.nPhrase = pCsr->nPhrase;
/* Determine the number of integers in the buffer returned by this call. */
for(i=0; zArg[i]; i++){
nMatchinfo += fts3MatchinfoSize(&sInfo, zArg[i]);
}
sInfo.aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo);
if( !sInfo.aMatchinfo ){
return SQLITE_NOMEM;
}
memset(sInfo.aMatchinfo, 0, sizeof(u32)*nMatchinfo);
/* Allocate space for Fts3Cursor.aMatchinfo[] and Fts3Cursor.zMatchinfo. */
nArg = (int)strlen(zArg);
pCsr->aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo + nArg + 1);
if( !pCsr->aMatchinfo ) return SQLITE_NOMEM;
/* First element of match-info is the number of phrases in the query */
sInfo.aMatchinfo[0] = nPhrase;
sInfo.aMatchinfo[1] = sInfo.nCol;
(void)fts3ExprIterate(pCsr->pExpr, fts3ExprGlobalMatchinfoCb,(void*)&sInfo);
if( pTab->bHasDocsize ){
int ofst = 2 + 3*sInfo.aMatchinfo[0]*sInfo.aMatchinfo[1];
rc = sqlite3Fts3MatchinfoDocsizeGlobal(pCsr, &sInfo.aMatchinfo[ofst]);
}
pCsr->aMatchinfo = sInfo.aMatchinfo;
pCsr->zMatchinfo = (char *)&pCsr->aMatchinfo[nMatchinfo];
pCsr->nMatchinfo = nMatchinfo;
memcpy(pCsr->zMatchinfo, zArg, nArg+1);
memset(pCsr->aMatchinfo, 0, sizeof(u32)*nMatchinfo);
pCsr->isMatchinfoNeeded = 1;
bGlobal = 1;
}
sInfo.aMatchinfo = pCsr->aMatchinfo;
if( rc==SQLITE_OK && pCsr->isMatchinfoNeeded ){
(void)fts3ExprIterate(pCsr->pExpr, fts3ExprLocalMatchinfoCb, (void*)&sInfo);
if( pTab->bHasDocsize ){
int ofst = 2 + 3*sInfo.aMatchinfo[0]*sInfo.aMatchinfo[1];
rc = sqlite3Fts3MatchinfoDocsizeLocal(pCsr, &sInfo.aMatchinfo[ofst]);
}
sInfo.nPhrase = pCsr->nPhrase;
if( pCsr->isMatchinfoNeeded ){
rc = fts3MatchinfoValues(pCsr, bGlobal, &sInfo, zArg);
pCsr->isMatchinfoNeeded = 0;
}
return SQLITE_OK;
return rc;
}
/*
@ -958,7 +1351,7 @@ void sqlite3Fts3Snippet(
** columns of the FTS3 table. Otherwise, only column iCol is considered.
*/
for(iRead=0; iRead<pTab->nColumn; iRead++){
SnippetFragment sF;
SnippetFragment sF = {0, 0, 0, 0};
int iS;
if( iCol>=0 && iRead!=iCol ) continue;
@ -992,6 +1385,7 @@ void sqlite3Fts3Snippet(
}
snippet_out:
sqlite3Fts3SegmentsClose(pTab);
if( rc!=SQLITE_OK ){
sqlite3_result_error_code(pCtx, rc);
sqlite3_free(res.z);
@ -1171,6 +1565,7 @@ void sqlite3Fts3Offsets(
offsets_out:
sqlite3_free(sCtx.aTerm);
assert( rc!=SQLITE_DONE );
sqlite3Fts3SegmentsClose(pTab);
if( rc!=SQLITE_OK ){
sqlite3_result_error_code(pCtx, rc);
sqlite3_free(res.z);
@ -1183,21 +1578,43 @@ void sqlite3Fts3Offsets(
/*
** Implementation of matchinfo() function.
*/
void sqlite3Fts3Matchinfo(sqlite3_context *pContext, Fts3Cursor *pCsr){
void sqlite3Fts3Matchinfo(
sqlite3_context *pContext, /* Function call context */
Fts3Cursor *pCsr, /* FTS3 table cursor */
const char *zArg /* Second arg to matchinfo() function */
){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
int rc;
int i;
const char *zFormat;
if( zArg ){
for(i=0; zArg[i]; i++){
char *zErr = 0;
if( fts3MatchinfoCheck(pTab, zArg[i], &zErr) ){
sqlite3_result_error(pContext, zErr, -1);
sqlite3_free(zErr);
return;
}
}
zFormat = zArg;
}else{
zFormat = FTS3_MATCHINFO_DEFAULT;
}
if( !pCsr->pExpr ){
sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC);
return;
}
rc = fts3GetMatchinfo(pCsr);
/* Retrieve matchinfo() data. */
rc = fts3GetMatchinfo(pCsr, zFormat);
sqlite3Fts3SegmentsClose(pTab);
if( rc!=SQLITE_OK ){
sqlite3_result_error_code(pContext, rc);
}else{
Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab;
int n = sizeof(u32)*(2+pCsr->aMatchinfo[0]*pCsr->aMatchinfo[1]*3);
if( pTab->bHasDocsize ){
n += sizeof(u32)*(1 + 2*pTab->nColumn);
}
int n = pCsr->nMatchinfo * sizeof(u32);
sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT);
}
}

View File

@ -97,7 +97,7 @@ static void scalarFunc(
sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
}
static int fts3IsIdChar(char c){
int sqlite3Fts3IsIdChar(char c){
static const char isFtsIdChar[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
@ -135,9 +135,9 @@ const char *sqlite3Fts3NextToken(const char *zStr, int *pn){
break;
default:
if( fts3IsIdChar(*z1) ){
if( sqlite3Fts3IsIdChar(*z1) ){
z2 = &z1[1];
while( fts3IsIdChar(*z2) ) z2++;
while( sqlite3Fts3IsIdChar(*z2) ) z2++;
}else{
z1++;
}
@ -150,9 +150,8 @@ const char *sqlite3Fts3NextToken(const char *zStr, int *pn){
int sqlite3Fts3InitTokenizer(
Fts3Hash *pHash, /* Tokenizer hash table */
const char *zArg, /* Possible tokenizer specification */
const char *zArg, /* Tokenizer name */
sqlite3_tokenizer **ppTok, /* OUT: Tokenizer (if applicable) */
const char **pzTokenizer, /* OUT: Set to zArg if is tokenizer */
char **pzErr /* OUT: Set to malloced error message */
){
int rc;
@ -162,26 +161,15 @@ int sqlite3Fts3InitTokenizer(
char *zEnd; /* Pointer to nul-term of zCopy */
sqlite3_tokenizer_module *m;
if( !z ){
zCopy = sqlite3_mprintf("simple");
}else{
if( sqlite3_strnicmp(z, "tokenize", 8) || fts3IsIdChar(z[8])){
return SQLITE_OK;
}
zCopy = sqlite3_mprintf("%s", &z[8]);
*pzTokenizer = zArg;
}
if( !zCopy ){
return SQLITE_NOMEM;
}
zCopy = sqlite3_mprintf("%s", zArg);
if( !zCopy ) return SQLITE_NOMEM;
zEnd = &zCopy[strlen(zCopy)];
z = (char *)sqlite3Fts3NextToken(zCopy, &n);
z[n] = '\0';
sqlite3Fts3Dequote(z);
m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, z, (int)strlen(z)+1);
m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash,z,(int)strlen(z)+1);
if( !m ){
*pzErr = sqlite3_mprintf("unknown tokenizer: %s", z);
rc = SQLITE_ERROR;
@ -477,15 +465,23 @@ int sqlite3Fts3InitHashTable(
}
#endif
if( SQLITE_OK!=rc
|| SQLITE_OK!=(rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0))
|| SQLITE_OK!=(rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0))
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0);
}
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0);
}
#ifdef SQLITE_TEST
|| SQLITE_OK!=(rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0))
|| SQLITE_OK!=(rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0))
|| SQLITE_OK!=(rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0))
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0);
}
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0);
}
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0);
}
#endif
);
#ifdef SQLITE_TEST
sqlite3_free(zTest);

File diff suppressed because it is too large Load Diff

122
ext/fts3/fts3speed.tcl Normal file
View File

@ -0,0 +1,122 @@
#--------------------------------------------------------------------------
# This script contains several sub-programs used to test FTS3/FTS4
# performance. It does not run the queries directly, but generates SQL
# scripts that can be run using the shell tool.
#
# The following cases are tested:
#
# 1. Inserting documents into an FTS3 table.
# 2. Optimizing an FTS3 table (i.e. "INSERT INTO t1 VALUES('optimize')").
# 3. Deleting documents from an FTS3 table.
# 4. Querying FTS3 tables.
#
# Number of tokens in vocabulary. And number of tokens in each document.
#
set VOCAB_SIZE 2000
set DOC_SIZE 100
set NUM_INSERTS 100000
set NUM_SELECTS 1000
# Force everything in this script to be deterministic.
#
expr {srand(0)}
proc usage {} {
puts stderr "Usage: $::argv0 <rows> <selects>"
exit -1
}
proc sql {sql} {
puts $::fd $sql
}
# Return a list of $nWord randomly generated tokens each between 2 and 10
# characters in length.
#
proc build_vocab {nWord} {
set ret [list]
set chars [list 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]
for {set i 0} {$i<$nWord} {incr i} {
set len [expr {int((rand()*9.0)+2)}]
set term ""
for {set j 0} {$j<$len} {incr j} {
append term [lindex $chars [expr {int(rand()*[llength $chars])}]]
}
lappend ret $term
}
set ret
}
proc select_term {} {
set n [llength $::vocab]
set t [expr int(rand()*$n*3)]
if {$t>=2*$n} { set t [expr {($t-2*$n)/100}] }
if {$t>=$n} { set t [expr {($t-$n)/10}] }
lindex $::vocab $t
}
proc select_doc {nTerm} {
set ret [list]
for {set i 0} {$i<$nTerm} {incr i} {
lappend ret [select_term]
}
set ret
}
proc test_1 {nInsert} {
sql "PRAGMA synchronous = OFF;"
sql "DROP TABLE IF EXISTS t1;"
sql "CREATE VIRTUAL TABLE t1 USING fts4;"
for {set i 0} {$i < $nInsert} {incr i} {
set doc [select_doc $::DOC_SIZE]
sql "INSERT INTO t1 VALUES('$doc');"
}
}
proc test_2 {} {
sql "INSERT INTO t1(t1) VALUES('optimize');"
}
proc test_3 {nSelect} {
for {set i 0} {$i < $nSelect} {incr i} {
sql "SELECT count(*) FROM t1 WHERE t1 MATCH '[select_term]';"
}
}
proc test_4 {nSelect} {
for {set i 0} {$i < $nSelect} {incr i} {
sql "SELECT count(*) FROM t1 WHERE t1 MATCH '[select_term] [select_term]';"
}
}
if {[llength $argv]!=0} usage
set ::vocab [build_vocab $::VOCAB_SIZE]
set ::fd [open fts3speed_insert.sql w]
test_1 $NUM_INSERTS
close $::fd
set ::fd [open fts3speed_select.sql w]
test_3 $NUM_SELECTS
close $::fd
set ::fd [open fts3speed_select2.sql w]
test_4 $NUM_SELECTS
close $::fd
set ::fd [open fts3speed_optimize.sql w]
test_2
close $::fd
puts "Success. Created files:"
puts " fts3speed_insert.sql"
puts " fts3speed_select.sql"
puts " fts3speed_select2.sql"
puts " fts3speed_optimize.sql"

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] rtree_util.tcl]
source $testdir/tester.tcl

View File

@ -13,7 +13,7 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] rtree_util.tcl]
source $testdir/tester.tcl
@ -27,7 +27,7 @@ set ::NROW 1000
set ::NDEL 10
set ::NSELECT 100
if {[info exists ISQUICK] && $ISQUICK} {
if {[info exists G(isquick)] && $G(isquick)} {
set ::NROW 100
set ::NSELECT 10
}

View File

@ -14,52 +14,93 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
ifcapable !rtree {
finish_test
return
}
# Only run these tests if memory debugging is turned on.
# Test summary:
#
source $testdir/malloc_common.tcl
if {!$MEMDEBUG} {
puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
finish_test
return
# rtree3-1: Test OOM in simple CREATE TABLE, INSERT, DELETE and SELECT
# commands on an almost empty table.
#
# rtree3-2: Test OOM in a DROP TABLE command.
#
# rtree3-3a: Test OOM during a transaction to insert 100 pseudo-random rows.
#
# rtree3-3b: Test OOM during a transaction deleting all entries in the
# database constructed in [rtree3-3a] in pseudo-random order.
#
# rtree3-4a: OOM during "SELECT count(*) FROM ..." on a big table.
#
# rtree3-4b: OOM while deleting rows from a big table.
#
# rtree3-5: Test OOM while inserting rows into a big table.
#
# rtree3-6: Test OOM while deleting all rows of a table, one at a time.
#
# rtree3-7: OOM during an ALTER TABLE RENAME TABLE command.
#
# rtree3-8: Test OOM while registering the r-tree module with sqlite.
#
do_faultsim_test rtree3-1 -faults oom* -prep {
faultsim_delete_and_reopen
} -body {
execsql {
BEGIN TRANSACTION;
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
INSERT INTO rt VALUES(NULL, 13, 15, 17, 19);
DELETE FROM rt WHERE ii = 1;
SELECT * FROM rt;
SELECT ii FROM rt WHERE ii = 2;
COMMIT;
}
}
do_malloc_test rtree3-1 -sqlbody {
BEGIN TRANSACTION;
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
INSERT INTO rt VALUES(NULL, 13, 15, 17, 19);
DELETE FROM rt WHERE ii = 1;
SELECT * FROM rt;
SELECT ii FROM rt WHERE ii = 2;
COMMIT;
}
do_malloc_test rtree3-2 -sqlprep {
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
} -sqlbody {
DROP TABLE rt;
}
do_test rtree3-2.prep {
faultsim_delete_and_reopen
execsql {
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
}
faultsim_save_and_close
} {}
do_faultsim_test rtree3-2 -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
execsql { DROP TABLE rt }
}
do_malloc_test rtree3-3.prep {
faultsim_delete_and_reopen
execsql {
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
}
faultsim_save_and_close
} {}
do_malloc_test rtree3-3 -sqlprep {
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
} -tclbody {
do_faultsim_test rtree3-3a -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
db eval BEGIN
for {set ii 0} {$ii < 100} {incr ii} {
set f [expr rand()]
db eval {INSERT INTO rt VALUES(NULL, $f*10.0, $f*10.0, $f*15.0, $f*15.0)}
}
db eval COMMIT
}
faultsim_save_and_close
do_faultsim_test rtree3-3b -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
db eval BEGIN
for {set ii 0} {$ii < 100} {incr ii} {
set f [expr rand()]
@ -68,4 +109,129 @@ do_malloc_test rtree3-3 -sqlprep {
db eval COMMIT
}
do_test rtree3-4.prep {
faultsim_delete_and_reopen
execsql {
BEGIN;
PRAGMA page_size = 512;
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
}
for {set i 0} {$i < 1500} {incr i} {
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
}
execsql { COMMIT }
faultsim_save_and_close
} {}
do_faultsim_test rtree3-4a -faults oom-* -prep {
faultsim_restore_and_reopen
} -body {
db eval { SELECT count(*) FROM rt }
} -test {
faultsim_test_result {0 1500}
}
do_faultsim_test rtree3-4b -faults oom-transient -prep {
faultsim_restore_and_reopen
} -body {
db eval { DELETE FROM rt WHERE ii BETWEEN 1 AND 100 }
} -test {
faultsim_test_result {0 {}}
}
do_test rtree3-5.prep {
faultsim_delete_and_reopen
execsql {
BEGIN;
PRAGMA page_size = 512;
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
}
for {set i 0} {$i < 100} {incr i} {
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
}
execsql { COMMIT }
faultsim_save_and_close
} {}
do_faultsim_test rtree3-5 -faults oom-* -prep {
faultsim_restore_and_reopen
} -body {
for {set i 100} {$i < 110} {incr i} {
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
}
} -test {
faultsim_test_result {0 {}}
}
do_test rtree3-6.prep {
faultsim_delete_and_reopen
execsql {
BEGIN;
PRAGMA page_size = 512;
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
}
for {set i 0} {$i < 50} {incr i} {
execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) }
}
execsql { COMMIT }
faultsim_save_and_close
} {}
do_faultsim_test rtree3-6 -faults oom-* -prep {
faultsim_restore_and_reopen
} -body {
execsql BEGIN
for {set i 0} {$i < 50} {incr i} {
execsql { DELETE FROM rt WHERE ii=$i }
}
execsql COMMIT
} -test {
faultsim_test_result {0 {}}
}
do_test rtree3-7.prep {
faultsim_delete_and_reopen
execsql { CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2) }
faultsim_save_and_close
} {}
do_faultsim_test rtree3-7 -faults oom-* -prep {
faultsim_restore_and_reopen
} -body {
execsql { ALTER TABLE rt RENAME TO rt2 }
} -test {
faultsim_test_result {0 {}}
}
do_faultsim_test rtree3-8 -faults oom-* -prep {
catch { db close }
} -body {
sqlite3 db test.db
}
do_faultsim_test rtree3-9 -faults oom-* -prep {
sqlite3 db :memory:
} -body {
set rc [register_cube_geom db]
if {$rc != "SQLITE_OK"} { error $rc }
} -test {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
do_test rtree3-10.prep {
faultsim_delete_and_reopen
execsql {
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2, z1, z2);
INSERT INTO rt VALUES(1, 10, 10, 10, 11, 11, 11);
INSERT INTO rt VALUES(2, 5, 6, 6, 7, 7, 8);
}
faultsim_save_and_close
} {}
do_faultsim_test rtree3-10 -faults oom-* -prep {
faultsim_restore_and_reopen
register_cube_geom db
execsql { SELECT * FROM rt }
} -body {
execsql { SELECT ii FROM rt WHERE ii MATCH cube(4.5, 5.5, 6.5, 1, 1, 1) }
} -test {
faultsim_test_result {0 2}
}
finish_test

View File

@ -13,7 +13,7 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
@ -23,7 +23,7 @@ ifcapable !rtree {
}
set ::NROW 2500
if {[info exists ISQUICK] && $ISQUICK} {
if {[info exists G(isquick)] && $G(isquick)} {
set ::NROW 250
}
@ -93,7 +93,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
for {set i 1} {$i<$::NROW} {incr i} {
# Do a random insert
#
do_test rtree-$nDim.2.$i.1 {
do_test rtree4-$nDim.2.$i.1 {
set vlist {}
for {set j 0} {$j<$nDim} {incr j} {
set mn [rand 10000]
@ -113,7 +113,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mn$j>=$mn mx$j<=$mx
}
set where "WHERE [join $where { AND }]"
do_test rtree-$nDim.2.$i.2 {
do_test rtree4-$nDim.2.$i.2 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
@ -126,7 +126,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mx$j>=$mn mn$j<=$mx
}
set where "WHERE [join $where { AND }]"
do_test rtree-$nDim.2.$i.3 {
do_test rtree4-$nDim.2.$i.3 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
@ -143,7 +143,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mn$j>=$mn mx$j<=$mx
}
set where "WHERE [join $where { AND }]"
do_test rtree-$nDim.2.$i.3 {
do_test rtree4-$nDim.2.$i.3 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
@ -160,7 +160,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mx$j>$mn mn$j<$mx
}
set where "WHERE [join $where { AND }]"
do_test rtree-$nDim.2.$i.4 {
do_test rtree4-$nDim.2.$i.4 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
@ -176,7 +176,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mn$j>=-10000 mx$j<10000
}
set where "WHERE [join $where { AND }]"
do_test rtree-$nDim.2.$i.5 {
do_test rtree4-$nDim.2.$i.5 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
@ -192,7 +192,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mx$j>-10000 mn$j<=10000
}
set where "WHERE [join $where { AND }]"
do_test rtree-$nDim.2.$i.6 {
do_test rtree4-$nDim.2.$i.6 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
@ -208,7 +208,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mn$j>=$mn1 mn$j>$mn2 mx$j<$mx1 mx$j<=$mx2
}
set where "WHERE [join [scramble $where] { AND }]"
do_test rtree-$nDim.2.$i.7 {
do_test rtree4-$nDim.2.$i.7 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
@ -224,7 +224,7 @@ for {set nDim 1} {$nDim<=5} {incr nDim} {
lappend where mx$j>=$mn1 mx$j>$mn2 mn$j<$mx1 mn$j<=$mx2
}
set where "WHERE [join [scramble $where] { AND }]"
do_test rtree-$nDim.2.$i.8 {
do_test rtree4-$nDim.2.$i.8 {
list $where [db eval "SELECT id FROM rx $where ORDER BY id"]
} [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]]
}

View File

@ -14,7 +14,7 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl

View File

@ -12,7 +12,7 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
@ -71,39 +71,39 @@ do_test rtree6-1.5 {
rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
} {Ca}
do_test rtree6.2.1 {
query_plan {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
} [list \
{TABLE t1 VIRTUAL TABLE INDEX 2:Ca} \
{TABLE t2 USING PRIMARY KEY} \
]
do_eqp_test rtree6.2.1 {
SELECT * FROM t1,t2 WHERE k=+ii AND x1<10
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)}
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
}
do_test rtree6.2.2 {
query_plan {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
} [list \
{TABLE t1 VIRTUAL TABLE INDEX 2:Ca} \
{TABLE t2 USING PRIMARY KEY} \
]
do_eqp_test rtree6.2.2 {
SELECT * FROM t1,t2 WHERE k=ii AND x1<10
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)}
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
}
do_test rtree6.2.3 {
query_plan {SELECT * FROM t1,t2 WHERE k=ii}
} [list \
{TABLE t1 VIRTUAL TABLE INDEX 2:} \
{TABLE t2 USING PRIMARY KEY} \
]
do_eqp_test rtree6.2.3 {
SELECT * FROM t1,t2 WHERE k=ii
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)}
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
}
do_test rtree6.2.4 {
query_plan {SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10}
} [list \
{TABLE t1 VIRTUAL TABLE INDEX 2:CaEb} \
{TABLE t2} \
]
do_eqp_test rtree6.2.4 {
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb (~0 rows)}
0 1 1 {SCAN TABLE t2 (~100000 rows)}
}
do_test rtree6.2.5 {
query_plan {SELECT * FROM t1,t2 WHERE k=ii AND x1<v}
} [list \
{TABLE t1 VIRTUAL TABLE INDEX 2:} \
{TABLE t2 USING PRIMARY KEY} \
]
do_eqp_test rtree6.2.5 {
SELECT * FROM t1,t2 WHERE k=ii AND x1<v
} {
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)}
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
}
finish_test

View File

@ -15,7 +15,7 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl

171
ext/rtree/rtree8.test Normal file
View File

@ -0,0 +1,171 @@
# 2010 February 16
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !rtree { finish_test ; return }
#-------------------------------------------------------------------------
# The following block of tests - rtree8-1.* - feature reading and writing
# an r-tree table while there exist open cursors on it.
#
proc populate_t1 {n} {
execsql { DELETE FROM t1 }
for {set i 1} {$i <= $n} {incr i} {
execsql { INSERT INTO t1 VALUES($i, $i, $i+2) }
}
}
# A DELETE while a cursor is reading the table.
#
do_test rtree8-1.1.1 {
execsql { PRAGMA page_size = 512 }
execsql { CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2) }
populate_t1 5
} {}
do_test rtree8-1.1.2 {
set res [list]
db eval { SELECT * FROM t1 } {
lappend res $x1 $x2
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
}
set res
} {1 3 2 4 3 5}
do_test rtree8-1.1.3 {
execsql { SELECT * FROM t1 }
} {1 1 3 2 2 4 3 3 5}
# Many SELECTs on the same small table.
#
proc nested_select {n} {
set ::max $n
db eval { SELECT * FROM t1 } {
if {$id == $n} { nested_select [expr $n+1] }
}
return $::max
}
do_test rtree8-1.2.1 { populate_t1 50 } {}
do_test rtree8-1.2.2 { nested_select 1 } {51}
# This test runs many SELECT queries simultaneously against a large
# table, causing a collision in the hash-table used to store r-tree
# nodes internally.
#
populate_t1 1500
do_execsql_test rtree8-1.3.1 { SELECT max(nodeno) FROM t1_node } {164}
do_test rtree8-1.3.2 {
set rowids [execsql {SELECT min(rowid) FROM t1_rowid GROUP BY nodeno}]
set stmt_list [list]
foreach row $rowids {
set stmt [sqlite3_prepare db "SELECT * FROM t1 WHERE id = $row" -1 tail]
sqlite3_step $stmt
lappend res_list [sqlite3_column_int $stmt 0]
lappend stmt_list $stmt
}
} {}
do_test rtree8-1.3.3 { set res_list } $rowids
do_execsql_test rtree8-1.3.4 { SELECT count(*) FROM t1 } {1500}
do_test rtree8-1.3.5 {
foreach stmt $stmt_list { sqlite3_finalize $stmt }
} {}
#-------------------------------------------------------------------------
# The following block of tests - rtree8-2.* - test a couple of database
# corruption cases. In this case things are not corrupted at the b-tree
# level, but the contents of the various tables used internally by an
# r-tree table are inconsistent.
#
populate_t1 50
do_execsql_test rtree8-2.1.1 { SELECT max(nodeno) FROM t1_node } {5}
do_execsql_test rtree8-2.1.2 { DELETE FROM t1_node } {}
for {set i 1} {$i <= 50} {incr i} {
do_catchsql_test rtree8-2.1.3.$i {
SELECT * FROM t1 WHERE id = $i
} {1 {database disk image is malformed}}
}
do_catchsql_test rtree8-2.1.4 {
SELECT * FROM t1
} {1 {database disk image is malformed}}
do_catchsql_test rtree8-2.1.5 {
DELETE FROM t1
} {1 {database disk image is malformed}}
do_execsql_test rtree8-2.1.6 {
DROP TABLE t1;
CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2);
} {}
populate_t1 50
do_execsql_test rtree8-2.2.1 {
DELETE FROM t1_parent
} {}
do_catchsql_test rtree8-2.2.2 {
DELETE FROM t1 WHERE id=25
} {1 {database disk image is malformed}}
do_execsql_test rtree8-2.2.3 {
DROP TABLE t1;
CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2);
} {}
#-------------------------------------------------------------------------
# Test that trying to use the MATCH operator with the r-tree module does
# not confuse it.
#
populate_t1 10
do_catchsql_test rtree8-3.1 {
SELECT * FROM t1 WHERE x1 MATCH '1234'
} {1 {SQL logic error or missing database}}
#-------------------------------------------------------------------------
# Test a couple of invalid arguments to rtreedepth().
#
do_catchsql_test rtree8-4.1 {
SELECT rtreedepth('hello world')
} {1 {Invalid argument to rtreedepth()}}
do_catchsql_test rtree8-4.2 {
SELECT rtreedepth(X'00')
} {1 {Invalid argument to rtreedepth()}}
#-------------------------------------------------------------------------
# Delete half of a lopsided tree.
#
do_execsql_test rtree8-5.1 {
CREATE VIRTUAL TABLE t2 USING rtree_i32(id, x1, x2)
} {}
do_test rtree8-5.2 {
execsql BEGIN
for {set i 0} {$i < 100} {incr i} {
execsql { INSERT INTO t2 VALUES($i, 100, 101) }
}
for {set i 100} {$i < 200} {incr i} {
execsql { INSERT INTO t2 VALUES($i, 1000, 1001) }
}
execsql COMMIT
} {}
do_test rtree8-5.3 {
execsql BEGIN
for {set i 0} {$i < 200} {incr i} {
execsql { DELETE FROM t2 WHERE id = $i }
}
execsql COMMIT
} {}
finish_test

125
ext/rtree/rtree9.test Normal file
View File

@ -0,0 +1,125 @@
# 2010 August 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.
#
#***********************************************************************
# This file contains tests for the r-tree module. Specifically, it tests
# that custom r-tree queries (geometry callbacks) work.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !rtree { finish_test ; return }
register_cube_geom db
do_execsql_test rtree9-1.1 {
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, y1, y2, z1, z2);
INSERT INTO rt VALUES(1, 1, 2, 1, 2, 1, 2);
} {}
do_execsql_test rtree9-1.2 {
SELECT * FROM rt WHERE id MATCH cube(0, 0, 0, 2, 2, 2);
} {1 1.0 2.0 1.0 2.0 1.0 2.0}
do_execsql_test rtree9-1.3 {
SELECT * FROM rt WHERE id MATCH cube(3, 3, 3, 2, 2, 2);
} {}
do_execsql_test rtree9-1.4 {
DELETE FROM rt;
} {}
for {set i 0} {$i < 1000} {incr i} {
set x [expr $i%10]
set y [expr ($i/10)%10]
set z [expr ($i/100)%10]
execsql { INSERT INTO rt VALUES($i, $x, $x+1, $y, $y+1, $z, $z+1) }
}
do_execsql_test rtree9-2.1 {
SELECT id FROM rt WHERE id MATCH cube(2.5, 2.5, 2.5, 1, 1, 1) ORDER BY id;
} {222 223 232 233 322 323 332 333}
do_execsql_test rtree9-2.2 {
SELECT id FROM rt WHERE id MATCH cube(5.5, 5.5, 5.5, 1, 1, 1) ORDER BY id;
} {555 556 565 566 655 656 665 666}
do_execsql_test rtree9-3.1 {
CREATE VIRTUAL TABLE rt32 USING rtree_i32(id, x1, x2, y1, y2, z1, z2);
} {}
for {set i 0} {$i < 1000} {incr i} {
set x [expr $i%10]
set y [expr ($i/10)%10]
set z [expr ($i/100)%10]
execsql { INSERT INTO rt32 VALUES($i, $x, $x+1, $y, $y+1, $z, $z+1) }
}
do_execsql_test rtree9-3.2 {
SELECT id FROM rt32 WHERE id MATCH cube(3, 3, 3, 1, 1, 1) ORDER BY id;
} {222 223 224 232 233 234 242 243 244 322 323 324 332 333 334 342 343 344 422 423 424 432 433 434 442 443 444}
do_execsql_test rtree9-3.3 {
SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 5.5, 1, 1, 1) ORDER BY id;
} {555 556 565 566 655 656 665 666}
do_catchsql_test rtree9-4.1 {
SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 1, 1, 1) ORDER BY id;
} {1 {SQL logic error or missing database}}
for {set x 2} {$x<200} {incr x 2} {
do_catchsql_test rtree9-4.2.[expr $x/2] {
SELECT id FROM rt WHERE id MATCH randomblob($x)
} {1 {SQL logic error or missing database}}
}
do_catchsql_test rtree9-4.3 {
SELECT id FROM rt WHERE id MATCH CAST(
(cube(5.5, 5.5, 5.5, 1, 1, 1) || X'1234567812345678') AS blob
)
} {1 {SQL logic error or missing database}}
#-------------------------------------------------------------------------
# Test the example 2d "circle" geometry callback.
#
register_circle_geom db
breakpoint
do_execsql_test rtree9-5.1 {
CREATE VIRTUAL TABLE rt2 USING rtree(id, xmin, xmax, ymin, ymax);
INSERT INTO rt2 VALUES(1, 1, 2, 1, 2);
INSERT INTO rt2 VALUES(2, 1, 2, -2, -1);
INSERT INTO rt2 VALUES(3, -2, -1, -2, -1);
INSERT INTO rt2 VALUES(4, -2, -1, 1, 2);
INSERT INTO rt2 VALUES(5, 2, 3, 2, 3);
INSERT INTO rt2 VALUES(6, 2, 3, -3, -2);
INSERT INTO rt2 VALUES(7, -3, -2, -3, -2);
INSERT INTO rt2 VALUES(8, -3, -2, 2, 3);
INSERT INTO rt2 VALUES(9, 1.8, 3, 1.8, 3);
INSERT INTO rt2 VALUES(10, 1.8, 3, -3, -1.8);
INSERT INTO rt2 VALUES(11, -3, -1.8, -3, -1.8);
INSERT INTO rt2 VALUES(12, -3, -1.8, 1.8, 3);
INSERT INTO rt2 VALUES(13, -15, 15, 1.8, 2.2);
INSERT INTO rt2 VALUES(14, -15, 15, -2.2, -1.8);
INSERT INTO rt2 VALUES(15, 1.8, 2.2, -15, 15);
INSERT INTO rt2 VALUES(16, -2.2, -1.8, -15, 15);
INSERT INTO rt2 VALUES(17, -100, 100, -100, 100);
} {}
do_execsql_test rtree9-5.2 {
SELECT id FROM rt2 WHERE id MATCH circle(0.0, 0.0, 2.0);
} {1 2 3 4 13 14 15 16 17}
do_execsql_test rtree9-5.3 {
UPDATE rt2 SET xmin=xmin+5, ymin=ymin+5, xmax=xmax+5, ymax=ymax+5;
SELECT id FROM rt2 WHERE id MATCH circle(5.0, 5.0, 2.0);
} {1 2 3 4 13 14 15 16 17}
finish_test

220
ext/rtree/rtreeA.test Normal file
View File

@ -0,0 +1,220 @@
# 2010 September 22
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains tests for the r-tree module. Specifically, it tests
# that corrupt or inconsistent databases do not cause crashes in the r-tree
# module.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
ifcapable !rtree { finish_test ; return }
proc create_t1 {} {
db close
forcedelete test.db
sqlite3 db test.db
execsql {
PRAGMA page_size = 1024;
CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2, y1, y2);
}
}
proc populate_t1 {} {
execsql BEGIN
for {set i 0} {$i < 500} {incr i} {
set x2 [expr $i+5]
set y2 [expr $i+5]
execsql { INSERT INTO t1 VALUES($i, $i, $x2, $i, $y2) }
}
execsql COMMIT
}
proc truncate_node {nodeno nTrunc} {
set blob [db one {SELECT data FROM t1_node WHERE nodeno=$nodeno}]
if {$nTrunc<0} {set nTrunc "end-$nTrunc"}
set blob [string range $blob 0 $nTrunc]
db eval { UPDATE t1_node SET data = $blob WHERE nodeno=$nodeno }
}
proc set_tree_depth {tbl {newvalue ""}} {
set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=1"]
if {$newvalue == ""} {
binary scan $blob Su oldvalue
return $oldvalue
}
set blob [binary format Sua* $newvalue [string range $blob 2 end]]
db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=1"
return [set_tree_depth $tbl]
}
proc set_entry_count {tbl nodeno {newvalue ""}} {
set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=$nodeno"]
if {$newvalue == ""} {
binary scan [string range $blob 2 end] Su oldvalue
return $oldvalue
}
set blob [binary format a*Sua* \
[string range $blob 0 1] $newvalue [string range $blob 4 end]
]
db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=$nodeno"
return [set_entry_count $tbl $nodeno]
}
proc do_corruption_tests {prefix args} {
set testarray [lindex $args end]
set errormsg {database disk image is malformed}
foreach {z value} [lrange $args 0 end-1] {
set n [string length $z]
if {$n>=2 && [string equal -length $n $z "-error"]} {
set errormsg $value
}
}
foreach {tn sql} $testarray {
do_catchsql_test $prefix.$tn $sql [list 1 $errormsg]
}
}
#-------------------------------------------------------------------------
# Test the libraries response if the %_node table is completely empty
# (i.e. the root node is missing), or has been removed from the database
# entirely.
#
create_t1
populate_t1
do_execsql_test rtreeA-1.0 {
DELETE FROM t1_node;
} {}
do_corruption_tests rtreeA-1.1 {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
}
do_execsql_test rtreeA-1.2.0 { DROP TABLE t1_node } {}
do_corruption_tests rtreeA-1.2 -error "SQL logic error or missing database" {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
}
#-------------------------------------------------------------------------
# Test the libraries response if some of the entries in the %_node table
# are the wrong size.
#
create_t1
populate_t1
do_test rtreeA-2.1.0 {
set nodes [db eval {select nodeno FROM t1_node}]
foreach {a b c} $nodes { truncate_node $c 200 }
} {}
do_corruption_tests rtreeA-2.1 {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
}
create_t1
populate_t1
do_test rtreeA-2.2.0 { truncate_node 1 200 } {}
do_corruption_tests rtreeA-2.2 {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
}
#-------------------------------------------------------------------------
# Set the "depth" of the tree stored on the root node incorrectly. Test
# that this does not cause any problems.
#
create_t1
populate_t1
do_test rtreeA-3.1.0.1 { set_tree_depth t1 } {1}
do_test rtreeA-3.1.0.2 { set_tree_depth t1 3 } {3}
do_corruption_tests rtreeA-3.1 {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
}
do_test rtreeA-3.2.0 { set_tree_depth t1 1000 } {1000}
do_corruption_tests rtreeA-3.2 {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
}
create_t1
populate_t1
do_test rtreeA-3.3.0 {
execsql { DELETE FROM t1 WHERE rowid = 0 }
set_tree_depth t1 65535
} {65535}
do_corruption_tests rtreeA-3.3 {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
}
#-------------------------------------------------------------------------
# Set the "number of entries" field on some nodes incorrectly.
#
create_t1
populate_t1
do_test rtreeA-4.1.0 {
set_entry_count t1 1 4000
} {4000}
do_corruption_tests rtreeA-4.1 {
1 "SELECT * FROM t1"
2 "SELECT * FROM t1 WHERE rowid=5"
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
4 "SELECT * FROM t1 WHERE x1<10 AND x2>12"
}
#-------------------------------------------------------------------------
# Remove entries from the %_parent table and check that this does not
# cause a crash.
#
create_t1
populate_t1
do_execsql_test rtreeA-5.1.0 { DELETE FROM t1_parent } {}
do_corruption_tests rtreeA-5.1 {
1 "DELETE FROM t1 WHERE rowid = 5"
2 "DELETE FROM t1"
}
#-------------------------------------------------------------------------
# Add some bad entries to the %_parent table.
#
create_t1
populate_t1
do_execsql_test rtreeA-6.1.0 {
UPDATE t1_parent set parentnode = parentnode+1
} {}
do_corruption_tests rtreeA-6.1 {
1 "DELETE FROM t1 WHERE rowid = 5"
2 "UPDATE t1 SET x1=x1+1, x2=x2+1"
}
finish_test

56
ext/rtree/sqlite3rtree.h Normal file
View File

@ -0,0 +1,56 @@
/*
** 2010 August 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.
**
*************************************************************************
*/
#ifndef _SQLITE3RTREE_H_
#define _SQLITE3RTREE_H_
#include <sqlite3.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
/*
** Register a geometry callback named zGeom that can be used as part of an
** R-Tree geometry query as follows:
**
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...)
*/
int sqlite3_rtree_geometry_callback(
sqlite3 *db,
const char *zGeom,
int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes),
void *pContext
);
/*
** A pointer to a structure of the following type is passed as the first
** argument to callbacks registered using rtree_geometry_callback().
*/
struct sqlite3_rtree_geometry {
void *pContext; /* Copy of pContext passed to s_r_g_c() */
int nParam; /* Size of array aParam[] */
double *aParam; /* Parameters passed to SQL geom function */
void *pUser; /* Callback implementation user data */
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
};
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* ifndef _SQLITE3RTREE_H_ */

View File

@ -13,7 +13,7 @@
#
if {![info exists testdir]} {
set testdir [file join [file dirname $argv0] .. .. test]
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] rtree_util.tcl]
source $testdir/tester.tcl

19
main.mk
View File

@ -241,13 +241,17 @@ TESTSRC = \
$(TOP)/src/test_intarray.c \
$(TOP)/src/test_journal.c \
$(TOP)/src/test_malloc.c \
$(TOP)/src/test_multiplex.c \
$(TOP)/src/test_mutex.c \
$(TOP)/src/test_onefile.c \
$(TOP)/src/test_osinst.c \
$(TOP)/src/test_pcache.c \
$(TOP)/src/test_quota.c \
$(TOP)/src/test_rtree.c \
$(TOP)/src/test_schema.c \
$(TOP)/src/test_server.c \
$(TOP)/src/test_stat.c \
$(TOP)/src/test_superlock.c \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vfs.c \
@ -367,7 +371,9 @@ target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl
sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl
tclsh $(TOP)/tool/mksqlite3c.tcl
cp sqlite3.c tclsqlite3.c
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
cat sqlite3.c >>tclsqlite3.c
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
fts2amal.c: target_source $(TOP)/ext/fts2/mkfts2amal.tcl
@ -521,6 +527,17 @@ soaktest: testfixture$(EXE) sqlite3$(EXE)
test: testfixture$(EXE) sqlite3$(EXE)
./testfixture$(EXE) $(TOP)/test/veryquick.test
# The next two rules are used to support the "threadtest" target. Building
# threadtest runs a few thread-safety tests that are implemented in C. This
# target is invoked by the releasetest.tcl script.
#
threadtest3$(EXE): sqlite3.c $(TOP)/test/threadtest3.c
$(TCCX) -O2 sqlite3.c $(TOP)/test/threadtest3.c \
-o threadtest3$(EXE) $(THREADLIB)
threadtest: threadtest3$(EXE)
./threadtest3$(EXE)
sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \
$(TOP)/tool/spaceanal.tcl
sed \

416
manifest
View File

@ -1,14 +1,14 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
C Version\s3.7.2\srelease\scandidate\s1
D 2010-08-23T18:52:01
C SQLite\sversion\s3.7.5\srelease\scandidate\s2
D 2011-01-28T17:03:50.592
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 543f91f24cd7fee774ecc0a61c19704c0c3e78fd
F Makefile.in de6498556d536ae60bb8bb10e8c1ba011448658c
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION 6062e0026a5ab33dabb4efae38604d40115819ec
F VERSION de8d3477dbf0d6cc226ccc6e046273627eb55fc5
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
@ -19,13 +19,14 @@ F art/SQLite_big.gif 2b8e4603b91ba2a2c7062a82ff570d945034bb30
F art/nocopy.gif 716aa07d4bb7250d4e75756073bf8ef9f56bec8f
F art/powered_by_sqlite.gif 7fbcd7d3675391fd3d21672c14c05f5999eb60d1
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 868fdb48c028421a203470e15c69ada15b9ba673
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure a5dab32df872ed3fe248c7f0cbc324595c169781 x
F configure.ac 699040cc9abb7465dca5a2972bc89d227fd8f734
F configure 0eb10c03a6536d8e5ce52ab70fda0a152d8a3262 x
F configure.ac 87a3c71bbe9c925381c154413eea7f3cdc397244
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
F doc/pager-invariants.txt 870107036470d7c419e93768676fae2f8749cf9e
@ -50,53 +51,58 @@ F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d
F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9
F ext/fts2/README.tokenizers 21e3684ea5a095b55d70f6878b4ce6af5932dfb7
F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts2/fts2.c 6cbd0fbdfe4699c108199e3c8f7e76f71d597335
F ext/fts2/fts2.c 238e9e19158ef75fb4155613a870443394fbf7da
F ext/fts2/fts2.h da5f76c65163301d1068a971fd32f4119e3c95fa
F ext/fts2/fts2_hash.c 2689e42e1107ea67207f725cf69cf8972d00cf93
F ext/fts2/fts2_hash.h 9a5b1be94664139f93217a0770d7144425cffb3a
F ext/fts2/fts2_icu.c 1ea9993a39c9783c2e2d7446d055e9d64411dda0
F ext/fts2/fts2_porter.c 8a6369b0fae98c04db95e4fa95fac7c03d7182ec
F ext/fts2/fts2_porter.c 747056987951f743e955c8479f1df21a565720fe
F ext/fts2/fts2_tokenizer.c 26e993a00b2bd5b6e73c155597361710b12ffe25
F ext/fts2/fts2_tokenizer.h a7e46462d935a314b2682287f12f27530a3ee08e
F ext/fts2/fts2_tokenizer1.c 8545ce12b41922004da46e91a7b023b92b76f94e
F ext/fts2/fts2_tokenizer1.c 0123d21078e053bd98fd6186c5c6dc6d67969f2e
F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c e818310c473d7703f7818887a3537ec42ae0d528
F ext/fts3/fts3.c 28ada7d1c700e57b072b2c95d70565b05925fa46
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h 70528ba8c33991699f96ecc64112122833cdbdb5
F ext/fts3/fts3_expr.c 42d5697731cd30fbeabd081bb3e6d3df5531f606
F ext/fts3/fts3Int.h a6c69c1c5e2c8c19172ddff42d262c087dcd7337
F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93
F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295
F ext/fts3/fts3_porter.c 8df6f6efcc4e9e31f8bf73a4007c2e9abca1dfba
F ext/fts3/fts3_snippet.c 2c4c921155e4b6befd272041fb903d999ac07d30
F ext/fts3/fts3_tokenizer.c b4f2d01c24573852755bc92864816785dae39318
F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2
F ext/fts3/fts3_snippet.c 196c5e6cde57bfc1907c2d60e9c29590e4f93fb6
F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
F ext/fts3/fts3_write.c 4b21a0c6f2772b261f14e3a2e80e1e3e849268b0
F ext/fts3/fts3_write.c 3eea26b9ca4219e1711b0db74fd5a9d448a6afbb
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F ext/icu/icu.c 850e9a36567bbcce6bd85a4b68243cad8e3c2de2
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c f2fbb6470155316027a8b888e8623bc1819fe443
F ext/rtree/rtree.c 05b293c85403cf39bb5af0e7c010b0cafeab5e47
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test 51bb0cd0405970501e63258401ae5ad235a4f468
F ext/rtree/rtree2.test 7b665c44d25e51b3098068d983a39902b2e2d7a1
F ext/rtree/rtree3.test dece988c363368af8c11862995c762071894918f
F ext/rtree/rtree4.test 94fdd570ab5bc47244d87d4590023be43ac786bd
F ext/rtree/rtree5.test 92508f5152a50110af6551fa5b769d1bbd7c4ef3
F ext/rtree/rtree6.test 903720aaab819764c3693aaac0affe8174104ac8
F ext/rtree/rtree7.test 6fd29fb8e13795c822f4ceeea92ab5d61c96976d
F ext/rtree/rtree1.test dbd4250ac0ad367a262eb9676f7e3080b0368206
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af
F ext/rtree/rtree5.test ce3d7ccae2cfd9d2e1052b462424964c9bdcda12
F ext/rtree/rtree6.test 309806a2a27ef5897d4dd6aee2e8006bf754cc22
F ext/rtree/rtree7.test bcb647b42920b3b5d025846689147778485cc318
F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d
F ext/rtree/rtree9.test df9843d1a9195249c8d3b4ea6aedda2d5c73e9c2
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
F ext/rtree/tkt3363.test 2bf324f7908084a5f463de3109db9c6e607feb1b
F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk 26ad86cf0689940f19b3d608bbfdb3956b2fb9a7
F main.mk 05d0f3475dd331896bd607cfb45c5e21b94589ad
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@ -109,26 +115,26 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c 8dc27638e7e2553e80b2b621f232be5eb1e85ef3
F src/analyze.c da65ce99bb159b10e85a1e460adbe53a88062500
F src/attach.c 17bec1f18254d9341369f20f90ba24ce35d20d10
F src/alter.c 6a0c176e64a34929a4436048066a84ef4f1445b3
F src/analyze.c a038162344265ac21dfb24b3fcc06c666ebb9c07
F src/attach.c 252c4f7e36cc219349451ed63e278c60e80b26f3
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 8ff0b7018df253c7f30d3f9702b0b16f19209d5c
F src/backup.c 6728d6d48d55b449af76a3e51c0808849cb32a2e
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
F src/btree.c 5047fb303cdf6806a42676a6f513c57e15b7d69b
F src/btree.h b4ba2fdf6b64c7c376bdfffa826af6b786b151d9
F src/btreeInt.h 5b034ff54800046cc5870605d683ac1f9134bd99
F src/build.c 0018d49629fc4807100c988dd191dd95e185bb38
F src/callback.c da3c38d0ef5d7f04fae371e519bda61aa9cb1704
F src/btree.c 9004c98fc576306eee4fc0562ffeb362ef53912c
F src/btree.h 10f9296bf4edf034f5adce921b7b4383a56a1c90
F src/btreeInt.h 20f73dc93b1eeb83afd7259fbc6bd7dcf2df7fe4
F src/build.c 00a327120d81ace6267e714ae8010c997d55de5d
F src/callback.c a1d1b1c9c85415dff013af033e2fed9c8382d33b
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c 4f3aadad62c6c9f0d4e5a96718516ac4e3c598df
F src/date.c 5dd8448a0bfea8d31fb14cff487d0c06ff8c8b20
F src/ctime.c 7deec4534f3b5a0c3b4a4cbadf809d321f64f9c4
F src/date.c 1548fdac51377e4e7833251de878b4058c148e1b
F src/delete.c 7ed8a8c8b5f748ece92df173d7e0f7810c899ebd
F src/expr.c 9ee507c3dc6eaa5657cbd1dad026cdeda89c559f
F src/expr.c 1810f3056b11de99cc10e24629edf00e5fbd3a75
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 58bbf52c6ddd3f64ca40a3230f9e548a83a5cb16
F src/func.c 464b0dc70618b896c402c574eb04bc5eacf35341
F src/fkey.c 17950a28f28b23e8ad3feaac5fc88c324d2f600a
F src/func.c cb41f614edc43b00bfeb030f9768e80eaff47edd
F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
@ -137,52 +143,52 @@ F src/insert.c a4995747c062256582a90b4f87f716e11b067050
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
F src/loadext.c 6d422ea91cf3d2d00408c5a8f2391cd458da85f8
F src/main.c 99622181f36d68e9f2a851c7b34263b3dcd03470
F src/malloc.c 19a468460c7df72de245f10c06bd0625777b7c83
F src/loadext.c 8af9fcc75708d60b88636ccba38b4a7b3c155c3e
F src/main.c 6653e46db7ecb5a7449d8a12900147192f748b97
F src/malloc.c 92d59a007d7a42857d4e9454aa25b6b703286be1
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 89d4ea8d5cdd55635cbaa48ad53132af6294cbb2
F src/mem2.c 9e5f72e38573db9598fe60d3fa530d473cc8714e
F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
F src/mem3.c 9b237d911ba9904142a804be727cc6664873f8a3
F src/mem5.c eb7a5cb98915dd7a086fa415ce3a5a0f20d0acff
F src/memjournal.c 4a93a25ad9f76c40afa070ffd7187eb3a5fd7aee
F src/mem5.c 6fe00f46997bebb690397cb029719f711e7640e3
F src/memjournal.c 0ebce851677a7ac035ba1512a7e65851b34530c6
F src/mutex.c 6949180803ff05a7d0e2b9334a95b4fb5a00e23f
F src/mutex.h 6fde601e55fa6c3fae768783c439797ab84c87c6
F src/mutex.h fe2ef5e1c4dae531d5a544f9241f19c56d26803d
F src/mutex_noop.c d5cfbca87168c661a0b118cd8e329a908e453151
F src/mutex_os2.c 6a62583e374ba3ac1a3fcc0da2bfdac7d3942689
F src/mutex_unix.c abb8c98a6c27c57280e71522d059e929c708d019
F src/mutex_w32.c b7ed3366a1d44a62a17d4eaefdaa2e7c25f944c2
F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579
F src/mutex_w32.c 3ade5ae71449d1d023f0ebb3184c2ae6aa9307e4
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
F src/os.c 60178f518c4d6c0dcb59f7292232281d7bea2dcf
F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d
F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e
F src/os_unix.c 11194cbcf6a57456e58022dc537ab8c3497d9bb9
F src/os_win.c 51cb62f76262d961ea4249489383d714501315a7
F src/pager.c a5f5d9787b11dfb0b6082e6f5846d00b459a8e19
F src/pager.h ef8c8f71ab022cc2fff768a1175dd32355be9dcd
F src/os_os2.c 2e452c9f2ca507623ad351c33a8a8b27849b1863
F src/os_unix.c 1be46a35bad4bec5171e4de88aaff817260eb378
F src/os_win.c 9abdcdd925416d854eabb0996c96debd92abfef5
F src/pager.c b0fcbe3038fd08b111e1cf1deddd5f42418004d8
F src/pager.h 0ea59db2a33bc6c2c02cae34de33367e1effdf76
F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
F src/pcache.c 09d38c44ab275db581f7a2f6ff8b9bc7f8c0faaa
F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
F src/pcache1.c e921e8a1d52c93abde63cb6dad1fa39770410c52
F src/pragma.c 8b24ce00a93de345b6c3bd1e1e2cfba9f63d2325
F src/prepare.c ce4c35a2b1d5fe916e4a46b70d24a6e997d7c4c6
F src/printf.c 8ae5082dd38a1b5456030c3755ec3a392cd51506
F src/pcache1.c d548e31beafa792d1994b663a29a5303569efc4e
F src/pragma.c 8a6cd3c787f882fa44f6490d2411fc26839ce8f3
F src/prepare.c 395b3fab1b93f45b6aa194b23ebc201221c47b99
F src/printf.c df2ff3bb5409e8958136933342c46464fbd017e7
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c 8add6cab889fc02e1492eda8dba462ccf11f51dd
F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056
F src/sqlite.h.in 2d72a6242df41c517e38eec8791abcf5484a36f1
F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
F src/sqliteInt.h e33b15e8176442bf7484f0e716edfd1ce03b2979
F src/select.c 8a7ba246b0b4bb45df7fbc52681728a0e3deaaa7
F src/shell.c 83c6f0cc5a79a081c7b9ddfe4f557b47e0bad976
F src/sqlite.h.in 76955fcd1c5371268ecc8afe0ce6c49ea750ae38
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
F src/sqliteInt.h 45926deaf59b1ce3f55d21d5f91a8cecb6a7eb4c
F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b
F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c cacee9482417b6fc6043f6bb831ff9496d46242d
F src/test1.c 55005c9781b157b1d215ba145768783b9abae78c
F src/tclsqlite.c 549859dc2c143f3deb6a92636a2d27973652c164
F src/test1.c 771407a49ae199241f0efb7055634e4a1899c026
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 4c21700c73a890a47fc685c1097bfb661346ac94
F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc
F src/test4.c 0528360b5025688002a5feb6be906ddce52eaaee
F src/test5.c e1a19845625144caf038031234a12185e40d315c
F src/test6.c c7256cc21d2409486d094277d5b017e8eced44ba
@ -193,7 +199,7 @@ F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
F src/test_config.c 5a11c51af2156e2d07186930b36f2b8239a4393f
F src/test_config.c 9f025a7f3686c94e82dc6d6bd3cbf0f89cd67487
F src/test_demovfs.c 0aed671636735116fc872c5b03706fd5612488b5
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_func.c 13b582345fb1185a93e46c53310fae8547dcce20
@ -203,48 +209,53 @@ F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
F src/test_journal.c 785edd54f963aefb3c1628124170a56697c68c70
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
F src/test_malloc.c 09a88f0c111201dc4f8c20470aa1b5f611d59200
F src/test_mutex.c ce06b59aca168cd8c520b77159a24352a7469bd3
F src/test_malloc.c fd6188b1501c0010fb4241ddc9f0d5ac402c688d
F src/test_multiplex.c 5990431a852aa21c9a67da748f23d2cf1e21f8fc
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
F src/test_osinst.c f408c6a181f2fb04c56273afd5c3e1e82f60392c
F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8
F src/test_quota.c b5576f17d701af461effd7ca1e71f0d100071192
F src/test_rtree.c 30c981837445a4e187ee850a49c4760d9642f7c3
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0
F src/test_vfs.c 702e52636113f6b9721da90ef1bf26e07fff414d
F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080
F src/trigger.c b8bedb9c0084ceb51a40f54fcca2ce048c8de852
F src/update.c 1521162d20c2994af1fdc8833e1a88dae09052c8
F src/update.c 227e6cd512108b84f69421fc6c7aa1b83d60d6e0
F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685
F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b
F src/vacuum.c 241a8386727c1497eba4955933356dfba6ff8c9f
F src/vdbe.c 66c262a923915e596379b1d597178e04c5d719e4
F src/util.c ab1c92426494f499f42b9e307537b03e923d75c1
F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f
F src/vdbe.c 5d310eaf1a4d8383602126fa82e01291ab7d3cf3
F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2
F src/vdbeInt.h ffd68c4d4229227a5089bec53a1c635146177abc
F src/vdbeapi.c d0f4407e465f261780ad725c1caece7d66a6aa35
F src/vdbeaux.c c73bcefcebfd3d2cf91bf6a41ef0fb0d884814c6
F src/vdbeblob.c 258a6010ba7a82b72b327fb24c55790655689256
F src/vdbemem.c e5673f81a2381b35c60e73ef0a8502be2ab1041e
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
F src/vtab.c 0e8e0cb30dffb078367e843e84e37ef99236c7e4
F src/wal.c 5ac2119e23ee4424599d4275b66dc88d612a0543
F src/wal.h 96669b645e27cd5a111ba59f0cae7743a207bc3c
F src/vdbeInt.h 6e6f28e9bccc6c703dca1372fd661c57b5c15fb0
F src/vdbeapi.c 8e9324fd35eb70d0b5904bd1af40f2598744dc4d
F src/vdbeaux.c 33448d23b857654dd69ed2103611f5c733606f68
F src/vdbeblob.c 18955f0ee6b133cd08e1592010cb9a6b11e9984c
F src/vdbemem.c 411649a35686f54268ccabeda175322c4697f5a6
F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5
F src/vtab.c b297e8fa656ab5e66244ab15680d68db0adbec30
F src/wal.c dbca424f71678f663a286ab2a98f947af1d412a7
F src/wal.h c1aac6593a0b02b15dc625987e619edeab39292e
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c 7db3e41c2a846f9deeb24f1bbb75461b4010b7b5
F src/where.c af069e6b53234118014dabfece96a9515b69d76b
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 6745008c144bd2956d58864d21f7b304689c1cce
F test/all.test 51756962d522e474338e9b2ebb26e7364d4aa125
F test/alter.test 15f9224868b290d6bf7a63f31437f31aee070636
F test/alter2.test 52096b711afe5f219e575c6db7a70f7a35df4f63
F test/alter3.test 25b95a136708f22b87184fa6a4309eea03d65153
F test/alter4.test 9386ffd1e9c7245f43eca412b2058d747509cc1f
F test/alter2.test 75f731508f1bf27ba09a6075c66cd02216ba464b
F test/alter3.test 8677e48d95536f7a6ed86a1a774744dadcc22b07
F test/alter4.test 1e5dd6b951e9f65ca66422edff02e56df82dd403
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/analyze.test bf692e7db414f268a136bade16c03a1bdbb9240c
F test/analyze2.test 59dac6c399c0c5d1a90a11ee7cc606743fb6db93
F test/analyze3.test 6d4f4b0929545a9d1af803a0608a0c51b92a3537
F test/analyze.test c1eb87067fc16ece7c07e823d6395fd831b270c5
F test/analyze2.test 3bde8f0879d9c1f2df3af21fcf42e706d8ee1e43
F test/analyze3.test 820ddfb7591b49607fbaf77240c7955ac3cabb04
F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045
F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3
F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6
F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e
@ -253,17 +264,17 @@ F test/async5.test f3592d79c84d6e83a5f50d3fd500445f7d97dfdf
F test/attach.test ce9660e51768fab93cf129787be886c5d6c4fd81
F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437
F test/attach3.test bd9830bc3a0d22ed1310c9bff6896927937017dc
F test/attachmalloc.test 38d2da5fdaf09ba0add57296967a3061e5842584
F test/auth.test 8f21c160a4562f54f27618e85bac869efcecbcaf
F test/attachmalloc.test 1d5b821a676f7bf0b00d87cc106b78966789ba57
F test/auth.test 26cc6f219580191539bf335abe03e55e49310846
F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
F test/autoinc.test 85ef3180a737e6580086a018c09c6f1a52759b46
F test/autoindex1.test 7df441bf0e7a88644eb80993339dbf1db3a12c68
F test/autoindex1.test 860fc83f4fefb0c68ad062afc3ff43faa1534fc4
F test/autovacuum.test bb7c0885e6f8f1d633045de48f2b66082162766d
F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
F test/backcompat.test 49bd844eb245f0b2b6f2a3f8bebad0065403a9a7
F test/backup.test 200e64bd91244b73ca8094bc1e03dfc83cc94c2e
F test/backcompat.test 541314d69ec9db3e03630b7616696ddc5048efb1
F test/backup.test 004d3b78bffd990741ab50133ed4347c25c172b1
F test/backup2.test b7c69f937c912e85ac8a5dbd1e1cf290302b2d49
F test/backup_ioerr.test 1f012e692f42c0442ae652443258f70e9f20fa38
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
@ -284,12 +295,13 @@ F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45
F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983
F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
F test/cache.test c4288607b54f2702858492fc4b92828336a1812f
F test/capi2.test 00032d7504b9c14f1b36331670c5e7b0f73e3c5d
F test/cache.test 754baab2f18089fc9bcba7afaeb4dc907c6c6de2
F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360
F test/capi3.test 1945a2ba75e3f4c49d5beb8fc092115b6292d471
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
F test/capi3c.test bea67403a5e37a4b33230ee4723e315a2ffb31e7
F test/capi3d.test 57d83b690d7364bde02cddbf8339a4b50d80ce23
F test/capi3d.test cd36571f014f34bdc4421967f6453cbb597d5d16
F test/capi3e.test 4fda47388ddfbfe807987aa62f46fcbceec9327f
F test/cast.test 166951664a0b0a2e0f8fb5997a152490c6363932
F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
@ -305,7 +317,7 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/colmeta.test 087c42997754b8c648819832241daf724f813322
F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
F test/conflict.test f2f2b2950730a9532e11e468070cebf389f5c375
F test/conflict.test cabc41f7616675df71b4fddabca3bd5d9221915a
F test/corrupt.test 1a5bef8b2d178859af69814ecedcd37219a89968
F test/corrupt2.test 808a28d0ca3b97e9aa8c91cd2b485ea2700b76d1
F test/corrupt3.test a399dacccb91c732f6b071c913e70d195af8c058
@ -333,8 +345,8 @@ F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/createtab.test 199cf68f44e5d9e87a0b8afc7130fdeb4def3272
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
F test/date.test 6354b883f922c38046a8efbad187cc95df6da023
F test/dbstatus.test 946e1399d4574fc5dac934cceedbc76924af3f5a
F test/date.test a18a2ce81add84b17b06559e82ad7bb91bc6ddff
F test/dbstatus.test 175b088308f2ce3f7afb8208f25c10878ee05921
F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
F test/delete.test f7629d9eb245dfca170169cc5c7a735dec34aeb4
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
@ -344,22 +356,35 @@ F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f
F test/diskfull.test 0cede7ef9d8f415d9d3944005c76be7589bb5ebb
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
F test/e_expr.test 8a35ce2718c61e871970bda09f4f3e549067c1ba
F test/e_fkey.test 6721a741c6499b3ab7e5385923233343c8f1ad05
F test/e_createtable.test b8f5286879315d5b7f4cc5ead1afda4846f0c0bb
F test/e_delete.test 55d868b647acc091c261a10b9b0cb0ab660a6acb
F test/e_droptrigger.test ddd4b28ed8a3d81bd5153fa0ab7559529a2ca03a
F test/e_dropview.test b347bab30fc8de67b131594b3cd6f3d3bdaa753d
F test/e_expr.test 4e004d1f5187d4bbc9ca3d55660a8d164dd59f4e
F test/e_fkey.test 38039b840ab19331000b0f0eb1d82baa7208a67a
F test/e_fts3.test 75bb0aee26384ef586165e21018a17f7cd843469
F test/e_insert.test 7390c2da39f16a134dc9a439144768c727757d2c
F test/e_reindex.test a064f0878b8f848fbca38f1f61f82f15a3000c64
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
F test/e_select.test bf385ae3aa0f014c4933ae66fd3e1302138493eb
F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
F test/e_update.test 963d6876064e65f318d1c93aaed36a02b9b389bf
F test/e_vacuum.test 057cc29445746fc1d2542984ff0253d511a234bd
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398
F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041
F test/enc4.test 4b575ef09e0eff896e73bd24076f96c2aa6a42de
F test/eqp.test 69670e7919030f21de29fb99bf1d68f97aedcbdb
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
F test/exclusive.test b1f9012cabc124af947165d15ffa62ad20f63db8
F test/exclusive2.test fcbb1c9ca9739292a0a22a3763243ad6d868086b
F test/exclusive.test 53e1841b422e554cecf0160f937c473d6d0e3062
F test/exclusive2.test b65264c3e76e1db6c6eda15c02000a40743f6541
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/expr.test 9f521ae22f00e074959f72ce2e55d46b9ed23f68
F test/expr.test 620a636cf7b7d4e5834a0b9d83a4da372e24a7b7
F test/fallocate.test 43dc34b8c24be6baffadc3b4401ee15710ce83c6
F test/filectrl.test 97003734290887566e01dded09dc9e99cb937e9e
F test/filefmt.test f77c92141960b7933bc6691631d2ad62257ef40a
F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da
F test/fkey2.test e028cd80aa0bd38541c99214e3ba2dfccadffe6f
F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f
F test/fkey3.test 42f88d6048d8dc079e2a8cf7baad1cc1483a7620
F test/fkey_malloc.test a5ede29bd2f6e56dea78c3d43fb86dd696c068c8
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
@ -399,37 +424,45 @@ F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e
F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
F test/fts3_common.tcl 4d8eec9db565fed9098f45c378f28e1657802011
F test/fts3aa.test 5327d4c1d9b6c61021696746cc9a6cdc5bf159c0
F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0
F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9
F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63
F test/fts3ad.test e40570cb6f74f059129ad48bcef3d7cbc20dda49
F test/fts3ae.test ce32a13b34b0260928e4213b4481acf801533bda
F test/fts3af.test d394978c534eabf22dd0837e718b913fd66b499c
F test/fts3ag.test 0b7d303f61ae5d620c4efb5e825713ea34ff9441
F test/fts3ah.test ba181d6a3dee0c929f0d69df67cac9c47cda6bff
F test/fts3ah.test dc9f66c32c296f1bc8bcc4535126bddfeca62894
F test/fts3ai.test d29cee6ed653e30de478066881cec8aa766531b2
F test/fts3aj.test 584facbc9ac4381a7ec624bfde677340ffc2a5a4
F test/fts3ak.test bd14deafe9d1586e8e9bf032411026ac4f8c925d
F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f
F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
F test/fts3ao.test 0aa29dd4fc1c8d46b1f7cfe5926f7ac97551bea9
F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9
F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3cov.test 3a9d8618a3107166530c447e808f8992372e0415
F test/fts3corrupt.test d874ba27975aa8e5514bf58bf97b473404de0dbb
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
F test/fts3d.test 95fb3c862cbc4297c93fceb9a635543744e9ef52
F test/fts3defer.test d6cb0db9b5997ecf863d96ff419f83f8f2c87f4f
F test/fts3defer2.test da840efaedebfdd54293d04b36098e2d9872caa6
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3malloc.test 059592c4f37ccd30138bbf8e3e5b7982cb5c8f2e
F test/fts3fault.test f83e556465bb69dc8bc676339eca408dce4ca246
F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b
F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
F test/fts3query.test 2468caf7938dbc3be2e049524320ce4faf2227b3
F test/fts3rnd.test 707533ce943f490443ce5e696236bb1675a37635
F test/fts3snippet.test 9f9a4a7e396c5d8ce2898be65ebabc429555430f
F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2
F test/fts3rnd.test 2b1a579be557ab8ac54a51b39caa4aa8043cc4ad
F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
F test/fts3snippet.test a12f22a3ba4dd59751a57c79b031d07ab5f51ddd
F test/fts4aa.test eadf85621c0a113d4c7ad3ccbf8441130e007b8f
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
F test/func3.test 7ba2ca5a1e9bca900ba2c230cf04bd67184bc1bc
F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6
F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
@ -437,20 +470,22 @@ F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test dd7001ac86d09c154a7dff064f4739c60e2b312c
F test/hook.test f04c3412463f8ec117c1c704c74ca0f627ce733a
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
F test/in.test d49419c6df515852f477fa513f3317181d46bc92
F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
F test/incrblob.test e557f262cd2cc088e6bb4d154575a1bbe242edcd
F test/incrblob.test 76e787ca3301d9bfa6906031c626d26f8dd707de
F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
F test/incrvacuum.test 453d1e490d8f5ad2c9b3a54282a0690d6ae56462
F test/incrvacuum2.test 9e22a794899c91b7d8c8e12eaacac8df249faafe
F test/incrvacuum_ioerr.test 57d2f5777ab13fa03b87b262a4ea1bad5cfc0291
F test/index.test cbf301cdb2da43e4eac636c3400c2439af1834ad
F test/index.test df7c00c6edd9504ab71c83a9514f1c5ca0fa54d8
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7
F test/indexedby.test 5a1180602f2e72c481467bd4cae05dae5dc36f47
F test/indexedby.test d7367c5a0e8ed8db642824a68126753e0808c706
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
@ -461,7 +496,7 @@ F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1
F test/intpkey.test 537669fd535f62632ca64828e435b9e54e8d677f
F test/io.test 1b895d6774491895cbc75659969f07ca01860c88
F test/ioerr.test 390785ec65f10aa58a82b048ee12e9052d783fa8
F test/ioerr.test 622aebd2f24779cafaf5dd3e3c2b349ce40ade3b
F test/ioerr2.test 1b56cb80d5b0726ee3ba325ca175734541e32955
F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
F test/ioerr4.test fc6eddfec2efc2f1ed217b9eae4c1c1d3516ce86
@ -477,28 +512,28 @@ F test/journal2.test 50a3604768494d4a337f194f0a9480e7c57dcb72
F test/journal3.test ff175219be1b02d2f7e54297ad7e491b7533edb6
F test/jrnlmode.test e3fe6c4a2c3213d285650dc8e33aab7eaaa5ce53
F test/jrnlmode2.test a19e28de1a6ec898067e46a122f1b71c9323bf00
F test/jrnlmode3.test cfcdb12b90e640a23b92785a002d96c0624c8710
F test/jrnlmode3.test c6522b276ba315fd1416198de6fc1da9e72409fb
F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05
F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
F test/like.test 565d240313f15a8afa8d7098dc9fe303c1e2a496
F test/like.test 0f64aeaed50b6e3ebaef3af0b3b8f894aed5acca
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
F test/limit.test 2db7b3b34fb925b8e847d583d2eb67531d0ce67e
F test/loadext.test 0393ce12d9616aa87597dd0ec88181de181f6db0
F test/loadext2.test 0bcaeb4d81cd5b6e883fdfea3c1bdbe1f173cbca
F test/lock.test 842e80b6be816c79525a20b098cca066989feed7
F test/lock.test db74fdf5a73bad29ab3d862ea78bf1068972cc1d
F test/lock2.test 5242d8ac4e2d59c403aebff606af449b455aceff
F test/lock3.test f271375930711ae044080f4fe6d6eda930870d00
F test/lock4.test c82268c031d39345d05efa672f80b025481b3ae5
F test/lock5.test b2abb5e711bc59b0eae00f6c97a36ec9f458fada
F test/lock6.test 8df56060f396151777390982422c800d026e1722
F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5
F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64
F test/lock_common.tcl 18c637fc89e12f1ac0d27d2186f12c3d3f789e3e
F test/lookaside.test 382e7bc2fab23d902c8eafb1b9ed7ababfff75a6
F test/lock_common.tcl d279887a0ab16cdb6d935c1203e64113c5a000e9
F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2
F test/main.test 9d7bbfcc1b52c88ba7b2ba6554068ecf9939f252
F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
F test/malloc.test 927e6c8668a1d48c23aa6189bda02aff5a1b83de
F test/malloc3.test 4bc57f850b212f706f3e1b37c4eced1d5a727cd1
F test/malloc3.test 4128b1e6ffa506103b278ad97af89174f310c7ca
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
F test/malloc5.test 4d16d1bb26d2deddd7c4f480deec341f9b2d0e22
F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
@ -517,11 +552,11 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
F test/malloc_common.tcl f4a04b7a733eb114a3da16eb39035cde2c851220
F test/malloc_common.tcl 660b82ab528521cc4a48ff6df05ca3b6a00d47c5
F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c
F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
F test/memsubsys1.test 8fb47b7e2523f94c100f5885c5697505524de4b9
F test/memsubsys1.test 679db68394a5692791737b150852173b3e2fea10
F test/memsubsys2.test 72a731225997ad5e8df89fdbeae9224616b6aecc
F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
@ -529,12 +564,13 @@ F test/minmax3.test 66a60eb0f20281b0753249d347c5de0766954cee
F test/misc1.test e56baf44656dd68d6475a4b44521045a60241e9b
F test/misc2.test a628db7b03e18973e5d446c67696b03de718c9fd
F test/misc3.test 72c5dc87a78e7865c5ec7a969fc572913dbe96b6
F test/misc4.test 91e8ed25c092c2bb4e0bb01864631e2930f8d7de
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test c5f4e6a82e04e71820c0f9f64f6733f04c8ae0ae
F test/misc7.test 29032efcd3d826fbd409e2a7af873e7939f4a4e3
F test/misuse.test 30b3a458e5a70c31e74c291937b6c82204c59f33
F test/mutex1.test 5b71777fc127509cd257910c8db799de557a02de
F test/multiplex.test 92a4839213fd8cba8b59f86d42b7a1da1857db39
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test a44e04df1486fcfb02d32468cbcd3c8e1e433723
F test/notify1.test 8433bc74bd952fb8a6e3f8d7a4c2b28dfd69e310
@ -543,32 +579,36 @@ F test/notify3.test d60923e186e0900f4812a845fcdfd8eea096e33a
F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec
F test/pager1.test 6922029d71a8090169c71a67a141b6b94ad17d50
F test/pager2.test 0fbb6b6dc40ce1fecfe758c555a748ad2e9beaa3
F test/pager1.test 7006a8b5dd3df1fe0d51d7da014333d7dc099778
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
F test/pagerfault.test 84c6a4fcfe1a9e450fc1cec86f5baebfb017e53e
F test/pagerfault.test 9de4d3e0c59970b4c6cb8dac511fa242f335d8a7
F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401
F test/pagerfault3.test 9b413f48a3e9a9a8c26968118f8db19fd7bfb8c7
F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb
F test/pcache.test 4118a183908ecaed343a06fcef3ba82e87e0129d
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16
F test/permutations.test 17498d1219f922d5a6da893a94c4dc7766fb2426
F test/pragma.test ed78d200f65c6998df51196cb8c39d5300570f24
F test/permutations.test c0ce0f3b741dd92a6d4c2671dbacba4b92dd81eb
F test/pragma.test fdfc09067ea104a0c247a1a79d8093b56656f850
F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quota.test ddafe133653093eb9a99ccd6264884ae43f9c9b8
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
F test/releasetest.tcl 627ccd04a113a193c375594bd5d6d051d8220658
F test/rollback.test 1a83118ea6db4e7d8c10eaa63871b5e90502ffdc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test e58e0acef38b527ed1b0b70d3ada588f804af287
F test/rtree.test fb372aff108d4371bd0b5e63e106947587ff4310
F test/savepoint.test 992d6429b6bce16ac172f7431975044ceaeb0803
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/savepoint.test a1bef7ace82cc7922975fa96b06176e9bd5114cf
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
@ -577,13 +617,14 @@ F test/savepoint6.test 76d3948568b2cdc0c13a671cadcae75009b183d6
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
F test/schema4.test e6a66e20cc69f0e306667c08be7fda3d11707dc5
F test/securedel.test 328d2921c0ca49bdd3352e516b0377fc07143254
F test/select1.test f67ca2dfc05df41c7b86eb32ca409b427a5f43b0
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
F test/select4.test 44aa6e7110592e18110b0b9cf5c024d37d23be17
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test 2b5e8500d8ec3dd4c8e0c99eb1431b3d11fcc24c
F test/select6.test cc25a8650cf9a4d4f74e586c45a75f9836516b18
F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
F test/select9.test 74c0fb2c6eecb0219cbed0cbe3df136f8fbf9343
@ -592,7 +633,7 @@ F test/selectB.test f305cc6660804cb239aab4e2f26b0e288b59958b
F test/selectC.test f9bf1bc4581b5b8158caa6e4e4f682acb379fb25
F test/server1.test f5b790d4c0498179151ca8a7715a65a7802c859c
F test/shared.test b9114eaea7e748a3a4c8ff7b9ca806c8f95cef3e
F test/shared2.test d6ba4ca1827ea36a1ac23a99e3c36eeac9165450
F test/shared2.test 7f6ad2d857d0f4e5d6a0b9a897b5e56a6b6ea18c
F test/shared3.test d69bdd5f156580876c5345652d21dc2092e85962
F test/shared4.test d0fadacb50bb6981b2fb9dc6d1da30fa1edddf83
F test/shared6.test 990d2584b5db28e6e1f24742c711b26e59757b67
@ -613,19 +654,20 @@ F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
F test/sqllimits1.test e90a0ed94452076f6a10209d378e06b5f75ef0a0
F test/stat.test 70fe540ffb285947aead5533dfd0c8c12f17f14e
F test/stmt.test 7915bd3e8380b956c095f40f41a775a30716e649
F test/stat.test c7b20ea43003dc2dc33335e231c27be8284c4a2a
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 8468e057d8a5531ff99e504e77fcc585a0291bf2
F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3
F test/table.test 04ba066432430657712d167ebf28080fe878d305
F test/tableapi.test 7262a8cbaa9965d429f1cbd2747edc185fa56516
F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
F test/tempdb.test 800c36623d67a2ad1f58784b9c5644e0405af6e6
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
F test/tester.tcl 6135019fcfac363ea0e11aee670cc97080ab578e
F test/tester.tcl dafe0d30279f6d380d5d2a535781dda91b8cfc3f
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@ -636,23 +678,30 @@ F test/thread2.test e08034b83fe9693ade77049732518e5b3d2d700d
F test/thread_common.tcl 2aa6f2fdcd4d6e461169c3e5ca098eebf643b863
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
F test/threadtest3.c 58df1e3c060f534fd7fb0702331b0acc41c381d8
F test/threadtest3.c d6d209190c7110f9a7e6a8154bdc3260efdbf8b7
F test/tkt-02a8e81d44.test 58494de77be2cf249228ada3f313fa399821c6ab
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
F test/tkt-31338dca7e.test 5741cd48de500347a437ba1be58c8335e83c5a5e
F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac
F test/tkt-38cb5df375.test 9e9b19857dba0896a8efdaf334d405ba423492f2
F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7
F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00
F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
F test/tkt-5d863f876e.test 884072c2de496ddbb90c387c9ebc0d4f44a91b8e
F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
F test/tkt-5ee23731f.test 3581260f2a71e51db94e1506ba6b0f7311d002a9
F test/tkt-78e04e52ea.test fb5430c675e708f5cbafdf3e7e5593da5145a527
F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
F test/tkt-94c04eaadb.test be5ea61cb04dfdc047d19b5c5a9e75fa3da67a7f
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
F test/tkt-cbd054fa6b.test f14f97ea43662e6f70c9e63287081e8be5d9d589
F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
F test/tkt-d82e3f3721.test 731359dfdcdb36fea0559cd33fec39dd0ceae8e6
F test/tkt-f3e5abed55.test 91713833e266fbdc60f2030e05647ad4762073f6
F test/tkt-f3e5abed55.test 19fb59268da6f20a69a181b9c14154132d1c65e3
F test/tkt-f777251dc7a.test 6f24c053bc5cdb7e1e19be9a72c8887cf41d5e87
F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4
F test/tkt-fc62af4523.test 72825d3febdedcd5593a27989fc05accdbfc2bb4
@ -668,7 +717,7 @@ F test/tkt1536.test 83ff7a7b6e248016f8d682d4f7a4ae114070d466
F test/tkt1537.test e3a14332de9770be8ff14bd15c19a49cbec10808
F test/tkt1567.test 18023cc3626a365f0118e17b66decedec93b1a6f
F test/tkt1644.test 80b6a2bb17885f3cf1cb886d97cdad13232bb869
F test/tkt1667.test c5e5a9f6c71634b5edd1c54eeb84008771526562
F test/tkt1667.test 5d208e8d8cbcf82a446b315774290b66b464bc5f
F test/tkt1873.test 255a002b9afdcf8b0fa3188984e2c964202340e9
F test/tkt2141.test f543d96f50d5a5dc0bc744f7db74ea166720ce46
F test/tkt2192.test ff40157e5f42e65f844255d220fc6b290470942f
@ -704,7 +753,7 @@ F test/tkt3346.test 6f67c3ed7db94dfc5df4f5f0b63809a1f611e01a
F test/tkt3357.test 77c37c6482b526fe89941ce951c22d011f5922ed
F test/tkt3419.test 1bbf36d7ea03b638c15804251287c2391f5c1f6b
F test/tkt3424.test 61f831bd2b071bd128fa5d00fbda57e656ca5812
F test/tkt3442.test 89d7b41a4ec4d9d9b40ab8575d648579fb13cb4f
F test/tkt3442.test 0adb70e9fe9cb750a702065a68ad647409dbc158
F test/tkt3457.test edbf54b05cbe5165f00192becbd621038f1615e4
F test/tkt3461.test 228ea328a5a21e8663f80ee3d212a6ad92549a19
F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218
@ -718,7 +767,7 @@ F test/tkt35xx.test ed9721bd9eb1693b3b4d3cf2a093fa7f92af0c93
F test/tkt3630.test 929f64852103054125200bc825c316d5f75d42f7
F test/tkt3718.test 3b59dcb5c4e7754dacd91e7fd353a61492cc402a
F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
F test/tkt3757.test 8f2208930655bbd4f92c14e19e72303a43e098ef
F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595
F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33
F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df
F test/tkt3773.test 430b06567ce40285dfd2c4834a2a61816403efeb
@ -741,6 +790,7 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
F test/trace2.test 092bc2c5776272700450d60a36919921095bdc21
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97
@ -755,22 +805,22 @@ F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31
F test/triggerA.test eaf11a29db2a11967d2d4b49d37f92bce598194e
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
F test/triggerC.test 2a23edcc00684d084902ba5ec93e721775c3a70a
F test/triggerC.test 8a691ff6dd47df2e57395bbec4b62101fac0f363
F test/triggerD.test c6add3817351451e419f6ff9e9a259b02b6e2de7
F test/types.test 9a825ec8eea4e965d7113b74c76a78bb5240f2ac
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
F test/vacuum.test 15ae6784e70428b8db64e95c92d84b19e507b719
F test/vacuum2.test ec57f21d394b7b72249b11f8e4b5d487bab56539
F test/vacuum.test 29b60e8cc9e573b39676df6c4a75fe9e02d04a09
F test/vacuum2.test 2165164ed2463816e8c4648d0a779a863ce1a76c
F test/vacuum3.test f39ad1428347c5808cd2da7578c470f186a4d0ce
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b
F test/view.test 45f518205ecdb6dd23a86dd4a99bb4ae945e625d
F test/vtab1.test 9bc4a349a1989bcd064eb3b8fac2f06aca64297a
F test/vtab1.test 7b79832824cbae37ff01a06ed155027f7c15bf9e
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
@ -788,31 +838,33 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
F test/wal.test 70227190e713b3e7eb2a7d5ec3510b66db01f327
F test/wal2.test 223f3e14d475730af772a7f5862d4bcfa7565c3a
F test/wal3.test 695ea0f6c516423c611891df9a285aacd33344e3
F test/wal2.test 3de797854de175323e7351b5f2514a30d1ee1410
F test/wal3.test ac51126c36814bce334f66a0a4dbbfa56d429733
F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30
F test/wal6.test 07aa31ca8892d0527f2c5c5a9a2a87aa421dfaa8
F test/wal_common.tcl 895d76138043b86bdccf36494054bdabcf65837b
F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4
F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0
F test/walcksum.test a37b36375c595e61bdb7e1ec49b5f0979b6fc7ce
F test/walcrash.test e763841551d6b23677ccb419797c1589dcbdbaf5
F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
F test/walfault.test 05c470688d742688e455dd56816bd6bcffa298f8
F test/walfault.test 81ed760def1c1573151d416b0d09178cf006f9fd
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
F test/walmode.test 4ecd37284f245247f7935896ab66f6943f0432a0
F test/walshared.test 985b4a3406b2b2dace1d52a42d26a11dd6900981
F test/walmode.test 22ddccd073c817ac9ead62b88ac446e8dedc7d2c
F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
F test/where3.test 3bf8006d441b66a57bee02bb420423f84eb8fde3
F test/where3.test 8ebedae552e13fc7f2b4e8df6cbe72a095347400
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test a0a92b8ce48d9c027fbdd7b764c7de1e1213575a
F test/where7.test aa4cfcd6f66e2a4ef87b6717327325bf4d547502
F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
F test/where9.test be19e1a92f80985c1a121b4678bf7d2123eaa623
F test/where9.test 7ee38c3fd67e76789a6ec769f62f6433d3d4a5cf
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
@ -821,24 +873,24 @@ F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/lemon.c fe890e2d8d2db1e3f57e2a22503dbb0f6843e517
F tool/lemon.c dfd81a51b6e27e469ba21d01a75ddf092d429027
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c.tcl aff0d53f0e84cf919922c0d02e767bdf5eeafb90
F tool/mksqlite3h.tcl eb100dce83f24b501b325b340f8b5eb8e5106b3b
F tool/mksqlite3c.tcl e0db70c2c52b0e3d0867ca931229e5b90ffe7837
F tool/mksqlite3h.tcl d76c226a5e8e1f3b5f6593bcabe5e98b3b1ec9ff
F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87
F tool/omittest.tcl 27d6f6e3b1e95aeb26a1c140e6eb57771c6d794a
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/shell1.test 930444cadb71ce9ce78bc6cd14ec21e6b69776ea
F tool/shell1.test c31b0814a9c543db51ca0cc63edb5e77ea532303
F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3
F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836
F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce
F tool/shell5.test 62bfaf9267296da1b91e4b1c03e44e7b393f6a94
F tool/showdb.c c7a978cf525ef0f8bc2fd29cd52108dd1dfa605a
F tool/showjournal.c ec3b171be148656827c4949fbfb8ab4370822f87
F tool/showdb.c 471c0f8fa472e71bb7654500096a5bdb4ea1fb2a
F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
@ -849,14 +901,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 21a1e5961bba148fda50cc0b7d472ca74f90808a
R aa9cc30f9dbcb7ea427137065cf4e4be
P 682fe41efd3578e8c9abc7138b61f361c3adbe95
R 7fccaf9f7c013ddef8926255f6aeb773
U drh
Z e73ee740c6593b4458c2d23b6fa7b923
Z 4223173e58f25d45d236e232b9c90989
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFMcsNUoxKgR168RlERApyRAKCOHvk0Gn1uE2F8YVm3k/w13/3UugCeOh16
bApiXjM1a7FP+Qr1HX5kWTQ=
=L2Lf
iD8DBQFNQvb6oxKgR168RlERAtOTAJ9BaewewKXL3RGZUy5ycaHwjeEmJACeOPbw
/JKqOugR+37RH7HnLCo9DBk=
=kgPx
-----END PGP SIGNATURE-----

View File

@ -1 +1 @@
42537b60566f288167f1b5864a5435986838e3a3
ed759d5a9edb3bba5f48f243df47be29e3fe8cd7

View File

@ -313,6 +313,11 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
}
}
}
if( zWhere ){
char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere);
sqlite3DbFree(pParse->db, zWhere);
zWhere = zNew;
}
return zWhere;
}

View File

@ -113,7 +113,8 @@ static void analyzeOneTable(
int i; /* Loop counter */
int topOfLoop; /* The top of the loop */
int endOfLoop; /* The end of the loop */
int addr; /* The address of an instruction */
int addr = 0; /* The address of an instruction */
int jZeroRows = 0; /* Jump from here if number of rows is zero */
int iDb; /* Index of database containing pTab */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
@ -132,8 +133,15 @@ static void analyzeOneTable(
#endif
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){
/* Do no analysis for tables that have no indices */
if( v==0 || NEVER(pTab==0) ){
return;
}
if( pTab->tnum==0 ){
/* Do not gather statistics on views or virtual tables */
return;
}
if( memcmp(pTab->zName, "sqlite_", 7)==0 ){
/* Do not gather statistics on system tables */
return;
}
assert( sqlite3BtreeHoldsAllMutexes(db) );
@ -150,6 +158,7 @@ static void analyzeOneTable(
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol = pIdx->nColumn;
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
@ -164,10 +173,7 @@ static void analyzeOneTable(
(char *)pKey, P4_KEYINFO_HANDOFF);
VdbeComment((v, "%s", pIdx->zName));
/* Populate the registers containing the table and index names. */
if( pTab->pIndex==pIdx ){
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
}
/* Populate the register containing the index name. */
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
#ifdef SQLITE_ENABLE_STAT2
@ -227,9 +233,10 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
for(i=0; i<nCol; i++){
CollSeq *pColl;
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
#ifdef SQLITE_ENABLE_STAT2
if( i==0 ){
#ifdef SQLITE_ENABLE_STAT2
/* Check if the record that cursor iIdxCur points to contains a
** value that should be stored in the sqlite_stat2 table. If so,
** store it. */
@ -258,12 +265,17 @@ static void analyzeOneTable(
sqlite3VdbeJumpHere(v, ne);
sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
}
#endif
sqlite3VdbeAddOp3(v, OP_Ne, regCol, 0, iMem+nCol+i+1);
/**** TODO: add collating sequence *****/
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
/* Always record the very first row */
sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
}
assert( pIdx->azColl!=0 );
assert( pIdx->azColl[i]!=0 );
pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
(char*)pColl, P4_COLLSEQ);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
}
if( db->mallocFailed ){
/* If a malloc failure has occurred, then the result of the expression
@ -274,7 +286,11 @@ static void analyzeOneTable(
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
for(i=0; i<nCol; i++){
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-(nCol*2));
int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2);
if( i==0 ){
sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */
}
sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
}
@ -302,8 +318,10 @@ static void analyzeOneTable(
** If K>0 then it is always the case the D>0 so division by zero
** is never possible.
*/
addr = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
if( jZeroRows==0 ){
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
}
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
@ -317,13 +335,35 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
}
/* If the table has no indices, create a single sqlite_stat1 entry
** containing NULL as the index name and the row count as the content.
*/
if( pTab->pIndex==0 ){
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
VdbeComment((v, "%s", pTab->zName));
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
}else{
assert( jZeroRows>0 );
addr = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, jZeroRows);
}
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
if( pParse->nMem<regRec ) pParse->nMem = regRec;
if( jZeroRows ){
sqlite3VdbeJumpHere(v, addr);
}
}
/*
** Generate code that will cause the most recent index analysis to
** be laoded into internal hash tables where is can be used.
** be loaded into internal hash tables where is can be used.
*/
static void loadAnalysis(Parse *pParse, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
@ -453,33 +493,46 @@ struct analysisInfo {
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
** argv[0] = name of the index
** argv[1] = results of analysis - on integer for each column
** argv[0] = name of the table
** argv[1] = name of the index (might be NULL)
** argv[2] = results of analysis - on integer for each column
**
** Entries for which argv[1]==NULL simply record the number of rows in
** the table.
*/
static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
analysisInfo *pInfo = (analysisInfo*)pData;
Index *pIndex;
int i, c;
Table *pTable;
int i, c, n;
unsigned int v;
const char *z;
assert( argc==2 );
assert( argc==3 );
UNUSED_PARAMETER2(NotUsed, argc);
if( argv==0 || argv[0]==0 || argv[1]==0 ){
if( argv==0 || argv[0]==0 || argv[2]==0 ){
return 0;
}
pIndex = sqlite3FindIndex(pInfo->db, argv[0], pInfo->zDatabase);
if( pIndex==0 ){
pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase);
if( pTable==0 ){
return 0;
}
z = argv[1];
for(i=0; *z && i<=pIndex->nColumn; i++){
if( argv[1] ){
pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase);
}else{
pIndex = 0;
}
n = pIndex ? pIndex->nColumn : 0;
z = argv[2];
for(i=0; *z && i<=n; i++){
v = 0;
while( (c=z[0])>='0' && c<='9' ){
v = v*10 + c - '0';
z++;
}
if( i==0 ) pTable->nRowEst = v;
if( pIndex==0 ) break;
pIndex->aiRowEst[i] = v;
if( *z==' ' ) z++;
}
@ -555,7 +608,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load new statistics out of the sqlite_stat1 table */
zSql = sqlite3MPrintf(db,
"SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
"SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
@ -583,8 +636,11 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
if( rc==SQLITE_OK ){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
char *zIndex = (char *)sqlite3_column_text(pStmt, 0);
Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase);
char *zIndex; /* Index name */
Index *pIdx; /* Pointer to the index object */
zIndex = (char *)sqlite3_column_text(pStmt, 0);
pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0;
if( pIdx ){
int iSample = sqlite3_column_int(pStmt, 1);
if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){

View File

@ -124,9 +124,8 @@ static void attachFunc(
** it to obtain the database schema. At this point the schema may
** or may not be initialised.
*/
rc = sqlite3BtreeFactory(db, zFile, 0, SQLITE_DEFAULT_CACHE_SIZE,
db->openFlags | SQLITE_OPEN_MAIN_DB,
&aNew->pBt);
rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0,
db->openFlags | SQLITE_OPEN_MAIN_DB);
db->nDb++;
if( rc==SQLITE_CONSTRAINT ){
rc = SQLITE_ERROR;
@ -367,7 +366,8 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
0, /* xStep */
0, /* xFinalize */
"sqlite_detach", /* zName */
0 /* pHash */
0, /* pHash */
0 /* pDestructor */
};
codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname);
}
@ -388,7 +388,8 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
0, /* xStep */
0, /* xFinalize */
"sqlite_attach", /* zName */
0 /* pHash */
0, /* pHash */
0 /* pDestructor */
};
codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey);
}

View File

@ -117,6 +117,16 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
return pDb->aDb[i].pBt;
}
/*
** Attempt to set the page size of the destination to match the page size
** of the source.
*/
static int setDestPgsz(sqlite3_backup *p){
int rc;
rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
return rc;
}
/*
** Create an sqlite3_backup process to copy the contents of zSrcDb from
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
@ -150,7 +160,10 @@ sqlite3_backup *sqlite3_backup_init(
);
p = 0;
}else {
/* Allocate space for a new sqlite3_backup object */
/* Allocate space for a new sqlite3_backup object...
** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
** call to sqlite3_backup_init() and is destroyed by a call to
** sqlite3_backup_finish(). */
p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup));
if( !p ){
sqlite3Error(pDestDb, SQLITE_NOMEM, 0);
@ -167,10 +180,11 @@ sqlite3_backup *sqlite3_backup_init(
p->iNext = 1;
p->isAttached = 0;
if( 0==p->pSrc || 0==p->pDest ){
/* One (or both) of the named databases did not exist. An error has
** already been written into the pDestDb handle. All that is left
** to do here is free the sqlite3_backup structure.
if( 0==p->pSrc || 0==p->pDest || setDestPgsz(p)==SQLITE_NOMEM ){
/* 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.
*/
sqlite3_free(p);
p = 0;
@ -427,32 +441,46 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
i64 iOff;
i64 iEnd;
assert( pFile );
assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
));
if( SQLITE_OK==(rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1))
&& SQLITE_OK==(rc = backupTruncateFile(pFile, iSize))
&& SQLITE_OK==(rc = sqlite3PagerSync(pDestPager))
/* This call ensures that all data required to recreate the original
** database has been stored in the journal for pDestPager and the
** journal synced to disk. So at this point we may safely modify
** the database file in any way, knowing that if a power failure
** occurs, the original database will be reconstructed from the
** journal file. */
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
/* Write the extra pages and truncate the database file as required. */
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
for(
iOff=PENDING_BYTE+pgszSrc;
rc==SQLITE_OK && iOff<iEnd;
iOff+=pgszSrc
){
i64 iOff;
i64 iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
for(
iOff=PENDING_BYTE+pgszSrc;
rc==SQLITE_OK && iOff<iEnd;
iOff+=pgszSrc
){
PgHdr *pSrcPg = 0;
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
if( rc==SQLITE_OK ){
u8 *zData = sqlite3PagerGetData(pSrcPg);
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
}
sqlite3PagerUnref(pSrcPg);
PgHdr *pSrcPg = 0;
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
if( rc==SQLITE_OK ){
u8 *zData = sqlite3PagerGetData(pSrcPg);
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
}
sqlite3PagerUnref(pSrcPg);
}
if( rc==SQLITE_OK ){
rc = backupTruncateFile(pFile, iSize);
}
/* Sync the database file to disk. */
if( rc==SQLITE_OK ){
rc = sqlite3PagerSync(pDestPager);
}
}else{
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
@ -533,6 +561,9 @@ int sqlite3_backup_finish(sqlite3_backup *p){
}
sqlite3BtreeLeave(p->pSrc);
if( p->pDestDb ){
/* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
** call to sqlite3_backup_init() and is destroyed by a call to
** sqlite3_backup_finish(). */
sqlite3_free(p);
}
sqlite3_mutex_leave(mutex);

View File

@ -918,14 +918,9 @@ static void btreeParseCellPtr(
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
int nSize; /* Total size of cell content in bytes */
nSize = nPayload + n;
if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
pInfo->iOverflow = 0;
if( (nSize & ~3)==0 ){
nSize = 4; /* Minimum cell size is 4 */
}
pInfo->nSize = (u16)nSize;
}else{
/* If the payload will not fit completely on the local page, we have
** to decide how much to store locally and how much to spill onto
@ -1672,11 +1667,20 @@ static int btreeInvokeBusyHandler(void *pArg){
** Open a database file.
**
** zFilename is the name of the database file. If zFilename is NULL
** a new database with a random name is created. This randomly named
** database file will be deleted when sqlite3BtreeClose() is called.
** then an ephemeral database is created. The ephemeral database might
** be exclusively in memory, or it might use a disk-based memory cache.
** Either way, the ephemeral database will be automatically deleted
** when sqlite3BtreeClose() is called.
**
** If zFilename is ":memory:" then an in-memory database is created
** that is automatically destroyed when it is closed.
**
** The "flags" parameter is a bitmask that might contain bits
** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK
** bit is also set if the SQLITE_NoReadlock flags is set in db->flags.
** These flags are passed through into sqlite3PagerOpen() and must
** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK.
**
** If the database is already opened in the same database connection
** and we are in shared cache mode, then the open will fail with an
** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared
@ -1698,22 +1702,38 @@ int sqlite3BtreeOpen(
u8 nReserve; /* Byte of unused space on each page */
unsigned char zDbHeader[100]; /* Database header content */
/* True if opening an ephemeral, temporary database */
const int isTempDb = zFilename==0 || zFilename[0]==0;
/* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database. This symbol is only required if
** either of the shared-data or autovacuum features are compiled
** into the library.
** false for a file-based database.
*/
#if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM)
#ifdef SQLITE_OMIT_MEMORYDB
const int isMemdb = 0;
#else
const int isMemdb = zFilename && !strcmp(zFilename, ":memory:");
#endif
#ifdef SQLITE_OMIT_MEMORYDB
const int isMemdb = 0;
#else
const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
|| (isTempDb && sqlite3TempInMemory(db));
#endif
assert( db!=0 );
assert( sqlite3_mutex_held(db->mutex) );
assert( (flags&0xff)==flags ); /* flags fit in 8 bits */
/* Only a BTREE_SINGLE database can be BTREE_UNORDERED */
assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 );
/* A BTREE_SINGLE database is always a temporary and/or ephemeral */
assert( (flags & BTREE_SINGLE)==0 || isTempDb );
if( db->flags & SQLITE_NoReadlock ){
flags |= BTREE_NO_READLOCK;
}
if( isMemdb ){
flags |= BTREE_MEMORY;
}
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
}
pVfs = db->pVfs;
p = sqlite3MallocZero(sizeof(Btree));
if( !p ){
@ -1731,7 +1751,7 @@ int sqlite3BtreeOpen(
** If this Btree is a candidate for shared cache, try to find an
** existing BtShared object that we can share with
*/
if( isMemdb==0 && zFilename && zFilename[0] ){
if( isMemdb==0 && isTempDb==0 ){
if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
int nFullPathname = pVfs->mxPathname+1;
char *zFullPathname = sqlite3Malloc(nFullPathname);
@ -1806,6 +1826,7 @@ int sqlite3BtreeOpen(
if( rc!=SQLITE_OK ){
goto btree_open_out;
}
pBt->openFlags = (u8)flags;
pBt->db = db;
sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
p->pBt = pBt;
@ -1910,6 +1931,14 @@ btree_open_out:
sqlite3_free(pBt);
sqlite3_free(p);
*ppBtree = 0;
}else{
/* If the B-Tree was successfully opened, set the pager-cache size to the
** default value. Except, when opening on an existing shared pager-cache,
** do not change the pager-cache size.
*/
if( sqlite3BtreeSchema(p, 0, 0)==0 ){
sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE);
}
}
if( mutexOpen ){
assert( sqlite3_mutex_held(mutexOpen) );
@ -2067,11 +2096,17 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
** probability of damage to near zero but with a write performance reduction.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
int sqlite3BtreeSetSafetyLevel(
Btree *p, /* The btree to set the safety level on */
int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
int fullSync, /* PRAGMA fullfsync. */
int ckptFullSync /* PRAGMA checkpoint_fullfync */
){
BtShared *pBt = p->pBt;
assert( sqlite3_mutex_held(p->db->mutex) );
assert( level>=1 && level<=3 );
sqlite3BtreeEnter(p);
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@ -2346,7 +2381,7 @@ static int lockBtree(BtShared *pBt){
pageSize-usableSize);
return rc;
}
if( nPageHeader>nPageFile ){
if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPageHeader>nPageFile ){
rc = SQLITE_CORRUPT_BKPT;
goto page1_init_failed;
}
@ -3129,8 +3164,8 @@ static void btreeEndTransaction(Btree *p){
** are no active cursors, it also releases the read lock.
*/
int sqlite3BtreeCommitPhaseTwo(Btree *p){
BtShared *pBt = p->pBt;
if( p->inTrans==TRANS_NONE ) return SQLITE_OK;
sqlite3BtreeEnter(p);
btreeIntegrity(p);
@ -3139,6 +3174,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
*/
if( p->inTrans==TRANS_WRITE ){
int rc;
BtShared *pBt = p->pBt;
assert( pBt->inTransaction==TRANS_WRITE );
assert( pBt->nTransaction>0 );
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
@ -6860,11 +6896,12 @@ int sqlite3BtreeDelete(BtCursor *pCur){
** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
** BTREE_ZERODATA Used for SQL indices
*/
static int btreeCreateTable(Btree *p, int *piTable, int flags){
static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
BtShared *pBt = p->pBt;
MemPage *pRoot;
Pgno pgnoRoot;
int rc;
int ptfFlags; /* Page-type flage for the root page of new table */
assert( sqlite3BtreeHoldsMutex(p) );
assert( pBt->inTransaction==TRANS_WRITE );
@ -6983,8 +7020,14 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){
}
#endif
assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
zeroPage(pRoot, flags | PTF_LEAF);
if( createTabFlags & BTREE_INTKEY ){
ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF;
}else{
ptfFlags = PTF_ZERODATA | PTF_LEAF;
}
zeroPage(pRoot, ptfFlags);
sqlite3PagerUnref(pRoot->pDbPage);
assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 );
*piTable = (int)pgnoRoot;
return SQLITE_OK;
}
@ -8050,8 +8093,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
void sqlite3BtreeCacheOverflow(BtCursor *pCur){
assert( cursorHoldsMutex(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert(!pCur->isIncrblobHandle);
assert(!pCur->aOverflow);
invalidateOverflowCache(pCur);
pCur->isIncrblobHandle = 1;
}
#endif

View File

@ -67,16 +67,15 @@ int sqlite3BtreeOpen(
** NOTE: These values must match the corresponding PAGER_ values in
** pager.h.
*/
#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */
#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */
#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */
#define BTREE_MEMORY 4 /* In-memory DB. No argument */
#define BTREE_READONLY 8 /* Open the database in read-only mode */
#define BTREE_READWRITE 16 /* Open for both reading and writing */
#define BTREE_CREATE 32 /* Create the database if it does not exist */
#define BTREE_MEMORY 4 /* This is an in-memory DB */
#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */
#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */
int sqlite3BtreeClose(Btree*);
int sqlite3BtreeSetCacheSize(Btree*,int);
int sqlite3BtreeSetSafetyLevel(Btree*,int,int);
int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
int sqlite3BtreeSyncDisabled(Btree*);
int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
int sqlite3BtreeGetPageSize(Btree*);
@ -108,11 +107,17 @@ int sqlite3BtreeCopyFile(Btree *, Btree *);
int sqlite3BtreeIncrVacuum(Btree *);
/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR
** of the following flags:
** of the flags shown below.
**
** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set.
** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data
** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With
** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored
** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL
** indices.)
*/
#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */
#define BTREE_ZERODATA 2 /* Table has keys only - no data */
#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */
#define BTREE_BLOBKEY 2 /* Table has keys only - no data */
int sqlite3BtreeDropTable(Btree*, int, int*);
int sqlite3BtreeClearTable(Btree*, int, int*);

View File

@ -409,16 +409,17 @@ struct BtShared {
u8 pageSizeFixed; /* True if the page size can no longer be changed */
u8 secureDelete; /* True if secure_delete is enabled */
u8 initiallyEmpty; /* Database is empty at start of transaction */
u8 openFlags; /* Flags to sqlite3BtreeOpen() */
#ifndef SQLITE_OMIT_AUTOVACUUM
u8 autoVacuum; /* True if auto-vacuum is enabled */
u8 incrVacuum; /* True if incr-vacuum is enabled */
#endif
u8 inTransaction; /* Transaction state */
u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
u8 inTransaction; /* Transaction state */
u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
u32 pageSize; /* Total number of bytes on a page */
u32 usableSize; /* Number of usable bytes on each page */
int nTransaction; /* Number of open transactions (read + write) */
@ -445,8 +446,8 @@ struct BtShared {
*/
typedef struct CellInfo CellInfo;
struct CellInfo {
u8 *pCell; /* Pointer to the start of cell content */
i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
u8 *pCell; /* Pointer to the start of cell content */
u32 nData; /* Number of bytes of data */
u32 nPayload; /* Total amount of payload */
u16 nHeader; /* Size of the cell content header in bytes */
@ -488,20 +489,20 @@ struct BtCursor {
Pgno pgnoRoot; /* The root page of this tree */
sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
CellInfo info; /* A parse of the cell we are pointing at */
i64 nKey; /* Size of pKey, or last integer key */
void *pKey; /* Saved key that was cursor's last known position */
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
u8 wrFlag; /* True if writable */
u8 atLast; /* Cursor pointing to the last entry */
u8 validNKey; /* True if info.nKey is valid */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
void *pKey; /* Saved key that was cursor's last known position */
i64 nKey; /* Size of pKey, or last integer key */
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
#ifndef SQLITE_OMIT_INCRBLOB
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
Pgno *aOverflow; /* Cache of overflow page locations */
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
#endif
i16 iPage; /* Index of current page in apPage */
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
};
/*

View File

@ -726,8 +726,9 @@ void sqlite3StartTable(
*/
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
if( iDb<0 ) return;
if( !OMIT_TEMPDB && isTemp && iDb>1 ){
/* If creating a temp table, the name may not be qualified */
if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
/* If creating a temp table, the name may not be qualified. Unless
** the database name is "temp" anyway. */
sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
return;
}
@ -775,17 +776,18 @@ void sqlite3StartTable(
** collisions.
*/
if( !IN_DECLARE_VTAB ){
char *zDb = db->aDb[iDb].zName;
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto begin_table_error;
}
pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
pTable = sqlite3FindTable(db, zName, zDb);
if( pTable ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "table %T already exists", pName);
}
goto begin_table_error;
}
if( sqlite3FindIndex(db, zName, 0)!=0 && (iDb==0 || !db->init.busy) ){
if( sqlite3FindIndex(db, zName, zDb)!=0 ){
sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
goto begin_table_error;
}
@ -802,6 +804,7 @@ void sqlite3StartTable(
pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
pTable->nRowEst = 1000000;
assert( pParse->pNewTable==0 );
pParse->pNewTable = pTable;
@ -1648,12 +1651,10 @@ void sqlite3CreateView(
}
sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
p = pParse->pNewTable;
if( p==0 ){
if( p==0 || pParse->nErr ){
sqlite3SelectDelete(db, pSelect);
return;
}
assert( pParse->nErr==0 ); /* If sqlite3StartTable return non-NULL then
** there could not have been an error */
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
iDb = sqlite3SchemaToIndex(db, p->pSchema);
if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
@ -2771,7 +2772,8 @@ Index *sqlite3CreateIndex(
sqlite3RefillIndex(pParse, pIndex, iMem);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0,
sqlite3MPrintf(db, "name='%q'", pIndex->zName), P4_DYNAMIC);
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName),
P4_DYNAMIC);
sqlite3VdbeAddOp1(v, OP_Expire, 0);
}
}
@ -2832,14 +2834,14 @@ exit_create_index:
void sqlite3DefaultRowEst(Index *pIdx){
unsigned *a = pIdx->aiRowEst;
int i;
unsigned n;
assert( a!=0 );
a[0] = 1000000;
for(i=pIdx->nColumn; i>=5; i--){
a[i] = 5;
}
while( i>=1 ){
a[i] = 11 - i;
i--;
a[0] = pIdx->pTable->nRowEst;
if( a[0]<10 ) a[0] = 10;
n = 10;
for(i=1; i<=pIdx->nColumn; i++){
a[i] = n;
if( n>5 ) n--;
}
if( pIdx->onError!=OE_None ){
a[pIdx->nColumn] = 1;
@ -2899,7 +2901,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
if( v ){
sqlite3BeginWriteOperation(pParse, 1, iDb);
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q",
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
pIndex->zName
);
@ -3391,7 +3393,7 @@ int sqlite3OpenTempDatabase(Parse *pParse){
SQLITE_OPEN_DELETEONCLOSE |
SQLITE_OPEN_TEMP_DB;
rc = sqlite3BtreeFactory(db, 0, 0, SQLITE_DEFAULT_CACHE_SIZE, flags, &pBt);
rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags);
if( rc!=SQLITE_OK ){
sqlite3ErrorMsg(pParse, "unable to open a temporary database "
"file for storing temporary tables");

View File

@ -358,7 +358,7 @@ FuncDef *sqlite3FindFunction(
** priority to built-in functions.
**
** Except, if createFlag is true, that means that we are trying to
** install a new function. Whatever FuncDef structure is returned will
** install a new function. Whatever FuncDef structure is returned it will
** have fields overwritten with new information appropriate for the
** new function. But the FuncDefs for built-in functions are read-only.
** So we must not search for built-ins when creating a new function.

View File

@ -174,6 +174,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_OMIT_AUTOMATIC_INDEX
"OMIT_AUTOMATIC_INDEX",
#endif
#ifdef SQLITE_OMIT_AUTORESET
"OMIT_AUTORESET",
#endif
#ifdef SQLITE_OMIT_AUTOVACUUM
"OMIT_AUTOVACUUM",
#endif

View File

@ -133,12 +133,6 @@ end_getDigits:
return cnt;
}
/*
** Read text from z[] and convert into a floating point number. Return
** the number of digits converted.
*/
#define getValue sqlite3AtoF
/*
** Parse a timezone extension on the end of a date-time.
** The extension is of the form:
@ -340,7 +334,7 @@ static int parseDateOrTime(
const char *zDate,
DateTime *p
){
int isRealNum; /* Return from sqlite3IsNumber(). Not used */
double r;
if( parseYyyyMmDd(zDate,p)==0 ){
return 0;
}else if( parseHhMmSs(zDate, p)==0 ){
@ -348,9 +342,7 @@ static int parseDateOrTime(
}else if( sqlite3StrICmp(zDate,"now")==0){
setDateTimeToCurrent(context, p);
return 0;
}else if( sqlite3IsNumber(zDate, &isRealNum, SQLITE_UTF8) ){
double r;
getValue(zDate, &r);
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
p->validJD = 1;
return 0;
@ -571,8 +563,9 @@ static int parseModifier(const char *zMod, DateTime *p){
** weekday N where 0==Sunday, 1==Monday, and so forth. If the
** date is already on the appropriate weekday, this is a no-op.
*/
if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0
&& (n=(int)r)==r && n>=0 && r<7 ){
if( strncmp(z, "weekday ", 8)==0
&& sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
&& (n=(int)r)==r && n>=0 && r<7 ){
sqlite3_int64 Z;
computeYMD_HMS(p);
p->validTZ = 0;
@ -627,8 +620,11 @@ static int parseModifier(const char *zMod, DateTime *p){
case '8':
case '9': {
double rRounder;
n = getValue(z, &r);
assert( n>=1 );
for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
rc = 1;
break;
}
if( z[n]==':' ){
/* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
** specified number of hours, minutes, seconds, and fractional seconds

View File

@ -484,6 +484,9 @@ Expr *sqlite3PExpr(
){
Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1);
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
if( p ) {
sqlite3ExprCheckHeight(pParse, p->nHeight);
}
return p;
}
@ -555,7 +558,7 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
i64 i;
int bOk = sqlite3Atoi64(&z[1], &i);
int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8);
pExpr->iColumn = (ynVar)i;
testcase( i==0 );
testcase( i==1 );
@ -1535,8 +1538,8 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
#endif
/*
** Generate code for scalar subqueries used as an expression
** and IN operators. Examples:
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
** or IN operators. Examples:
**
** (SELECT a FROM b) -- subquery
** EXISTS (SELECT a FROM b) -- EXISTS subquery
@ -1597,12 +1600,22 @@ int sqlite3CodeSubselect(
assert( testAddr>0 || pParse->db->mallocFailed );
}
#ifndef SQLITE_OMIT_EXPLAIN
if( pParse->explain==2 ){
char *zMsg = sqlite3MPrintf(
pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr?"":"CORRELATED ",
pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
);
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
}
#endif
switch( pExpr->op ){
case TK_IN: {
char affinity;
KeyInfo keyInfo;
int addr; /* Address of OP_OpenEphemeral instruction */
Expr *pLeft = pExpr->pLeft;
char affinity; /* Affinity of the LHS of the IN */
KeyInfo keyInfo; /* Keyinfo for the generated table */
int addr; /* Address of OP_OpenEphemeral instruction */
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
if( rMayHaveNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
@ -1625,6 +1638,7 @@ int sqlite3CodeSubselect(
*/
pExpr->iTable = pParse->nTab++;
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
memset(&keyInfo, 0, sizeof(keyInfo));
keyInfo.nField = 1;
@ -1641,6 +1655,7 @@ int sqlite3CodeSubselect(
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
dest.affinity = (u8)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
pExpr->x.pSelect->iLimit = 0;
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
return 0;
}
@ -1741,6 +1756,7 @@ int sqlite3CodeSubselect(
sqlite3ExprDelete(pParse->db, pSel->pLimit);
pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
&sqlite3IntTokens[1]);
pSel->iLimit = 0;
if( sqlite3Select(pParse, pSel, &dest) ){
return 0;
}
@ -1917,7 +1933,7 @@ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
if( ALWAYS(z!=0) ){
double value;
char *zV;
sqlite3AtoF(z, &value);
sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */
if( negateFlag ) value = -value;
zV = dup8bytes(v, (char*)&value);
@ -1931,9 +1947,7 @@ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
** Generate an instruction that will put the integer describe by
** text z[0..n-1] into register iMem.
**
** The z[] string will probably not be zero-terminated. But the
** z[n] character is guaranteed to be something that does not look
** like the continuation of the number.
** Expr.u.zToken is always UTF8 and zero-terminated.
*/
static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
Vdbe *v = pParse->pVdbe;
@ -1942,13 +1956,14 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
if( negFlag ) i = -i;
sqlite3VdbeAddOp2(v, OP_Integer, i, iMem);
}else{
int c;
i64 value;
const char *z = pExpr->u.zToken;
assert( z!=0 );
if( sqlite3FitsIn64Bits(z, negFlag) ){
i64 value;
c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
if( c==0 || (c==2 && negFlag) ){
char *zV;
sqlite3Atoi64(z, &value);
if( negFlag ) value = -value;
if( negFlag ){ value = -value; }
zV = dup8bytes(v, (char*)&value);
sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
}else{
@ -2233,73 +2248,6 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
}
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */
/*
** If the last instruction coded is an ephemeral copy of any of
** the registers in the nReg registers beginning with iReg, then
** convert the last instruction from OP_SCopy to OP_Copy.
*/
void sqlite3ExprHardCopy(Parse *pParse, int iReg, int nReg){
VdbeOp *pOp;
Vdbe *v;
assert( pParse->db->mallocFailed==0 );
v = pParse->pVdbe;
assert( v!=0 );
pOp = sqlite3VdbeGetOp(v, -1);
assert( pOp!=0 );
if( pOp->opcode==OP_SCopy && pOp->p1>=iReg && pOp->p1<iReg+nReg ){
pOp->opcode = OP_Copy;
}
}
/*
** Generate code to store the value of the iAlias-th alias in register
** target. The first time this is called, pExpr is evaluated to compute
** the value of the alias. The value is stored in an auxiliary register
** and the number of that register is returned. On subsequent calls,
** the register number is returned without generating any code.
**
** Note that in order for this to work, code must be generated in the
** same order that it is executed.
**
** Aliases are numbered starting with 1. So iAlias is in the range
** of 1 to pParse->nAlias inclusive.
**
** pParse->aAlias[iAlias-1] records the register number where the value
** of the iAlias-th alias is stored. If zero, that means that the
** alias has not yet been computed.
*/
static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){
#if 0
sqlite3 *db = pParse->db;
int iReg;
if( pParse->nAliasAlloc<pParse->nAlias ){
pParse->aAlias = sqlite3DbReallocOrFree(db, pParse->aAlias,
sizeof(pParse->aAlias[0])*pParse->nAlias );
testcase( db->mallocFailed && pParse->nAliasAlloc>0 );
if( db->mallocFailed ) return 0;
memset(&pParse->aAlias[pParse->nAliasAlloc], 0,
(pParse->nAlias-pParse->nAliasAlloc)*sizeof(pParse->aAlias[0]));
pParse->nAliasAlloc = pParse->nAlias;
}
assert( iAlias>0 && iAlias<=pParse->nAlias );
iReg = pParse->aAlias[iAlias-1];
if( iReg==0 ){
if( pParse->iCacheLevel>0 ){
iReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
}else{
iReg = ++pParse->nMem;
sqlite3ExprCode(pParse, pExpr, iReg);
pParse->aAlias[iAlias-1] = iReg;
}
}
return iReg;
#else
UNUSED_PARAMETER(iAlias);
return sqlite3ExprCodeTarget(pParse, pExpr, target);
#endif
}
/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
@ -2408,7 +2356,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
break;
}
case TK_AS: {
inReg = codeAlias(pParse, pExpr->iTable, pExpr->pLeft, target);
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
break;
}
#ifndef SQLITE_OMIT_CAST
@ -2840,6 +2788,11 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
opCompare.op = TK_EQ;
opCompare.pLeft = &cacheX;
pTest = &opCompare;
/* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
** The value in regFree1 might get SCopy-ed into the file result.
** So make sure that the regFree1 register is not reused for other
** purposes and possibly overwritten. */
regFree1 = 0;
}
for(i=0; i<nExpr; i=i+2){
sqlite3ExprCachePush(pParse);
@ -2933,10 +2886,14 @@ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
int inReg;
assert( target>0 && target<=pParse->nMem );
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
assert( pParse->pVdbe || pParse->db->mallocFailed );
if( inReg!=target && pParse->pVdbe ){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
if( pExpr && pExpr->op==TK_REGISTER ){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target);
}else{
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
assert( pParse->pVdbe || pParse->db->mallocFailed );
if( inReg!=target && pParse->pVdbe ){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
}
}
return target;
}
@ -3083,9 +3040,22 @@ static int evalConstExpr(Walker *pWalker, Expr *pExpr){
** Preevaluate constant subexpressions within pExpr and store the
** results in registers. Modify pExpr so that the constant subexpresions
** are TK_REGISTER opcodes that refer to the precomputed values.
**
** This routine is a no-op if the jump to the cookie-check code has
** already occur. Since the cookie-check jump is generated prior to
** any other serious processing, this check ensures that there is no
** way to accidently bypass the constant initializations.
**
** This routine is also a no-op if the SQLITE_FactorOutConst optimization
** is disabled via the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)
** interface. This allows test logic to verify that the same answer is
** obtained for queries regardless of whether or not constants are
** precomputed into registers or if they are inserted in-line.
*/
void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
Walker w;
if( pParse->cookieGoto ) return;
if( (pParse->db->flags & SQLITE_FactorOutConst)!=0 ) return;
w.xExprCallback = evalConstExpr;
w.xSelectCallback = 0;
w.pParse = pParse;
@ -3109,19 +3079,14 @@ int sqlite3ExprCodeExprList(
int i, n;
assert( pList!=0 );
assert( target>0 );
assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */
n = pList->nExpr;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
if( pItem->iAlias ){
int iReg = codeAlias(pParse, pItem->iAlias, pItem->pExpr, target+i);
Vdbe *v = sqlite3GetVdbe(pParse);
if( iReg!=target+i ){
sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target+i);
}
}else{
sqlite3ExprCode(pParse, pItem->pExpr, target+i);
}
if( doHardCopy && !pParse->db->mallocFailed ){
sqlite3ExprHardCopy(pParse, target, n);
Expr *pExpr = pItem->pExpr;
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
if( inReg!=target+i ){
sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy,
inReg, target+i);
}
}
return n;

View File

@ -380,7 +380,7 @@ static void fkLookupParent(
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[i]+1+regData, regTemp+i);
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
}
/* If the parent table is the same as the child table, and we are about

View File

@ -285,7 +285,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
sqlite3_result_error_nomem(context);
return;
}
sqlite3AtoF(zBuf, &r);
sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8);
sqlite3_free(zBuf);
}
sqlite3_result_double(context, r);
@ -1450,10 +1450,10 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
}else{
pInfo = (struct compareInfo*)&likeInfoNorm;
}
sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0);
sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0);
sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY,
(struct compareInfo*)&globInfo, likeFunc, 0,0);
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0);
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
(struct compareInfo*)&globInfo, likeFunc, 0, 0, 0);
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
setLikeOptFlag(db, "like",
caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
@ -1537,10 +1537,10 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(coalesce, 1, 0, 0, 0 ),
FUNCTION(coalesce, 0, 0, 0, 0 ),
/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */
{-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0},
{-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0},
FUNCTION(hex, 1, 0, 0, hexFunc ),
/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */
{2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0},
{2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0},
FUNCTION(random, 0, 0, 0, randomFunc ),
FUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
@ -1567,7 +1567,7 @@ void sqlite3RegisterGlobalFunctions(void){
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
/* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
{0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0},
{0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),

View File

@ -327,6 +327,46 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_next_stmt,
sqlite3_sql,
sqlite3_status,
/*
** Added for 3.7.4
*/
sqlite3_backup_finish,
sqlite3_backup_init,
sqlite3_backup_pagecount,
sqlite3_backup_remaining,
sqlite3_backup_step,
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
sqlite3_compileoption_get,
sqlite3_compileoption_used,
#else
0,
0,
#endif
sqlite3_create_function_v2,
sqlite3_db_config,
sqlite3_db_mutex,
sqlite3_db_status,
sqlite3_extended_errcode,
sqlite3_log,
sqlite3_soft_heap_limit64,
sqlite3_sourceid,
sqlite3_stmt_status,
sqlite3_strnicmp,
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
sqlite3_unlock_notify,
#else
0,
#endif
#ifndef SQLITE_OMIT_WAL
sqlite3_wal_autocheckpoint,
sqlite3_wal_checkpoint,
sqlite3_wal_hook,
#else
0,
0,
0,
#endif
};
/*

View File

@ -26,15 +26,33 @@
# include "sqliteicu.h"
#endif
/*
** The version of the library
*/
#ifndef SQLITE_AMALGAMATION
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
** contains the text of SQLITE_VERSION macro.
*/
const char sqlite3_version[] = SQLITE_VERSION;
#endif
/* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns
** a pointer to the to the sqlite3_version[] string constant.
*/
const char *sqlite3_libversion(void){ return sqlite3_version; }
/* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a
** pointer to a string constant whose value is the same as the
** SQLITE_SOURCE_ID C preprocessor macro.
*/
const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function
** returns an integer equal to SQLITE_VERSION_NUMBER.
*/
int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; }
/* IMPLEMENTATION-OF: R-54823-41343 The sqlite3_threadsafe() function returns
** zero if and only if SQLite was compiled mutexing code omitted due to
** the SQLITE_THREADSAFE compile-time option being set to 0.
*/
int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE)
@ -155,6 +173,13 @@ int sqlite3_initialize(void){
** sqlite3_initialize(). The recursive calls normally come through
** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other
** recursive calls might also be possible.
**
** IMPLEMENTATION-OF: R-00140-37445 SQLite automatically serializes calls
** to the xInit method, so the xInit method need not be threadsafe.
**
** The following mutex is what serializes access to the appdef pcache xInit
** methods. The sqlite3_pcache_methods.xInit() all is embedded in the
** call to sqlite3PcacheInitialize().
*/
sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex);
if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){
@ -435,12 +460,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
sz = 0;
pStart = 0;
}else if( pBuf==0 ){
sz = ROUND8(sz);
sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
sqlite3BeginBenignMalloc();
pStart = sqlite3Malloc( sz*cnt );
pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */
sqlite3EndBenignMalloc();
}else{
sz = ROUNDDOWN8(sz);
sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
pStart = pBuf;
}
db->lookaside.pStart = pStart;
@ -483,14 +508,14 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
va_start(ap, op);
switch( op ){
case SQLITE_DBCONFIG_LOOKASIDE: {
void *pBuf = va_arg(ap, void*);
int sz = va_arg(ap, int);
int cnt = va_arg(ap, int);
void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */
rc = setupLookaside(db, pBuf, sz, cnt);
break;
}
default: {
rc = SQLITE_ERROR;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
break;
}
}
@ -595,11 +620,28 @@ void sqlite3CloseSavepoints(sqlite3 *db){
db->isTransactionSavepoint = 0;
}
/*
** Invoke the destructor function associated with FuncDef p, if any. Except,
** if this is not the last copy of the function, do not invoke it. Multiple
** copies of a single function are created when create_function() is called
** with SQLITE_ANY as the encoding.
*/
static void functionDestroy(sqlite3 *db, FuncDef *p){
FuncDestructor *pDestructor = p->pDestructor;
if( pDestructor ){
pDestructor->nRef--;
if( pDestructor->nRef==0 ){
pDestructor->xDestroy(pDestructor->pUserData);
sqlite3DbFree(db, pDestructor);
}
}
}
/*
** Close an existing SQLite database
*/
int sqlite3_close(sqlite3 *db){
HashElem *i;
HashElem *i; /* Hash table iterator */
int j;
if( !db ){
@ -667,6 +709,7 @@ int sqlite3_close(sqlite3 *db){
for(p=db->aFunc.a[j]; p; p=pHash){
pHash = p->pHash;
while( p ){
functionDestroy(db, p);
pNext = p->pNext;
sqlite3DbFree(db, p);
p = pNext;
@ -773,7 +816,7 @@ const char *sqlite3ErrStr(int rc){
/* SQLITE_INTERRUPT */ "interrupted",
/* SQLITE_IOERR */ "disk I/O error",
/* SQLITE_CORRUPT */ "database disk image is malformed",
/* SQLITE_NOTFOUND */ 0,
/* SQLITE_NOTFOUND */ "unknown operation",
/* SQLITE_FULL */ "database or disk is full",
/* SQLITE_CANTOPEN */ "unable to open database file",
/* SQLITE_PROTOCOL */ "locking protocol",
@ -941,7 +984,8 @@ int sqlite3CreateFunc(
void *pUserData,
void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*)
void (*xFinal)(sqlite3_context*),
FuncDestructor *pDestructor
){
FuncDef *p;
int nName;
@ -969,10 +1013,10 @@ int sqlite3CreateFunc(
}else if( enc==SQLITE_ANY ){
int rc;
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8,
pUserData, xFunc, xStep, xFinal);
pUserData, xFunc, xStep, xFinal, pDestructor);
if( rc==SQLITE_OK ){
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
pUserData, xFunc, xStep, xFinal);
pUserData, xFunc, xStep, xFinal, pDestructor);
}
if( rc!=SQLITE_OK ){
return rc;
@ -1005,6 +1049,15 @@ int sqlite3CreateFunc(
if( !p ){
return SQLITE_NOMEM;
}
/* If an older version of the function with a configured destructor is
** being replaced invoke the destructor function here. */
functionDestroy(db, p);
if( pDestructor ){
pDestructor->nRef++;
}
p->pDestructor = pDestructor;
p->flags = 0;
p->xFunc = xFunc;
p->xStep = xStep;
@ -1019,7 +1072,7 @@ int sqlite3CreateFunc(
*/
int sqlite3_create_function(
sqlite3 *db,
const char *zFunctionName,
const char *zFunc,
int nArg,
int enc,
void *p,
@ -1027,9 +1080,41 @@ int sqlite3_create_function(
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*)
){
int rc;
return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep,
xFinal, 0);
}
int sqlite3_create_function_v2(
sqlite3 *db,
const char *zFunc,
int nArg,
int enc,
void *p,
void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*),
void (*xDestroy)(void *)
){
int rc = SQLITE_ERROR;
FuncDestructor *pArg = 0;
sqlite3_mutex_enter(db->mutex);
rc = sqlite3CreateFunc(db, zFunctionName, nArg, enc, p, xFunc, xStep, xFinal);
if( xDestroy ){
pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
if( !pArg ){
xDestroy(p);
goto out;
}
pArg->xDestroy = xDestroy;
pArg->pUserData = p;
}
rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg);
if( pArg && pArg->nRef==0 ){
assert( rc!=SQLITE_OK );
xDestroy(p);
sqlite3DbFree(db, pArg);
}
out:
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
@ -1051,7 +1136,7 @@ int sqlite3_create_function16(
sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed );
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal);
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0);
sqlite3DbFree(db, zFunc8);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
@ -1082,7 +1167,7 @@ int sqlite3_overload_function(
sqlite3_mutex_enter(db->mutex);
if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
0, sqlite3InvalidFunction, 0, 0);
0, sqlite3InvalidFunction, 0, 0, 0);
}
rc = sqlite3ApiExit(db, SQLITE_OK);
sqlite3_mutex_leave(db->mutex);
@ -1220,7 +1305,10 @@ int sqlite3WalDefaultHook(
** configured by this function.
*/
int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){
#ifndef SQLITE_OMIT_WAL
#ifdef SQLITE_OMIT_WAL
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(nFrame);
#else
if( nFrame>0 ){
sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame));
}else{
@ -1350,60 +1438,6 @@ int sqlite3TempInMemory(const sqlite3 *db){
#endif
}
/*
** This routine is called to create a connection to a database BTree
** driver. If zFilename is the name of a file, then that file is
** opened and used. If zFilename is the magic name ":memory:" then
** the database is stored in memory (and is thus forgotten as soon as
** the connection is closed.) If zFilename is NULL then the database
** is a "virtual" database for transient use only and is deleted as
** soon as the connection is closed.
**
** A virtual database can be either a disk file (that is automatically
** deleted when the file is closed) or it an be held entirely in memory.
** The sqlite3TempInMemory() function is used to determine which.
*/
int sqlite3BtreeFactory(
sqlite3 *db, /* Main database when opening aux otherwise 0 */
const char *zFilename, /* Name of the file containing the BTree database */
int omitJournal, /* if TRUE then do not journal this file */
int nCache, /* How many pages in the page cache */
int vfsFlags, /* Flags passed through to vfsOpen */
Btree **ppBtree /* Pointer to new Btree object written here */
){
int btFlags = 0;
int rc;
assert( sqlite3_mutex_held(db->mutex) );
assert( ppBtree != 0);
if( omitJournal ){
btFlags |= BTREE_OMIT_JOURNAL;
}
if( db->flags & SQLITE_NoReadlock ){
btFlags |= BTREE_NO_READLOCK;
}
#ifndef SQLITE_OMIT_MEMORYDB
if( zFilename==0 && sqlite3TempInMemory(db) ){
zFilename = ":memory:";
}
#endif
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (zFilename==0 || *zFilename==0) ){
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
}
rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btFlags, vfsFlags);
/* If the B-Tree was successfully opened, set the pager-cache size to the
** default value. Except, if the call to BtreeOpen() returned a handle
** open on an existing shared pager-cache, do not change the pager-cache
** size.
*/
if( rc==SQLITE_OK && 0==sqlite3BtreeSchema(*ppBtree, 0, 0) ){
sqlite3BtreeSetCacheSize(*ppBtree, nCache);
}
return rc;
}
/*
** Return UTF-8 encoded English language explanation of the most recent
** error.
@ -1568,13 +1602,12 @@ static int createCollation(
}
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1);
if( pColl ){
pColl->xCmp = xCompare;
pColl->pUser = pCtx;
pColl->xDel = xDel;
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
pColl->type = collType;
}
if( pColl==0 ) return SQLITE_NOMEM;
pColl->xCmp = xCompare;
pColl->pUser = pCtx;
pColl->xDel = xDel;
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
pColl->type = collType;
sqlite3Error(db, SQLITE_OK, 0);
return SQLITE_OK;
}
@ -1646,17 +1679,39 @@ static const int aHardLimit[] = {
*/
int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
int oldLimit;
/* 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
** macro called SQLITE_MAX_NAME. (The "_LIMIT_" in the name is changed to
** "_MAX_".)
*/
assert( aHardLimit[SQLITE_LIMIT_LENGTH]==SQLITE_MAX_LENGTH );
assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH );
assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN );
assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH );
assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT);
assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP );
assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG );
assert( aHardLimit[SQLITE_LIMIT_ATTACHED]==SQLITE_MAX_ATTACHED );
assert( aHardLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]==
SQLITE_MAX_LIKE_PATTERN_LENGTH );
assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER);
assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH );
assert( SQLITE_LIMIT_TRIGGER_DEPTH==(SQLITE_N_LIMIT-1) );
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
return -1;
}
oldLimit = db->aLimit[limitId];
if( newLimit>=0 ){
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId];
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
}
db->aLimit[limitId] = newLimit;
}
return oldLimit;
return oldLimit; /* IMP: R-53341-35419 */
}
/*
@ -1680,6 +1735,24 @@ static int openDatabase(
if( rc ) return rc;
#endif
/* Only allow sensible combinations of bits in the flags argument.
** Throw an error if any non-sense combination is used. If we
** do not block illegal combinations here, it could trigger
** assert() statements in deeper layers. Sensible combinations
** are:
**
** 1: SQLITE_OPEN_READONLY
** 2: SQLITE_OPEN_READWRITE
** 6: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
*/
assert( SQLITE_OPEN_READONLY == 0x01 );
assert( SQLITE_OPEN_READWRITE == 0x02 );
assert( SQLITE_OPEN_CREATE == 0x04 );
testcase( (1<<(flags&7))==0x02 ); /* READONLY */
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE;
if( sqlite3GlobalConfig.bCoreMutex==0 ){
isThreadsafe = 0;
}else if( flags & SQLITE_OPEN_NOMUTEX ){
@ -1713,7 +1786,8 @@ static int openDatabase(
SQLITE_OPEN_SUBJOURNAL |
SQLITE_OPEN_MASTER_JOURNAL |
SQLITE_OPEN_NOMUTEX |
SQLITE_OPEN_FULLMUTEX
SQLITE_OPEN_FULLMUTEX |
SQLITE_OPEN_WAL
);
/* Allocate the sqlite data structure */
@ -1747,6 +1821,9 @@ static int openDatabase(
#endif
#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS
| SQLITE_RecTriggers
#endif
#if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS
| SQLITE_ForeignKeys
#endif
;
sqlite3HashInit(&db->aCollSeq);
@ -1785,9 +1862,8 @@ static int openDatabase(
/* Open the backend database driver */
db->openFlags = flags;
rc = sqlite3BtreeFactory(db, zFilename, 0, SQLITE_DEFAULT_CACHE_SIZE,
flags | SQLITE_OPEN_MAIN_DB,
&db->aDb[0].pBt);
rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0,
flags | SQLITE_OPEN_MAIN_DB);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_IOERR_NOMEM ){
rc = SQLITE_NOMEM;
@ -2283,8 +2359,13 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
assert( pPager!=0 );
fd = sqlite3PagerFile(pPager);
assert( fd!=0 );
if( fd->pMethods ){
if( op==SQLITE_FCNTL_FILE_POINTER ){
*(sqlite3_file**)pArg = fd;
rc = SQLITE_OK;
}else if( fd->pMethods ){
rc = sqlite3OsFileControl(fd, op, pArg);
}else{
rc = SQLITE_NOTFOUND;
}
sqlite3BtreeLeave(pBtree);
}
@ -2494,6 +2575,22 @@ int sqlite3_test_control(int op, ...){
break;
}
/* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree);
**
** Pass pFree into sqlite3ScratchFree().
** If sz>0 then allocate a scratch buffer into pNew.
*/
case SQLITE_TESTCTRL_SCRATCHMALLOC: {
void *pFree, **ppNew;
int sz;
sz = va_arg(ap, int);
ppNew = va_arg(ap, void**);
pFree = va_arg(ap, void*);
if( sz ) *ppNew = sqlite3ScratchMalloc(sz);
sqlite3ScratchFree(pFree);
break;
}
}
va_end(ap);
#endif /* SQLITE_OMIT_BUILTIN_TEST */

View File

@ -15,6 +15,66 @@
#include "sqliteInt.h"
#include <stdarg.h>
/*
** Attempt to release up to n bytes of non-essential memory currently
** held by SQLite. An example of non-essential memory is memory used to
** cache database pages that are not currently in use.
*/
int sqlite3_release_memory(int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
return sqlite3PcacheReleaseMemory(n);
#else
/* IMPLEMENTATION-OF: R-34391-24921 The sqlite3_release_memory() routine
** is a no-op returning zero if SQLite is not compiled with
** SQLITE_ENABLE_MEMORY_MANAGEMENT. */
UNUSED_PARAMETER(n);
return 0;
#endif
}
/*
** An instance of the following object records the location of
** each unused scratch buffer.
*/
typedef struct ScratchFreeslot {
struct ScratchFreeslot *pNext; /* Next unused scratch buffer */
} ScratchFreeslot;
/*
** State information local to the memory allocation subsystem.
*/
static SQLITE_WSD struct Mem0Global {
sqlite3_mutex *mutex; /* Mutex to serialize access */
/*
** The alarm callback and its arguments. The mem0.mutex lock will
** be held while the callback is running. Recursive calls into
** the memory subsystem are allowed, but no new callbacks will be
** issued.
*/
sqlite3_int64 alarmThreshold;
void (*alarmCallback)(void*, sqlite3_int64,int);
void *alarmArg;
/*
** Pointers to the end of sqlite3GlobalConfig.pScratch memory
** (so that a range test can be used to determine if an allocation
** being freed came from pScratch) and a pointer to the list of
** unused scratch allocations.
*/
void *pScratchEnd;
ScratchFreeslot *pScratchFree;
u32 nScratchFree;
/*
** True if heap is nearly "full" where "full" is defined by the
** sqlite3_soft_heap_limit() setting.
*/
int nearlyFull;
} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 };
#define mem0 GLOBAL(struct Mem0Global, mem0)
/*
** This routine runs when the memory allocator sees that the
** total memory allocation is about to exceed the soft heap
@ -29,79 +89,67 @@ static void softHeapLimitEnforcer(
sqlite3_release_memory(allocSize);
}
/*
** Change the alarm callback
*/
static int sqlite3MemoryAlarm(
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
void *pArg,
sqlite3_int64 iThreshold
){
int nUsed;
sqlite3_mutex_enter(mem0.mutex);
mem0.alarmCallback = xCallback;
mem0.alarmArg = pArg;
mem0.alarmThreshold = iThreshold;
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed);
sqlite3_mutex_leave(mem0.mutex);
return SQLITE_OK;
}
#ifndef SQLITE_OMIT_DEPRECATED
/*
** Deprecated external interface. Internal/core SQLite code
** should call sqlite3MemoryAlarm.
*/
int sqlite3_memory_alarm(
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
void *pArg,
sqlite3_int64 iThreshold
){
return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
}
#endif
/*
** Set the soft heap-size limit for the library. Passing a zero or
** negative value indicates no limit.
*/
void sqlite3_soft_heap_limit(int n){
sqlite3_uint64 iLimit;
int overage;
if( n<0 ){
iLimit = 0;
}else{
iLimit = n;
}
sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
sqlite3_int64 priorLimit;
sqlite3_int64 excess;
#ifndef SQLITE_OMIT_AUTOINIT
sqlite3_initialize();
#endif
if( iLimit>0 ){
sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, iLimit);
sqlite3_mutex_enter(mem0.mutex);
priorLimit = mem0.alarmThreshold;
sqlite3_mutex_leave(mem0.mutex);
if( n<0 ) return priorLimit;
if( n>0 ){
sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
}else{
sqlite3MemoryAlarm(0, 0, 0);
}
overage = (int)(sqlite3_memory_used() - (i64)n);
if( overage>0 ){
sqlite3_release_memory(overage);
}
excess = sqlite3_memory_used() - n;
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
return priorLimit;
}
/*
** Attempt to release up to n bytes of non-essential memory currently
** held by SQLite. An example of non-essential memory is memory used to
** cache database pages that are not currently in use.
*/
int sqlite3_release_memory(int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int nRet = 0;
nRet += sqlite3PcacheReleaseMemory(n-nRet);
return nRet;
#else
UNUSED_PARAMETER(n);
return SQLITE_OK;
#endif
void sqlite3_soft_heap_limit(int n){
if( n<0 ) n = 0;
sqlite3_soft_heap_limit64(n);
}
/*
** State information local to the memory allocation subsystem.
*/
static SQLITE_WSD struct Mem0Global {
/* Number of free pages for scratch and page-cache memory */
u32 nScratchFree;
u32 nPageFree;
sqlite3_mutex *mutex; /* Mutex to serialize access */
/*
** The alarm callback and its arguments. The mem0.mutex lock will
** be held while the callback is running. Recursive calls into
** the memory subsystem are allowed, but no new callbacks will be
** issued.
*/
sqlite3_int64 alarmThreshold;
void (*alarmCallback)(void*, sqlite3_int64,int);
void *alarmArg;
/*
** Pointers to the end of sqlite3GlobalConfig.pScratch and
** sqlite3GlobalConfig.pPage to a block of memory that records
** which pages are available.
*/
u32 *aScratchFree;
u32 *aPageFree;
} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 };
#define mem0 GLOBAL(struct Mem0Global, mem0)
/*
** Initialize the memory allocation subsystem.
*/
@ -114,36 +162,45 @@ int sqlite3MallocInit(void){
mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
}
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
&& sqlite3GlobalConfig.nScratch>=0 ){
int i;
sqlite3GlobalConfig.szScratch = ROUNDDOWN8(sqlite3GlobalConfig.szScratch-4);
mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch)
[sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch];
for(i=0; i<sqlite3GlobalConfig.nScratch; i++){ mem0.aScratchFree[i] = i; }
mem0.nScratchFree = sqlite3GlobalConfig.nScratch;
&& sqlite3GlobalConfig.nScratch>0 ){
int i, n, sz;
ScratchFreeslot *pSlot;
sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch);
sqlite3GlobalConfig.szScratch = sz;
pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch;
n = sqlite3GlobalConfig.nScratch;
mem0.pScratchFree = pSlot;
mem0.nScratchFree = n;
for(i=0; i<n-1; i++){
pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot);
pSlot = pSlot->pNext;
}
pSlot->pNext = 0;
mem0.pScratchEnd = (void*)&pSlot[1];
}else{
mem0.pScratchEnd = 0;
sqlite3GlobalConfig.pScratch = 0;
sqlite3GlobalConfig.szScratch = 0;
sqlite3GlobalConfig.nScratch = 0;
}
if( sqlite3GlobalConfig.pPage && sqlite3GlobalConfig.szPage>=512
&& sqlite3GlobalConfig.nPage>=1 ){
int i;
int overhead;
int sz = ROUNDDOWN8(sqlite3GlobalConfig.szPage);
int n = sqlite3GlobalConfig.nPage;
overhead = (4*n + sz - 1)/sz;
sqlite3GlobalConfig.nPage -= overhead;
mem0.aPageFree = (u32*)&((char*)sqlite3GlobalConfig.pPage)
[sqlite3GlobalConfig.szPage*sqlite3GlobalConfig.nPage];
for(i=0; i<sqlite3GlobalConfig.nPage; i++){ mem0.aPageFree[i] = i; }
mem0.nPageFree = sqlite3GlobalConfig.nPage;
}else{
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
|| sqlite3GlobalConfig.nPage<1 ){
sqlite3GlobalConfig.pPage = 0;
sqlite3GlobalConfig.szPage = 0;
sqlite3GlobalConfig.nPage = 0;
}
return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
}
/*
** Return true if the heap is currently under memory pressure - in other
** words if the amount of heap used is close to the limit set by
** sqlite3_soft_heap_limit().
*/
int sqlite3HeapNearlyFull(void){
return mem0.nearlyFull;
}
/*
** Deinitialize the memory allocation subsystem.
*/
@ -178,36 +235,6 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
return res;
}
/*
** Change the alarm callback
*/
int sqlite3MemoryAlarm(
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
void *pArg,
sqlite3_int64 iThreshold
){
sqlite3_mutex_enter(mem0.mutex);
mem0.alarmCallback = xCallback;
mem0.alarmArg = pArg;
mem0.alarmThreshold = iThreshold;
sqlite3_mutex_leave(mem0.mutex);
return SQLITE_OK;
}
#ifndef SQLITE_OMIT_DEPRECATED
/*
** Deprecated external interface. Internal/core SQLite code
** should call sqlite3MemoryAlarm.
*/
int sqlite3_memory_alarm(
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
void *pArg,
sqlite3_int64 iThreshold
){
return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
}
#endif
/*
** Trigger the alarm
*/
@ -240,14 +267,19 @@ static int mallocWithAlarm(int n, void **pp){
if( mem0.alarmCallback!=0 ){
int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed+nFull >= mem0.alarmThreshold ){
mem0.nearlyFull = 1;
sqlite3MallocAlarm(nFull);
}else{
mem0.nearlyFull = 0;
}
}
p = sqlite3GlobalConfig.m.xMalloc(nFull);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
if( p==0 && mem0.alarmCallback ){
sqlite3MallocAlarm(nFull);
p = sqlite3GlobalConfig.m.xMalloc(nFull);
}
#endif
if( p ){
nFull = sqlite3MallocSize(p);
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);
@ -263,7 +295,9 @@ static int mallocWithAlarm(int n, void **pp){
*/
void *sqlite3Malloc(int n){
void *p;
if( n<=0 || n>=0x7fffff00 ){
if( n<=0 /* IMP: R-65312-04917 */
|| n>=0x7fffff00
){
/* A memory allocation of a number of bytes which is near the maximum
** signed integer value might cause an integer overflow inside of the
** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
@ -277,6 +311,7 @@ void *sqlite3Malloc(int n){
}else{
p = sqlite3GlobalConfig.m.xMalloc(n);
}
assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */
return p;
}
@ -315,59 +350,65 @@ void *sqlite3ScratchMalloc(int n){
void *p;
assert( n>0 );
sqlite3_mutex_enter(mem0.mutex);
if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
p = mem0.pScratchFree;
mem0.pScratchFree = mem0.pScratchFree->pNext;
mem0.nScratchFree--;
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
sqlite3_mutex_leave(mem0.mutex);
}else{
if( sqlite3GlobalConfig.bMemstat ){
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
n = mallocWithAlarm(n, &p);
if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
sqlite3_mutex_leave(mem0.mutex);
}else{
sqlite3_mutex_leave(mem0.mutex);
p = sqlite3GlobalConfig.m.xMalloc(n);
}
sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
}
assert( sqlite3_mutex_notheld(mem0.mutex) );
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
/* Verify that no more than two scratch allocation per thread
** is outstanding at one time. (This is only checked in the
/* 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 );
#endif
if( sqlite3GlobalConfig.szScratch<n ){
goto scratch_overflow;
}else{
sqlite3_mutex_enter(mem0.mutex);
if( mem0.nScratchFree==0 ){
sqlite3_mutex_leave(mem0.mutex);
goto scratch_overflow;
}else{
int i;
i = mem0.aScratchFree[--mem0.nScratchFree];
i *= sqlite3GlobalConfig.szScratch;
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
sqlite3_mutex_leave(mem0.mutex);
p = (void*)&((char*)sqlite3GlobalConfig.pScratch)[i];
assert( (((u8*)p - (u8*)0) & 7)==0 );
}
}
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
scratchAllocOut = p!=0;
if( p ) scratchAllocOut++;
#endif
return p;
scratch_overflow:
if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
n = mallocWithAlarm(n, &p);
if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
sqlite3_mutex_leave(mem0.mutex);
}else{
p = sqlite3GlobalConfig.m.xMalloc(n);
}
sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
scratchAllocOut = p!=0;
#endif
return p;
}
void sqlite3ScratchFree(void *p){
if( p ){
if( sqlite3GlobalConfig.pScratch==0
|| p<sqlite3GlobalConfig.pScratch
|| p>=(void*)mem0.aScratchFree ){
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
/* Verify that no more than two scratch allocation per thread
** is 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 && scratchAllocOut<=2 );
scratchAllocOut--;
#endif
if( p>=sqlite3GlobalConfig.pScratch && p<mem0.pScratchEnd ){
/* Release memory from the SQLITE_CONFIG_SCRATCH allocation */
ScratchFreeslot *pSlot;
pSlot = (ScratchFreeslot*)p;
sqlite3_mutex_enter(mem0.mutex);
pSlot->pNext = mem0.pScratchFree;
mem0.pScratchFree = pSlot;
mem0.nScratchFree++;
assert( mem0.nScratchFree<=sqlite3GlobalConfig.nScratch );
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
sqlite3_mutex_leave(mem0.mutex);
}else{
/* Release memory back to the heap */
assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
@ -382,26 +423,6 @@ void sqlite3ScratchFree(void *p){
}else{
sqlite3GlobalConfig.m.xFree(p);
}
}else{
int i;
i = (int)((u8*)p - (u8*)sqlite3GlobalConfig.pScratch);
i /= sqlite3GlobalConfig.szScratch;
assert( i>=0 && i<sqlite3GlobalConfig.nScratch );
sqlite3_mutex_enter(mem0.mutex);
assert( mem0.nScratchFree<(u32)sqlite3GlobalConfig.nScratch );
mem0.aScratchFree[mem0.nScratchFree++] = i;
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
sqlite3_mutex_leave(mem0.mutex);
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
/* Verify that no more than two scratch allocation per thread
** is 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 && scratchAllocOut<=2 );
scratchAllocOut = 0;
#endif
}
}
}
@ -442,7 +463,7 @@ int sqlite3DbMallocSize(sqlite3 *db, void *p){
** Free memory previously obtained from sqlite3Malloc().
*/
void sqlite3_free(void *p){
if( p==0 ) return;
if( p==0 ) return; /* IMP: R-49053-54554 */
assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
if( sqlite3GlobalConfig.bMemstat ){
@ -489,10 +510,10 @@ void *sqlite3Realloc(void *pOld, int nBytes){
int nOld, nNew;
void *pNew;
if( pOld==0 ){
return sqlite3Malloc(nBytes);
return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
}
if( nBytes<=0 ){
sqlite3_free(pOld);
sqlite3_free(pOld); /* IMP: R-31593-10574 */
return 0;
}
if( nBytes>=0x7fffff00 ){
@ -500,6 +521,9 @@ void *sqlite3Realloc(void *pOld, int nBytes){
return 0;
}
nOld = sqlite3MallocSize(pOld);
/* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second
** argument to xRealloc is always a value returned by a prior call to
** xRoundup. */
nNew = sqlite3GlobalConfig.m.xRoundup(nBytes);
if( nOld==nNew ){
pNew = pOld;
@ -525,6 +549,7 @@ void *sqlite3Realloc(void *pOld, int nBytes){
}else{
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
}
assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */
return pNew;
}
@ -591,14 +616,20 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
if( db->mallocFailed ){
return 0;
}
if( db->lookaside.bEnabled && n<=db->lookaside.sz
&& (pBuf = db->lookaside.pFree)!=0 ){
db->lookaside.pFree = pBuf->pNext;
db->lookaside.nOut++;
if( db->lookaside.nOut>db->lookaside.mxOut ){
db->lookaside.mxOut = db->lookaside.nOut;
if( db->lookaside.bEnabled ){
if( n>db->lookaside.sz ){
db->lookaside.anStat[1]++;
}else if( (pBuf = db->lookaside.pFree)==0 ){
db->lookaside.anStat[2]++;
}else{
db->lookaside.pFree = pBuf->pNext;
db->lookaside.nOut++;
db->lookaside.anStat[0]++;
if( db->lookaside.nOut>db->lookaside.mxOut ){
db->lookaside.mxOut = db->lookaside.nOut;
}
return (void*)pBuf;
}
return (void*)pBuf;
}
}
#else

View File

@ -89,7 +89,7 @@ static int sqlite3MemSize(void *pPrior){
static void *sqlite3MemRealloc(void *pPrior, int nByte){
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 && nByte>0 );
nByte = ROUND8(nByte);
assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
p--;
p = realloc(p, nByte+8 );
if( p ){

View File

@ -344,6 +344,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
struct MemBlockHdr *pOldHdr;
void *pNew;
assert( mem.disallow==0 );
assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */
pOldHdr = sqlite3MemsysGetHeader(pPrior);
pNew = sqlite3MemMalloc(nByte);
if( pNew ){

View File

@ -395,7 +395,7 @@ static void *memsys5Realloc(void *pPrior, int nBytes){
int nOld;
void *p;
assert( pPrior!=0 );
assert( (nBytes&(nBytes-1))==0 );
assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */
assert( nBytes>=0 );
if( nBytes==0 ){
return 0;

View File

@ -252,8 +252,7 @@ int sqlite3IsMemJournal(sqlite3_file *pJfd){
}
/*
** Return the number of bytes required to store a MemJournal that uses vfs
** pVfs to create the underlying on-disk files.
** Return the number of bytes required to store a MemJournal file descriptor.
*/
int sqlite3MemJournalSize(void){
return sizeof(MemJournal);

View File

@ -63,8 +63,8 @@
#define sqlite3_mutex_enter(X)
#define sqlite3_mutex_try(X) SQLITE_OK
#define sqlite3_mutex_leave(X)
#define sqlite3_mutex_held(X) 1
#define sqlite3_mutex_notheld(X) 1
#define sqlite3_mutex_held(X) ((void)(X),1)
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
#define sqlite3MutexInit() SQLITE_OK
#define sqlite3MutexEnd()

View File

@ -99,7 +99,7 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_STATIC_MEM2
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_LRU2
** <li> SQLITE_MUTEX_STATIC_PMEM
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create

View File

@ -156,7 +156,7 @@ static int winMutexEnd(void){
** <li> SQLITE_MUTEX_STATIC_MEM2
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_LRU2
** <li> SQLITE_MUTEX_STATIC_PMEM
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create

View File

@ -183,6 +183,12 @@ int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
}
int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
int rc;
/* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64()
** method to get the current date and time if that method is available
** (if iVersion is 2 or greater and the function pointer is not NULL) and
** will fall back to xCurrentTime() if xCurrentTimeInt64() is
** unavailable.
*/
if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut);
}else{

View File

@ -533,7 +533,7 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK;
}
}
return SQLITE_ERROR;
return SQLITE_NOTFOUND;
}
/*

View File

@ -119,7 +119,9 @@
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#ifndef SQLITE_OMIT_WAL
#include <sys/mman.h>
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
# include <sys/ioctl.h>
@ -3133,8 +3135,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return proxyFileControl(id,op,pArg);
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
case SQLITE_FCNTL_SYNC_OMITTED: {
return SQLITE_OK; /* A no-op */
}
}
return SQLITE_ERROR;
return SQLITE_NOTFOUND;
}
/*
@ -3559,7 +3564,7 @@ static int unixShmMap(
pShmNode->apRegion = apNew;
while(pShmNode->nRegion<=iRegion){
void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE,
MAP_SHARED, pShmNode->h, iRegion*szRegion
MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion
);
if( pMem==MAP_FAILED ){
rc = SQLITE_IOERR;
@ -4077,11 +4082,21 @@ static int fillInUnixFile(
*/
UNUSED_PARAMETER(isDelete);
/* Usually the path zFilename should not be a relative pathname. The
** exception is when opening the proxy "conch" file in builds that
** include the special Apple locking styles.
*/
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
assert( zFilename==0 || zFilename[0]=='/'
|| pVfs->pAppData==(void*)&autolockIoFinder );
#else
assert( zFilename==0 || zFilename[0]=='/' );
#endif
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
pNew->h = h;
pNew->dirfd = dirfd;
pNew->fileFlags = 0;
assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */
pNew->zPath = zFilename;
#if OS_VXWORKS
@ -4421,9 +4436,24 @@ static int findCreateFileMode(
int nDb; /* Number of valid bytes in zDb */
struct stat sStat; /* Output of stat() on database file */
nDb = sqlite3Strlen30(zPath) - ((flags & SQLITE_OPEN_WAL) ? 4 : 8);
/* zPath is a path to a WAL or journal file. The following block derives
** the path to the associated database file from zPath. This block handles
** the following naming conventions:
**
** "<path to db>-journal"
** "<path to db>-wal"
** "<path to db>-journal-NNNN"
** "<path to db>-wal-NNNN"
**
** where NNNN is a 4 digit decimal number. The NNNN naming schemes are
** used by the test_multiplex.c module.
*/
nDb = sqlite3Strlen30(zPath) - 1;
while( nDb>0 && zPath[nDb]!='l' ) nDb--;
nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7);
memcpy(zDb, zPath, nDb);
zDb[nDb] = '\0';
if( 0==stat(zDb, &sStat) ){
*pMode = sStat.st_mode & 0777;
}else{
@ -4838,7 +4868,7 @@ static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){
** error message.
*/
static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){
char *zErr;
const char *zErr;
UNUSED_PARAMETER(NotUsed);
unixEnterMutex();
zErr = dlerror();
@ -4975,7 +5005,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
#if defined(NO_GETTOD)
time_t t;
time(&t);
*piNow = ((sqlite3_int64)i)*1000 + unixEpoch;
*piNow = ((sqlite3_int64)t)*1000 + unixEpoch;
#elif OS_VXWORKS
struct timespec sNow;
clock_gettime(CLOCK_REALTIME, &sNow);
@ -5378,17 +5408,21 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait);
** bytes of writable memory.
*/
static int proxyGetHostID(unsigned char *pHostID, int *pError){
struct timespec timeout = {1, 0}; /* 1 sec timeout */
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
memset(pHostID, 0, PROXY_HOSTIDLEN);
if( gethostuuid(pHostID, &timeout) ){
int err = errno;
if( pError ){
*pError = err;
#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\
&& __MAC_OS_X_VERSION_MIN_REQUIRED<1050
{
static const struct timespec timeout = {1, 0}; /* 1 sec timeout */
if( gethostuuid(pHostID, &timeout) ){
int err = errno;
if( pError ){
*pError = err;
}
return SQLITE_IOERR;
}
return SQLITE_IOERR;
}
#endif
#ifdef SQLITE_TEST
/* simulate multiple hosts by creating unique hostid file paths */
if( sqlite3_hostid_num != 0){
@ -5429,27 +5463,27 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
pathLen = strlcpy(tPath, cPath, MAXPATHLEN);
if( pathLen>MAXPATHLEN || pathLen<6 ||
(strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){
sprintf(errmsg, "path error (len %d)", (int)pathLen);
sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen);
goto end_breaklock;
}
/* read the conch content */
readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0);
if( readLen<PROXY_PATHINDEX ){
sprintf(errmsg, "read error (len %d)", (int)readLen);
sqlite3_snprintf(sizeof(errmsg),errmsg,"read error (len %d)",(int)readLen);
goto end_breaklock;
}
/* write it out to the temporary break file */
fd = open(tPath, (O_RDWR|O_CREAT|O_EXCL), SQLITE_DEFAULT_FILE_PERMISSIONS);
if( fd<0 ){
sprintf(errmsg, "create failed (%d)", errno);
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
goto end_breaklock;
}
if( pwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){
sprintf(errmsg, "write failed (%d)", errno);
sqlite3_snprintf(sizeof(errmsg), errmsg, "write failed (%d)", errno);
goto end_breaklock;
}
if( rename(tPath, cPath) ){
sprintf(errmsg, "rename failed (%d)", errno);
sqlite3_snprintf(sizeof(errmsg), errmsg, "rename failed (%d)", errno);
goto end_breaklock;
}
rc = 0;

View File

@ -1183,8 +1183,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
SimulateIOErrorBenign(0);
return SQLITE_OK;
}
case SQLITE_FCNTL_SYNC_OMITTED: {
return SQLITE_OK;
}
}
return SQLITE_ERROR;
return SQLITE_NOTFOUND;
}
/*
@ -1212,6 +1215,14 @@ static int winDeviceCharacteristics(sqlite3_file *id){
#ifndef SQLITE_OMIT_WAL
/*
** Windows will only let you create file view mappings
** on allocation size granularity boundaries.
** During sqlite3_os_init() we do a GetSystemInfo()
** to get the granularity size.
*/
SYSTEM_INFO winSysInfo;
/*
** Helper functions to obtain and relinquish the global mutex. The
** global mutex is used to protect the winLockInfo objects used by
@ -1380,6 +1391,7 @@ static int winDelete(sqlite3_vfs *,const char*,int);
static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
winShmNode **pp;
winShmNode *p;
BOOL bRc;
assert( winShmMutexHeld() );
pp = &winShmNodeList;
while( (p = *pp)!=0 ){
@ -1387,8 +1399,14 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
int i;
if( p->mutex ) sqlite3_mutex_free(p->mutex);
for(i=0; i<p->nRegion; i++){
UnmapViewOfFile(p->aRegion[i].pMap);
CloseHandle(p->aRegion[i].hMap);
bRc = UnmapViewOfFile(p->aRegion[i].pMap);
OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
(int)GetCurrentProcessId(), i,
bRc ? "ok" : "failed"));
bRc = CloseHandle(p->aRegion[i].hMap);
OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n",
(int)GetCurrentProcessId(), i,
bRc ? "ok" : "failed"));
}
if( p->hFile.h != INVALID_HANDLE_VALUE ){
SimulateIOErrorBenign(1);
@ -1465,10 +1483,11 @@ static int winOpenSharedMemory(winFile *pDbFd){
rc = SQLITE_NOMEM;
goto shm_open_err;
}
rc = winOpen(pDbFd->pVfs,
pShmNode->zFilename, /* Name of the file (UTF-8) */
(sqlite3_file*)&pShmNode->hFile, /* File handle here */
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */
SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */
0);
if( SQLITE_OK!=rc ){
rc = SQLITE_CANTOPEN_BKPT;
@ -1776,10 +1795,18 @@ static int winShmMap(
hMap = CreateFileMapping(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n",
(int)GetCurrentProcessId(), pShmNode->nRegion, nByte,
hMap ? "ok" : "failed"));
if( hMap ){
int iOffset = pShmNode->nRegion*szRegion;
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
0, 0, nByte
0, iOffset - iOffsetShift, szRegion + iOffsetShift
);
OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n",
(int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion,
pMap ? "ok" : "failed"));
}
if( !pMap ){
pShmNode->lastErrno = GetLastError();
@ -1796,8 +1823,10 @@ static int winShmMap(
shmpage_out:
if( pShmNode->nRegion>iRegion ){
int iOffset = iRegion*szRegion;
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
char *p = (char *)pShmNode->aRegion[iRegion].pMap;
*pp = (void *)&p[iRegion*szRegion];
*pp = (void *)&p[iOffsetShift];
}else{
*pp = 0;
}
@ -2024,9 +2053,60 @@ static int winOpen(
int isTemp = 0;
#endif
winFile *pFile = (winFile*)id;
void *zConverted; /* Filename in OS encoding */
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
void *zConverted; /* Filename in OS encoding */
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
int rc = SQLITE_OK; /* Function Return Code */
#if !defined(NDEBUG) || SQLITE_OS_WINCE
int eType = flags&0xFFFFFF00; /* Type of file to open */
#endif
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
int isCreate = (flags & SQLITE_OPEN_CREATE);
#ifndef NDEBUG
int isReadonly = (flags & SQLITE_OPEN_READONLY);
#endif
int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
#ifndef NDEBUG
int isOpenJournal = (isCreate && (
eType==SQLITE_OPEN_MASTER_JOURNAL
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
));
#endif
/* Check the following statements are true:
**
** (a) Exactly one of the READWRITE and READONLY flags must be set, and
** (b) if CREATE is set, then READWRITE must also be set, and
** (c) if EXCLUSIVE is set, then CREATE must also be set.
** (d) if DELETEONCLOSE is set, then CREATE must also be set.
*/
assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
assert(isCreate==0 || isReadWrite);
assert(isExclusive==0 || isCreate);
assert(isDelete==0 || isCreate);
/* The main DB, main journal, WAL file and master journal are never
** automatically deleted. Nor are they ever temporary files. */
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
/* Assert that the upper layer has set one of the "file-type" flags. */
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
assert( id!=0 );
UNUSED_PARAMETER(pVfs);
@ -2037,7 +2117,8 @@ static int winOpen(
** temporary file name to use
*/
if( !zUtf8Name ){
int rc = getTempname(MAX_PATH+1, zTmpname);
assert(isDelete && !isOpenJournal);
rc = getTempname(MAX_PATH+1, zTmpname);
if( rc!=SQLITE_OK ){
return rc;
}
@ -2050,29 +2131,31 @@ static int winOpen(
return SQLITE_NOMEM;
}
if( flags & SQLITE_OPEN_READWRITE ){
if( isReadWrite ){
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
}else{
dwDesiredAccess = GENERIC_READ;
}
/* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
** created. SQLite doesn't use it to indicate "exclusive access"
** as it is usually understood.
*/
assert(!(flags & SQLITE_OPEN_EXCLUSIVE) || (flags & SQLITE_OPEN_CREATE));
if( flags & SQLITE_OPEN_EXCLUSIVE ){
if( isExclusive ){
/* Creates a new file, only if it does not already exist. */
/* If the file exists, it fails. */
dwCreationDisposition = CREATE_NEW;
}else if( flags & SQLITE_OPEN_CREATE ){
}else if( isCreate ){
/* Open existing file, or create if it doesn't exist */
dwCreationDisposition = OPEN_ALWAYS;
}else{
/* Opens a file, only if it exists. */
dwCreationDisposition = OPEN_EXISTING;
}
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
if( flags & SQLITE_OPEN_DELETEONCLOSE ){
if( isDelete ){
#if SQLITE_OS_WINCE
dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
isTemp = 1;
@ -2089,6 +2172,7 @@ static int winOpen(
#if SQLITE_OS_WINCE
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
#endif
if( isNT() ){
h = CreateFileW((WCHAR*)zConverted,
dwDesiredAccess,
@ -2114,26 +2198,30 @@ static int winOpen(
);
#endif
}
OSTRACE(("OPEN %d %s 0x%lx %s\n",
h, zName, dwDesiredAccess,
h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
if( h==INVALID_HANDLE_VALUE ){
pFile->lastErrno = GetLastError();
free(zConverted);
if( flags & SQLITE_OPEN_READWRITE ){
if( isReadWrite ){
return winOpen(pVfs, zName, id,
((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags);
}else{
return SQLITE_CANTOPEN_BKPT;
}
}
if( pOutFlags ){
if( flags & SQLITE_OPEN_READWRITE ){
if( isReadWrite ){
*pOutFlags = SQLITE_OPEN_READWRITE;
}else{
*pOutFlags = SQLITE_OPEN_READONLY;
}
}
memset(pFile, 0, sizeof(*pFile));
pFile->pMethod = &winIoMethod;
pFile->h = h;
@ -2142,9 +2230,9 @@ static int winOpen(
pFile->pShm = 0;
pFile->zPath = zName;
pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
#if SQLITE_OS_WINCE
if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
(SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
&& !winceCreateLock(zName, pFile)
){
CloseHandle(h);
@ -2158,8 +2246,9 @@ static int winOpen(
{
free(zConverted);
}
OpenCounter(+1);
return SQLITE_OK;
return rc;
}
/*
@ -2678,6 +2767,13 @@ int sqlite3_os_init(void){
winCurrentTimeInt64, /* xCurrentTimeInt64 */
};
#ifndef SQLITE_OMIT_WAL
/* get memory map allocation granularity */
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
GetSystemInfo(&winSysInfo);
assert(winSysInfo.dwAllocationGranularity > 0);
#endif
sqlite3_vfs_register(&winVfs, 1);
return SQLITE_OK;
}

View File

@ -615,7 +615,8 @@ struct Pager {
u8 noReadlock; /* Do not bother to obtain readlocks */
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
u8 tempFile; /* zFilename is a temporary file */
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
@ -926,7 +927,9 @@ static int assert_pager_state(Pager *p){
return 1;
}
#endif /* ifndef NDEBUG */
#ifdef SQLITE_DEBUG
/*
** Return a pointer to a human readable string in a static buffer
** containing the state of the Pager object passed as an argument. This
@ -1050,7 +1053,7 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
static int pagerUnlockDb(Pager *pPager, int eLock){
int rc = SQLITE_OK;
assert( !pPager->exclusiveMode );
assert( !pPager->exclusiveMode || pPager->eLock==eLock );
assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
if( isOpen(pPager->fd) ){
@ -1297,7 +1300,7 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){
rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
}
if( rc==SQLITE_OK && !pPager->noSync ){
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags);
}
/* At this point the transaction is committed but the write lock
@ -2474,15 +2477,21 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
i64 currentSize, newSize;
int szPage = pPager->pageSize;
assert( pPager->eLock==EXCLUSIVE_LOCK );
/* TODO: Is it safe to use Pager.dbFileSize here? */
rc = sqlite3OsFileSize(pPager->fd, &currentSize);
newSize = pPager->pageSize*(i64)nPage;
newSize = szPage*(i64)nPage;
if( rc==SQLITE_OK && currentSize!=newSize ){
if( currentSize>newSize ){
rc = sqlite3OsTruncate(pPager->fd, newSize);
}else{
rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
char *pTmp = pPager->pTmpSpace;
memset(pTmp, 0, szPage);
testcase( (newSize-szPage) < currentSize );
testcase( (newSize-szPage) == currentSize );
testcase( (newSize-szPage) > currentSize );
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
}
if( rc==SQLITE_OK ){
pPager->dbFileSize = nPage;
@ -2746,10 +2755,10 @@ end_playback:
rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK && !pPager->noSync
if( rc==SQLITE_OK
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
rc = sqlite3PagerSync(pPager);
}
if( rc==SQLITE_OK ){
rc = pager_end_transaction(pPager, zMaster[0]!='\0');
@ -2912,24 +2921,61 @@ static int pagerRollbackWal(Pager *pPager){
return rc;
}
/*
** Update the value of the change-counter at offsets 24 and 92 in
** the header and the sqlite version number at offset 96.
**
** This is an unconditional update. See also the pager_incr_changecounter()
** routine which only updates the change-counter if the update is actually
** needed, as determined by the pPager->changeCountDone state variable.
*/
static void pager_write_changecounter(PgHdr *pPg){
u32 change_counter;
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
put32bits(((char*)pPg->pData)+24, change_counter);
/* Also store the SQLite version number in bytes 96..99 and in
** bytes 92..95 store the change counter for which the version number
** is valid. */
put32bits(((char*)pPg->pData)+92, change_counter);
put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER);
}
/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
** this function notifies any active backup processes that the pages have
** changed.
** changed.
**
** The list of pages passed into this routine is always sorted by page number.
** Hence, if page 1 appears anywhere on the list, it will be the first page.
*/
static int pagerWalFrames(
Pager *pPager, /* Pager object */
PgHdr *pList, /* List of frames to log */
Pgno nTruncate, /* Database size after this commit */
int isCommit, /* True if this is a commit */
int sync_flags /* Flags to pass to OsSync() (or 0) */
int syncFlags /* Flags to pass to OsSync() (or 0) */
){
int rc; /* Return code */
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
PgHdr *p; /* For looping over pages */
#endif
assert( pPager->pWal );
#ifdef SQLITE_DEBUG
/* Verify that the page list is in accending order */
for(p=pList; p && p->pDirty; p=p->pDirty){
assert( p->pgno < p->pDirty->pgno );
}
#endif
if( pList->pgno==1 ) pager_write_changecounter(pList);
rc = sqlite3WalFrames(pPager->pWal,
pPager->pageSize, pList, nTruncate, isCommit, sync_flags
pPager->pageSize, pList, nTruncate, isCommit, syncFlags
);
if( rc==SQLITE_OK && pPager->pBackup ){
PgHdr *p;
@ -2939,9 +2985,8 @@ static int pagerWalFrames(
}
#ifdef SQLITE_CHECK_PAGES
{
PgHdr *p;
for(p=pList; p; p=p->pDirty) pager_set_pagehash(p);
for(p=pList; p; p=p->pDirty){
pager_set_pagehash(p);
}
#endif
@ -2971,12 +3016,13 @@ static int pagerBeginReadTransaction(Pager *pPager){
sqlite3WalEndReadTransaction(pPager->pWal);
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
if( rc==SQLITE_OK && changed ){
if( rc!=SQLITE_OK || changed ){
pager_reset(pPager);
}
return rc;
}
#endif
/*
** This function is called as part of the transition from PAGER_OPEN
@ -3033,7 +3079,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
return SQLITE_OK;
}
#ifndef SQLITE_OMIT_WAL
/*
** Check if the *-wal file that corresponds to the database opened by pPager
** exists if the database is not empy, or verify that the *-wal file does
@ -3258,14 +3304,49 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
** assurance that the journal will not be corrupted to the
** point of causing damage to the database during rollback.
**
** The above is for a rollback-journal mode. For WAL mode, OFF continues
** to mean that no syncs ever occur. NORMAL means that the WAL is synced
** prior to the start of checkpoint and that the database file is synced
** at the conclusion of the checkpoint if the entire content of the WAL
** was written back into the database. But no sync operations occur for
** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL
** file is synced following each commit operation, in addition to the
** syncs associated with NORMAL.
**
** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The
** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync
** using fcntl(F_FULLFSYNC). SQLITE_SYNC_NORMAL means to do an
** ordinary fsync() call. There is no difference between SQLITE_SYNC_FULL
** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the
** synchronous=FULL versus synchronous=NORMAL setting determines when
** the xSync primitive is called and is relevant to all platforms.
**
** Numeric values associated with these states are OFF==1, NORMAL=2,
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
void sqlite3PagerSetSafetyLevel(
Pager *pPager, /* The pager to set safety level for */
int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
int bFullFsync, /* PRAGMA fullfsync */
int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */
){
assert( level>=1 && level<=3 );
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
}else if( bFullFsync ){
pPager->syncFlags = SQLITE_SYNC_FULL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else if( bCkptFullFsync ){
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else{
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
}
}
#endif
@ -3444,9 +3525,8 @@ int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
if( mxPage>0 ){
pPager->mxPgno = mxPage;
}
if( pPager->eState!=PAGER_OPEN && pPager->mxPgno<pPager->dbSize ){
pPager->mxPgno = pPager->dbSize;
}
assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */
assert( pPager->mxPgno>=pPager->dbSize ); /* OP_MaxPgcnt enforces this */
return pPager->mxPgno;
}
@ -3651,10 +3731,7 @@ int sqlite3PagerClose(Pager *pPager){
/* pPager->errCode = 0; */
pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
sqlite3WalClose(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, pTmp
);
sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp);
pPager->pWal = 0;
#endif
pager_reset(pPager);
@ -3820,7 +3897,7 @@ static int syncJournal(Pager *pPager, int newHdr){
if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
if( rc!=SQLITE_OK ) return rc;
}
IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr));
@ -3832,8 +3909,8 @@ static int syncJournal(Pager *pPager, int newHdr){
if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
(pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags|
(pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
);
if( rc!=SQLITE_OK ) return rc;
}
@ -3934,6 +4011,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
char *pData; /* Data to write */
assert( (pList->flags&PGHDR_NEED_SYNC)==0 );
if( pList->pgno==1 ) pager_write_changecounter(pList);
/* Encode the database */
CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
@ -4226,6 +4304,13 @@ int sqlite3PagerOpen(
/* Set the output variable to NULL in case an error occurs. */
*ppPager = 0;
#ifndef SQLITE_OMIT_MEMORYDB
if( flags & PAGER_MEMORY ){
memDb = 1;
zFilename = 0;
}
#endif
/* Compute and store the full pathname in an allocated buffer pointed
** to by zPathname, length nPathname. Or, if this is a temporary file,
** leave both nPathname and zPathname set to 0.
@ -4236,17 +4321,8 @@ int sqlite3PagerOpen(
if( zPathname==0 ){
return SQLITE_NOMEM;
}
#ifndef SQLITE_OMIT_MEMORYDB
if( strcmp(zFilename,":memory:")==0 ){
memDb = 1;
zPathname[0] = 0;
}else
#endif
{
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
}
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
nPathname = sqlite3Strlen30(zPathname);
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
/* This branch is taken when the journal path required by
@ -4301,19 +4377,15 @@ int sqlite3PagerOpen(
/* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */
if( zPathname ){
assert( nPathname>0 );
pPager->zJournal = (char*)(pPtr += nPathname + 1);
memcpy(pPager->zFilename, zPathname, nPathname);
memcpy(pPager->zJournal, zPathname, nPathname);
memcpy(&pPager->zJournal[nPathname], "-journal", 8);
if( pPager->zFilename[0]==0 ){
pPager->zJournal[0] = 0;
}
#ifndef SQLITE_OMIT_WAL
else{
pPager->zWal = &pPager->zJournal[nPathname+8+1];
memcpy(pPager->zWal, zPathname, nPathname);
memcpy(&pPager->zWal[nPathname], "-wal", 4);
}
pPager->zWal = &pPager->zJournal[nPathname+8+1];
memcpy(pPager->zWal, zPathname, nPathname);
memcpy(&pPager->zWal[nPathname], "-wal", 4);
#endif
sqlite3_free(zPathname);
}
@ -4322,9 +4394,10 @@ int sqlite3PagerOpen(
/* Open the pager file.
*/
if( zFilename && zFilename[0] && !memDb ){
if( zFilename && zFilename[0] ){
int fout = 0; /* VFS flags returned by xOpen() */
rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout);
assert( !memDb );
readOnly = (fout&SQLITE_OPEN_READONLY);
/* If the file was successfully opened for read/write access,
@ -4428,7 +4501,8 @@ int sqlite3PagerOpen(
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
pPager->fullSync = pPager->noSync ?0:1;
pPager->sync_flags = SQLITE_SYNC_NORMAL;
pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = pPager->syncFlags;
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
@ -4528,7 +4602,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3BeginBenignMalloc();
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
pagerUnlockDb(pPager, SHARED_LOCK);
if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
}
sqlite3EndBenignMalloc();
}else{
@ -4778,7 +4852,9 @@ int sqlite3PagerSharedLock(Pager *pPager){
** mode. Otherwise, the following function call is a no-op.
*/
rc = pagerOpenWalIfPresent(pPager);
#ifndef SQLITE_OMIT_WAL
assert( pPager->pWal==0 || rc==SQLITE_OK );
#endif
}
if( pagerUseWal(pPager) ){
@ -5207,29 +5283,29 @@ static int pager_write(PgHdr *pPg){
CHECK_PAGE(pPg);
/* The journal file needs to be opened. Higher level routines have already
** obtained the necessary locks to begin the write-transaction, but the
** rollback journal might not yet be open. Open it now if this is the case.
**
** This is done before calling sqlite3PcacheMakeDirty() on the page.
** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then
** an error might occur and the pager would end up in WRITER_LOCKED state
** with pages marked as dirty in the cache.
*/
if( pPager->eState==PAGER_WRITER_LOCKED ){
rc = pager_open_journal(pPager);
if( rc!=SQLITE_OK ) return rc;
}
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
assert( assert_pager_state(pPager) );
/* Mark the page as dirty. If the page has already been written
** to the journal then we can return right away.
*/
sqlite3PcacheMakeDirty(pPg);
if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
assert( !pagerUseWal(pPager) );
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
}else{
/* If we get this far, it means that the page needs to be
** written to the transaction journal or the checkpoint journal
** or both.
**
** Higher level routines have already obtained the necessary locks
** to begin the write-transaction, but the rollback journal might not
** yet be open. Open it now if this is the case.
*/
if( pPager->eState==PAGER_WRITER_LOCKED ){
rc = pager_open_journal(pPager);
if( rc!=SQLITE_OK ) return rc;
}
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
assert( assert_pager_state(pPager) );
/* The transaction journal now exists and we have a RESERVED or an
** EXCLUSIVE lock on the main database file. Write the current page to
@ -5456,7 +5532,13 @@ void sqlite3PagerDontWrite(PgHdr *pPg){
/*
** This routine is called to increment the value of the database file
** change-counter, stored as a 4-byte big-endian integer starting at
** byte offset 24 of the pager file.
** byte offset 24 of the pager file. The secondary change counter at
** 92 is also updated, as is the SQLite version number at offset 96.
**
** But this only happens if the pPager->changeCountDone flag is false.
** To avoid excess churning of page 1, the update only happens once.
** See also the pager_write_changecounter() routine that does an
** unconditional update of the change counters.
**
** If the isDirectMode flag is zero, then this is done by calling
** sqlite3PagerWrite() on page 1, then modifying the contents of the
@ -5497,7 +5579,6 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
if( !pPager->changeCountDone && pPager->dbSize>0 ){
PgHdr *pPgHdr; /* Reference to page 1 */
u32 change_counter; /* Initial value of change-counter field */
assert( !pPager->tempFile && isOpen(pPager->fd) );
@ -5515,16 +5596,8 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
}
if( rc==SQLITE_OK ){
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers);
change_counter++;
put32bits(((char*)pPgHdr->pData)+24, change_counter);
/* Also store the SQLite version number in bytes 96..99 and in
** bytes 92..95 store the change counter for which the version number
** is valid. */
put32bits(((char*)pPgHdr->pData)+92, change_counter);
put32bits(((char*)pPgHdr->pData)+96, SQLITE_VERSION_NUMBER);
/* Actually do the update of the change counter */
pager_write_changecounter(pPgHdr);
/* If running in direct mode, write the contents of page 1 to the file. */
if( DIRECT_MODE ){
@ -5549,19 +5622,20 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
}
/*
** Sync the pager file to disk. This is a no-op for in-memory files
** Sync the database file to disk. This is a no-op for in-memory databases
** or pages with the Pager.noSync flag set.
**
** If successful, or called on a pager for which it is a no-op, this
** If successful, or if called on a pager for which it is a no-op, this
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
*/
int sqlite3PagerSync(Pager *pPager){
int rc; /* Return code */
assert( !MEMDB );
if( pPager->noSync ){
rc = SQLITE_OK;
}else{
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
int rc = SQLITE_OK;
if( !pPager->noSync ){
assert( !MEMDB );
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
}else if( isOpen(pPager->fd) ){
assert( !MEMDB );
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc);
}
return rc;
}
@ -5650,7 +5724,7 @@ int sqlite3PagerCommitPhaseOne(
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
if( pList ){
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
(pPager->fullSync ? pPager->sync_flags : 0)
(pPager->fullSync ? pPager->syncFlags : 0)
);
}
if( rc==SQLITE_OK ){
@ -5780,8 +5854,8 @@ int sqlite3PagerCommitPhaseOne(
}
/* Finally, sync the database file. */
if( !pPager->noSync && !noSync ){
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
if( !noSync ){
rc = sqlite3PagerSync(pPager);
}
IOTRACE(("DBSYNC %p\n", pPager))
}
@ -5893,7 +5967,17 @@ int sqlite3PagerRollback(Pager *pPager){
rc2 = pager_end_transaction(pPager, pPager->setMaster);
if( rc==SQLITE_OK ) rc = rc2;
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
int eState = pPager->eState;
rc = pager_end_transaction(pPager, 0);
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
/* This can happen using journal_mode=off. Move the pager to the error
** state to indicate that the contents of the cache may not be trusted.
** Any active readers will get SQLITE_ABORT.
*/
pPager->errCode = SQLITE_ABORT;
pPager->eState = PAGER_ERROR;
return rc;
}
}else{
rc = pager_playback(pPager, 0);
}
@ -6352,7 +6436,8 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){
|| eMode==PAGER_LOCKINGMODE_EXCLUSIVE );
assert( PAGER_LOCKINGMODE_QUERY<0 );
assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 );
if( eMode>=0 && !pPager->tempFile ){
assert( pPager->exclusiveMode || 0==sqlite3WalHeapMemory(pPager->pWal) );
if( eMode>=0 && !pPager->tempFile && !sqlite3WalHeapMemory(pPager->pWal) ){
pPager->exclusiveMode = (u8)eMode;
}
return (int)pPager->exclusiveMode;
@ -6521,10 +6606,8 @@ int sqlite3PagerCheckpoint(Pager *pPager){
int rc = SQLITE_OK;
if( pPager->pWal ){
u8 *zBuf = (u8 *)pPager->pTmpSpace;
rc = sqlite3WalCheckpoint(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, zBuf
);
rc = sqlite3WalCheckpoint(pPager->pWal, pPager->ckptSyncFlags,
pPager->pageSize, zBuf);
}
return rc;
}
@ -6539,9 +6622,61 @@ int sqlite3PagerWalCallback(Pager *pPager){
*/
int sqlite3PagerWalSupported(Pager *pPager){
const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
return pMethods->iVersion>=2 && pMethods->xShmMap!=0;
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
}
/*
** Attempt to take an exclusive lock on the database file. If a PENDING lock
** is obtained instead, immediately release it.
*/
static int pagerExclusiveLock(Pager *pPager){
int rc; /* Return code */
assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
/* If the attempt to grab the pending lock failed, release the
** exclusive lock that may have been obtained instead. */
pagerUnlockDb(pPager, SHARED_LOCK);
}
return rc;
}
/*
** Call sqlite3WalOpen() to open the WAL handle. If the pager is in
** exclusive-locking mode when this function is called, take an EXCLUSIVE
** lock on the database file and use heap-memory to store the wal-index
** in. Otherwise, use the normal shared-memory.
*/
static int pagerOpenWal(Pager *pPager){
int rc = SQLITE_OK;
assert( pPager->pWal==0 && pPager->tempFile==0 );
assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK || pPager->noReadlock);
/* If the pager is already in exclusive-mode, the WAL module will use
** heap-memory for the wal-index instead of the VFS shared-memory
** implementation. Take the exclusive lock now, before opening the WAL
** file, to make sure this is safe.
*/
if( pPager->exclusiveMode ){
rc = pagerExclusiveLock(pPager);
}
/* Open the connection to the log file. If this operation fails,
** (e.g. due to malloc() failure), return an error code.
*/
if( rc==SQLITE_OK ){
rc = sqlite3WalOpen(pPager->pVfs,
pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal
);
}
return rc;
}
/*
** The caller must be holding a SHARED lock on the database file to call
** this function.
@ -6575,11 +6710,7 @@ int sqlite3PagerOpenWal(
/* Close any rollback journal previously open */
sqlite3OsClose(pPager->jfd);
/* Open the connection to the log file. If this operation fails,
** (e.g. due to malloc() failure), unlock the database file and
** return an error code.
*/
rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
rc = pagerOpenWal(pPager);
if( rc==SQLITE_OK ){
pPager->journalMode = PAGER_JOURNALMODE_WAL;
pPager->eState = PAGER_OPEN;
@ -6618,8 +6749,7 @@ int sqlite3PagerCloseWal(Pager *pPager){
);
}
if( rc==SQLITE_OK && logexists ){
rc = sqlite3WalOpen(pPager->pVfs, pPager->fd,
pPager->zWal, &pPager->pWal);
rc = pagerOpenWal(pPager);
}
}
@ -6627,17 +6757,11 @@ int sqlite3PagerCloseWal(Pager *pPager){
** the database file, the log and log-summary files will be deleted.
*/
if( rc==SQLITE_OK && pPager->pWal ){
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
rc = pagerExclusiveLock(pPager);
if( rc==SQLITE_OK ){
rc = sqlite3WalClose(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, (u8*)pPager->pTmpSpace
);
rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
pPager->pageSize, (u8*)pPager->pTmpSpace);
pPager->pWal = 0;
}else{
/* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
** that we did get back to SHARED. */
pagerUnlockDb(pPager, SQLITE_LOCK_SHARED);
}
}
return rc;

View File

@ -59,6 +59,7 @@ typedef struct PgHdr DbPage;
*/
#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */
#define PAGER_MEMORY 0x0004 /* In-memory database */
/*
** Valid values for the second argument to sqlite3PagerLockingMode().
@ -102,7 +103,7 @@ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
int sqlite3PagerSetPagesize(Pager*, u32*, int);
int sqlite3PagerMaxPageCount(Pager*, int);
void sqlite3PagerSetCachesize(Pager*, int);
void sqlite3PagerSetSafetyLevel(Pager*,int,int);
void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
int sqlite3PagerLockingMode(Pager *, int);
int sqlite3PagerSetJournalMode(Pager *, int);
int sqlite3PagerGetJournalMode(Pager*);

View File

@ -142,12 +142,16 @@ static void pcacheUnpin(PgHdr *p){
*/
int sqlite3PcacheInitialize(void){
if( sqlite3GlobalConfig.pcache.xInit==0 ){
/* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
** built-in default page cache is used instead of the application defined
** page cache. */
sqlite3PCacheSetDefault();
}
return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);
}
void sqlite3PcacheShutdown(void){
if( sqlite3GlobalConfig.pcache.xShutdown ){
/* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg);
}
}

View File

@ -22,24 +22,62 @@
typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
/* Pointers to structures of this type are cast and returned as
** opaque sqlite3_pcache* handles
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
** of one or more PCaches that are able to recycle each others unpinned
** pages when they are under memory pressure. A PGroup is an instance of
** the following object.
**
** This page cache implementation works in one of two modes:
**
** (1) Every PCache is the sole member of its own PGroup. There is
** one PGroup per PCache.
**
** (2) There is a single global PGroup that all PCaches are a member
** of.
**
** Mode 1 uses more memory (since PCache instances are not able to rob
** unused pages from other PCaches) but it also operates without a mutex,
** and is therefore often faster. Mode 2 requires a mutex in order to be
** threadsafe, but is able recycle pages more efficient.
**
** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single
** PGroup which is the pcache1.grp global variable and its mutex is
** SQLITE_MUTEX_STATIC_LRU.
*/
struct PGroup {
sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */
int nMaxPage; /* Sum of nMax for purgeable caches */
int nMinPage; /* Sum of nMin for purgeable caches */
int mxPinned; /* nMaxpage + 10 - nMinPage */
int nCurrentPage; /* Number of purgeable pages allocated */
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
};
/* Each page cache is an instance of the following object. Every
** open database file (including each in-memory database and each
** temporary or transient database) has a single page cache which
** is an instance of this object.
**
** Pointers to structures of this type are cast and returned as
** opaque sqlite3_pcache* handles.
*/
struct PCache1 {
/* Cache configuration parameters. Page size (szPage) and the purgeable
** flag (bPurgeable) are set when the cache is created. nMax may be
** modified at any time by a call to the pcache1CacheSize() method.
** The global mutex must be held when accessing nMax.
** The PGroup mutex must be held when accessing nMax.
*/
PGroup *pGroup; /* PGroup this cache belongs to */
int szPage; /* Size of allocated pages in bytes */
int bPurgeable; /* True if cache is purgeable */
unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */
unsigned int n90pct; /* nMax*9/10 */
/* Hash table of all pages. The following variables may only be accessed
** when the accessor is holding the global mutex (see pcache1EnterMutex()
** and pcache1LeaveMutex()).
** when the accessor is holding the PGroup mutex.
*/
unsigned int nRecyclable; /* Number of pages in the LRU list */
unsigned int nPage; /* Total number of pages in apHash */
@ -75,18 +113,27 @@ struct PgFreeslot {
** Global data used by this cache.
*/
static SQLITE_WSD struct PCacheGlobal {
sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */
PGroup grp; /* The global PGroup for mode (2) */
int nMaxPage; /* Sum of nMaxPage for purgeable caches */
int nMinPage; /* Sum of nMinPage for purgeable caches */
int nCurrentPage; /* Number of purgeable pages allocated */
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
/* Variables related to SQLITE_CONFIG_PAGECACHE settings. */
int szSlot; /* Size of each free slot */
void *pStart, *pEnd; /* Bounds of pagecache malloc range */
PgFreeslot *pFree; /* Free page blocks */
int isInit; /* True if initialized */
/* Variables related to SQLITE_CONFIG_PAGECACHE settings. The
** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all
** fixed at sqlite3_initialize() time and do not require mutex protection.
** The nFreeSlot and pFree values do require mutex protection.
*/
int isInit; /* True if initialized */
int szSlot; /* Size of each free slot */
int nSlot; /* The number of pcache slots */
int nReserve; /* Try to keep nFreeSlot above this */
void *pStart, *pEnd; /* Bounds of pagecache malloc range */
/* Above requires no mutex. Use mutex below for variable that follow. */
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
int nFreeSlot; /* Number of unused pcache slots */
PgFreeslot *pFree; /* Free page blocks */
/* The following value requires a mutex to change. We skip the mutex on
** reading because (1) most platforms read a 32-bit integer atomically and
** (2) even if an incorrect value is read, no great harm is done since this
** is really just an optimization. */
int bUnderPressure; /* True if low on PAGECACHE memory */
} pcache1_g;
/*
@ -112,10 +159,10 @@ static SQLITE_WSD struct PCacheGlobal {
#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
/*
** Macros to enter and leave the global LRU mutex.
** Macros to enter and leave the PCache LRU mutex.
*/
#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex)
#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex)
#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
#define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
/******************************************************************************/
/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
@ -125,14 +172,20 @@ static SQLITE_WSD struct PCacheGlobal {
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
** verb to sqlite3_config(). Parameter pBuf points to an allocation large
** enough to contain 'n' buffers of 'sz' bytes each.
**
** This routine is called from sqlite3_initialize() and so it is guaranteed
** to be serialized already. There is no need for further mutexing.
*/
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
if( pcache1.isInit ){
PgFreeslot *p;
sz = ROUNDDOWN8(sz);
pcache1.szSlot = sz;
pcache1.nSlot = pcache1.nFreeSlot = n;
pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
pcache1.pStart = pBuf;
pcache1.pFree = 0;
pcache1.bUnderPressure = 0;
while( n-- ){
p = (PgFreeslot*)pBuf;
p->pNext = pcache1.pFree;
@ -148,30 +201,36 @@ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
** such buffer exists or there is no space left in it, this function falls
** back to sqlite3Malloc().
**
** Multiple threads can run this routine at the same time. Global variables
** in pcache1 need to be protected via mutex.
*/
static void *pcache1Alloc(int nByte){
void *p;
assert( sqlite3_mutex_held(pcache1.mutex) );
void *p = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
if( nByte<=pcache1.szSlot && pcache1.pFree ){
assert( pcache1.isInit );
if( nByte<=pcache1.szSlot ){
sqlite3_mutex_enter(pcache1.mutex);
p = (PgHdr1 *)pcache1.pFree;
pcache1.pFree = pcache1.pFree->pNext;
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
}else{
/* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the
** global pcache mutex and unlock the pager-cache object pCache. This is
** so that if the attempt to allocate a new buffer causes the the
** configured soft-heap-limit to be breached, it will be possible to
** reclaim memory from this pager-cache.
if( p ){
pcache1.pFree = pcache1.pFree->pNext;
pcache1.nFreeSlot--;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
assert( pcache1.nFreeSlot>=0 );
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
}
sqlite3_mutex_leave(pcache1.mutex);
}
if( p==0 ){
/* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get
** it from sqlite3Malloc instead.
*/
pcache1LeaveMutex();
p = sqlite3Malloc(nByte);
pcache1EnterMutex();
if( p ){
int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
sqlite3_mutex_leave(pcache1.mutex);
}
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
}
@ -182,30 +241,35 @@ static void *pcache1Alloc(int nByte){
** Free an allocated buffer obtained from pcache1Alloc().
*/
static void pcache1Free(void *p){
assert( sqlite3_mutex_held(pcache1.mutex) );
if( p==0 ) return;
if( p>=pcache1.pStart && p<pcache1.pEnd ){
PgFreeslot *pSlot;
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
pSlot = (PgFreeslot*)p;
pSlot->pNext = pcache1.pFree;
pcache1.pFree = pSlot;
pcache1.nFreeSlot++;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
assert( pcache1.nFreeSlot<=pcache1.nSlot );
sqlite3_mutex_leave(pcache1.mutex);
}else{
int iSize;
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
iSize = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
sqlite3_mutex_leave(pcache1.mutex);
sqlite3_free(p);
}
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/*
** Return the size of a pache allocation
** Return the size of a pcache allocation
*/
static int pcache1MemSize(void *p){
assert( sqlite3_mutex_held(pcache1.mutex) );
if( p>=pcache1.pStart && p<pcache1.pEnd ){
return pcache1.szSlot;
}else{
@ -229,7 +293,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
if( pPg ){
p = PAGE_TO_PGHDR1(pCache, pPg);
if( pCache->bPurgeable ){
pcache1.nCurrentPage++;
pCache->pGroup->nCurrentPage++;
}
}else{
p = 0;
@ -246,8 +310,9 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
*/
static void pcache1FreePage(PgHdr1 *p){
if( ALWAYS(p) ){
if( p->pCache->bPurgeable ){
pcache1.nCurrentPage--;
PCache1 *pCache = p->pCache;
if( pCache->bPurgeable ){
pCache->pGroup->nCurrentPage--;
}
pcache1Free(PGHDR1_TO_PAGE(p));
}
@ -259,20 +324,39 @@ static void pcache1FreePage(PgHdr1 *p){
** exists, this function falls back to sqlite3Malloc().
*/
void *sqlite3PageMalloc(int sz){
void *p;
pcache1EnterMutex();
p = pcache1Alloc(sz);
pcache1LeaveMutex();
return p;
return pcache1Alloc(sz);
}
/*
** Free an allocated buffer obtained from sqlite3PageMalloc().
*/
void sqlite3PageFree(void *p){
pcache1EnterMutex();
pcache1Free(p);
pcache1LeaveMutex();
}
/*
** Return true if it desirable to avoid allocating a new page cache
** entry.
**
** If memory was allocated specifically to the page cache using
** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then
** it is desirable to avoid allocating a new page cache entry because
** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient
** for all page cache needs and we should not need to spill the
** allocation onto the heap.
**
** Or, the heap is used for all page cache memory put the heap is
** under memory pressure, then again it is desirable to avoid
** allocating a new page cache entry in order to avoid stressing
** the heap even further.
*/
static int pcache1UnderMemoryPressure(PCache1 *pCache){
if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){
return pcache1.bUnderPressure;
}else{
return sqlite3HeapNearlyFull();
}
}
/******************************************************************************/
@ -282,25 +366,25 @@ void sqlite3PageFree(void *p){
** This function is used to resize the hash table used by the cache passed
** as the first argument.
**
** The global mutex must be held when this function is called.
** The PCache mutex must be held when this function is called.
*/
static int pcache1ResizeHash(PCache1 *p){
PgHdr1 **apNew;
unsigned int nNew;
unsigned int i;
assert( sqlite3_mutex_held(pcache1.mutex) );
assert( sqlite3_mutex_held(p->pGroup->mutex) );
nNew = p->nHash*2;
if( nNew<256 ){
nNew = 256;
}
pcache1LeaveMutex();
pcache1LeaveMutex(p->pGroup);
if( p->nHash ){ sqlite3BeginBenignMalloc(); }
apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew);
if( p->nHash ){ sqlite3EndBenignMalloc(); }
pcache1EnterMutex();
pcache1EnterMutex(p->pGroup);
if( apNew ){
memset(apNew, 0, sizeof(PgHdr1 *)*nNew);
for(i=0; i<p->nHash; i++){
@ -323,25 +407,33 @@ static int pcache1ResizeHash(PCache1 *p){
/*
** This function is used internally to remove the page pPage from the
** global LRU list, if is part of it. If pPage is not part of the global
** PGroup LRU list, if is part of it. If pPage is not part of the PGroup
** LRU list, then this function is a no-op.
**
** The global mutex must be held when this function is called.
** The PGroup mutex must be held when this function is called.
**
** If pPage is NULL then this routine is a no-op.
*/
static void pcache1PinPage(PgHdr1 *pPage){
assert( sqlite3_mutex_held(pcache1.mutex) );
if( pPage && (pPage->pLruNext || pPage==pcache1.pLruTail) ){
PCache1 *pCache;
PGroup *pGroup;
if( pPage==0 ) return;
pCache = pPage->pCache;
pGroup = pCache->pGroup;
assert( sqlite3_mutex_held(pGroup->mutex) );
if( pPage->pLruNext || pPage==pGroup->pLruTail ){
if( pPage->pLruPrev ){
pPage->pLruPrev->pLruNext = pPage->pLruNext;
}
if( pPage->pLruNext ){
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
}
if( pcache1.pLruHead==pPage ){
pcache1.pLruHead = pPage->pLruNext;
if( pGroup->pLruHead==pPage ){
pGroup->pLruHead = pPage->pLruNext;
}
if( pcache1.pLruTail==pPage ){
pcache1.pLruTail = pPage->pLruPrev;
if( pGroup->pLruTail==pPage ){
pGroup->pLruTail = pPage->pLruPrev;
}
pPage->pLruNext = 0;
pPage->pLruPrev = 0;
@ -354,13 +446,14 @@ static void pcache1PinPage(PgHdr1 *pPage){
** Remove the page supplied as an argument from the hash table
** (PCache1.apHash structure) that it is currently stored in.
**
** The global mutex must be held when this function is called.
** The PGroup mutex must be held when this function is called.
*/
static void pcache1RemoveFromHash(PgHdr1 *pPage){
unsigned int h;
PCache1 *pCache = pPage->pCache;
PgHdr1 **pp;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
h = pPage->iKey % pCache->nHash;
for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext);
*pp = (*pp)->pNext;
@ -369,13 +462,14 @@ static void pcache1RemoveFromHash(PgHdr1 *pPage){
}
/*
** If there are currently more than pcache.nMaxPage pages allocated, try
** to recycle pages to reduce the number allocated to pcache.nMaxPage.
** If there are currently more than nMaxPage pages allocated, try
** to recycle pages to reduce the number allocated to nMaxPage.
*/
static void pcache1EnforceMaxPage(void){
assert( sqlite3_mutex_held(pcache1.mutex) );
while( pcache1.nCurrentPage>pcache1.nMaxPage && pcache1.pLruTail ){
PgHdr1 *p = pcache1.pLruTail;
static void pcache1EnforceMaxPage(PGroup *pGroup){
assert( sqlite3_mutex_held(pGroup->mutex) );
while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
PgHdr1 *p = pGroup->pLruTail;
assert( p->pCache->pGroup==pGroup );
pcache1PinPage(p);
pcache1RemoveFromHash(p);
pcache1FreePage(p);
@ -387,15 +481,15 @@ static void pcache1EnforceMaxPage(void){
** greater than or equal to iLimit. Any pinned pages that meet this
** criteria are unpinned before they are discarded.
**
** The global mutex must be held when this function is called.
** The PCache mutex must be held when this function is called.
*/
static void pcache1TruncateUnsafe(
PCache1 *pCache,
unsigned int iLimit
PCache1 *pCache, /* The cache to truncate */
unsigned int iLimit /* Drop pages with this pgno or larger */
){
TESTONLY( unsigned int nPage = 0; ) /* Used to assert pCache->nPage is correct */
TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */
unsigned int h;
assert( sqlite3_mutex_held(pcache1.mutex) );
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
for(h=0; h<pCache->nHash; h++){
PgHdr1 **pp = &pCache->apHash[h];
PgHdr1 *pPage;
@ -425,8 +519,10 @@ static int pcache1Init(void *NotUsed){
assert( pcache1.isInit==0 );
memset(&pcache1, 0, sizeof(pcache1));
if( sqlite3GlobalConfig.bCoreMutex ){
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM);
}
pcache1.grp.mxPinned = 10;
pcache1.isInit = 1;
return SQLITE_OK;
}
@ -448,18 +544,47 @@ static void pcache1Shutdown(void *NotUsed){
** Allocate a new cache.
*/
static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
PCache1 *pCache;
PCache1 *pCache; /* The newly created page cache */
PGroup *pGroup; /* The group the new page cache will belong to */
int sz; /* Bytes of memory required to allocate the new cache */
pCache = (PCache1 *)sqlite3_malloc(sizeof(PCache1));
/*
** The seperateCache variable is true if each PCache has its own private
** PGroup. In other words, separateCache is true for mode (1) where no
** mutexing is required.
**
** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
**
** * Always use a unified cache in single-threaded applications
**
** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off)
** use separate caches (mode-1)
*/
#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
const int separateCache = 0;
#else
int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
#endif
sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
pCache = (PCache1 *)sqlite3_malloc(sz);
if( pCache ){
memset(pCache, 0, sizeof(PCache1));
memset(pCache, 0, sz);
if( separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
pGroup = &pcache1_g.grp;
}
pCache->pGroup = pGroup;
pCache->szPage = szPage;
pCache->bPurgeable = (bPurgeable ? 1 : 0);
if( bPurgeable ){
pCache->nMin = 10;
pcache1EnterMutex();
pcache1.nMinPage += pCache->nMin;
pcache1LeaveMutex();
pcache1EnterMutex(pGroup);
pGroup->nMinPage += pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1LeaveMutex(pGroup);
}
}
return (sqlite3_pcache *)pCache;
@ -473,11 +598,14 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
PCache1 *pCache = (PCache1 *)p;
if( pCache->bPurgeable ){
pcache1EnterMutex();
pcache1.nMaxPage += (nMax - pCache->nMax);
PGroup *pGroup = pCache->pGroup;
pcache1EnterMutex(pGroup);
pGroup->nMaxPage += (nMax - pCache->nMax);
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->nMax = nMax;
pcache1EnforceMaxPage();
pcache1LeaveMutex();
pCache->n90pct = pCache->nMax*9/10;
pcache1EnforceMaxPage(pGroup);
pcache1LeaveMutex(pGroup);
}
}
@ -486,9 +614,10 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
*/
static int pcache1Pagecount(sqlite3_pcache *p){
int n;
pcache1EnterMutex();
n = ((PCache1 *)p)->nPage;
pcache1LeaveMutex();
PCache1 *pCache = (PCache1*)p;
pcache1EnterMutex(pCache->pGroup);
n = pCache->nPage;
pcache1LeaveMutex(pCache->pGroup);
return n;
}
@ -516,14 +645,16 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** 2. If createFlag==0 and the page is not already in the cache, NULL is
** returned.
**
** 3. If createFlag is 1, and the page is not already in the cache,
** and if either of the following are true, return NULL:
** 3. If createFlag is 1, and the page is not already in the cache, then
** return NULL (do not allocate a new page) if any of the following
** conditions are true:
**
** (a) the number of pages pinned by the cache is greater than
** PCache1.nMax, or
**
** (b) the number of pages pinned by the cache is greater than
** the sum of nMax for all purgeable caches, less the sum of
** nMin for all other purgeable caches.
** nMin for all other purgeable caches, or
**
** 4. If none of the first three conditions apply and the cache is marked
** as purgeable, and if one of the following is true:
@ -535,6 +666,9 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** already equal to or greater than the sum of nMax for all
** purgeable caches,
**
** (c) The system is under memory pressure and wants to avoid
** unnecessary pages cache entry allocations
**
** then attempt to recycle a page from the LRU list. If it is the right
** size, return the recycled buffer. Otherwise, free the buffer and
** proceed to step 5.
@ -542,30 +676,50 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** 5. Otherwise, allocate and return a new page buffer.
*/
static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
unsigned int nPinned;
int nPinned;
PCache1 *pCache = (PCache1 *)p;
PGroup *pGroup;
PgHdr1 *pPage = 0;
assert( pCache->bPurgeable || createFlag!=1 );
pcache1EnterMutex();
if( createFlag==1 ) sqlite3BeginBenignMalloc();
assert( pCache->bPurgeable || pCache->nMin==0 );
assert( pCache->bPurgeable==0 || pCache->nMin==10 );
assert( pCache->nMin==0 || pCache->bPurgeable );
pcache1EnterMutex(pGroup = pCache->pGroup);
/* Search the hash table for an existing entry. */
/* Step 1: Search the hash table for an existing entry. */
if( pCache->nHash>0 ){
unsigned int h = iKey % pCache->nHash;
for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext);
}
/* Step 2: Abort if no existing page is found and createFlag is 0 */
if( pPage || createFlag==0 ){
pcache1PinPage(pPage);
goto fetch_out;
}
/* Step 3 of header comment. */
/* The pGroup local variable will normally be initialized by the
** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined,
** then pcache1EnterMutex() is a no-op, so we have to initialize the
** local variable here. Delaying the initialization of pGroup is an
** optimization: The common case is to exit the module before reaching
** this point.
*/
#ifdef SQLITE_MUTEX_OMIT
pGroup = pCache->pGroup;
#endif
/* Step 3: Abort if createFlag is 1 but the cache is nearly full */
nPinned = pCache->nPage - pCache->nRecyclable;
assert( nPinned>=0 );
assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
assert( pCache->n90pct == pCache->nMax*9/10 );
if( createFlag==1 && (
nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage)
|| nPinned>=(pCache->nMax * 9 / 10)
nPinned>=pGroup->mxPinned
|| nPinned>=(int)pCache->n90pct
|| pcache1UnderMemoryPressure(pCache)
)){
goto fetch_out;
}
@ -574,18 +728,22 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
goto fetch_out;
}
/* Step 4. Try to recycle a page buffer if appropriate. */
if( pCache->bPurgeable && pcache1.pLruTail && (
(pCache->nPage+1>=pCache->nMax) || pcache1.nCurrentPage>=pcache1.nMaxPage
/* Step 4. Try to recycle a page. */
if( pCache->bPurgeable && pGroup->pLruTail && (
(pCache->nPage+1>=pCache->nMax)
|| pGroup->nCurrentPage>=pGroup->nMaxPage
|| pcache1UnderMemoryPressure(pCache)
)){
pPage = pcache1.pLruTail;
PCache1 *pOtherCache;
pPage = pGroup->pLruTail;
pcache1RemoveFromHash(pPage);
pcache1PinPage(pPage);
if( pPage->pCache->szPage!=pCache->szPage ){
if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){
pcache1FreePage(pPage);
pPage = 0;
}else{
pcache1.nCurrentPage -= (pPage->pCache->bPurgeable - pCache->bPurgeable);
pGroup->nCurrentPage -=
(pOtherCache->bPurgeable - pCache->bPurgeable);
}
}
@ -593,7 +751,11 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
** attempt to allocate a new one.
*/
if( !pPage ){
if( createFlag==1 ) sqlite3BeginBenignMalloc();
pcache1LeaveMutex(pGroup);
pPage = pcache1AllocPage(pCache);
pcache1EnterMutex(pGroup);
if( createFlag==1 ) sqlite3EndBenignMalloc();
}
if( pPage ){
@ -612,8 +774,7 @@ fetch_out:
if( pPage && iKey>pCache->iMaxKey ){
pCache->iMaxKey = iKey;
}
if( createFlag==1 ) sqlite3EndBenignMalloc();
pcache1LeaveMutex();
pcache1LeaveMutex(pGroup);
return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
}
@ -626,37 +787,34 @@ fetch_out:
static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
PGroup *pGroup = pCache->pGroup;
assert( pPage->pCache==pCache );
pcache1EnterMutex();
pcache1EnterMutex(pGroup);
/* It is an error to call this function if the page is already
** part of the global LRU list.
** part of the PGroup LRU list.
*/
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage );
assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
if( reuseUnlikely || pcache1.nCurrentPage>pcache1.nMaxPage ){
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
pcache1RemoveFromHash(pPage);
pcache1FreePage(pPage);
}else{
/* Add the page to the global LRU list. Normally, the page is added to
** the head of the list (last page to be recycled). However, if the
** reuseUnlikely flag passed to this function is true, the page is added
** to the tail of the list (first page to be recycled).
*/
if( pcache1.pLruHead ){
pcache1.pLruHead->pLruPrev = pPage;
pPage->pLruNext = pcache1.pLruHead;
pcache1.pLruHead = pPage;
/* Add the page to the PGroup LRU list. */
if( pGroup->pLruHead ){
pGroup->pLruHead->pLruPrev = pPage;
pPage->pLruNext = pGroup->pLruHead;
pGroup->pLruHead = pPage;
}else{
pcache1.pLruTail = pPage;
pcache1.pLruHead = pPage;
pGroup->pLruTail = pPage;
pGroup->pLruHead = pPage;
}
pCache->nRecyclable++;
}
pcache1LeaveMutex();
pcache1LeaveMutex(pCache->pGroup);
}
/*
@ -675,7 +833,7 @@ static void pcache1Rekey(
assert( pPage->iKey==iOld );
assert( pPage->pCache==pCache );
pcache1EnterMutex();
pcache1EnterMutex(pCache->pGroup);
h = iOld%pCache->nHash;
pp = &pCache->apHash[h];
@ -692,7 +850,7 @@ static void pcache1Rekey(
pCache->iMaxKey = iNew;
}
pcache1LeaveMutex();
pcache1LeaveMutex(pCache->pGroup);
}
/*
@ -704,12 +862,12 @@ static void pcache1Rekey(
*/
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
PCache1 *pCache = (PCache1 *)p;
pcache1EnterMutex();
pcache1EnterMutex(pCache->pGroup);
if( iLimit<=pCache->iMaxKey ){
pcache1TruncateUnsafe(pCache, iLimit);
pCache->iMaxKey = iLimit-1;
}
pcache1LeaveMutex();
pcache1LeaveMutex(pCache->pGroup);
}
/*
@ -719,12 +877,15 @@ static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
*/
static void pcache1Destroy(sqlite3_pcache *p){
PCache1 *pCache = (PCache1 *)p;
pcache1EnterMutex();
PGroup *pGroup = pCache->pGroup;
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
pcache1EnterMutex(pGroup);
pcache1TruncateUnsafe(pCache, 0);
pcache1.nMaxPage -= pCache->nMax;
pcache1.nMinPage -= pCache->nMin;
pcache1EnforceMaxPage();
pcache1LeaveMutex();
pGroup->nMaxPage -= pCache->nMax;
pGroup->nMinPage -= pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1EnforceMaxPage(pGroup);
pcache1LeaveMutex(pGroup);
sqlite3_free(pCache->apHash);
sqlite3_free(pCache);
}
@ -763,16 +924,18 @@ void sqlite3PCacheSetDefault(void){
*/
int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
assert( sqlite3_mutex_notheld(pcache1.mutex) );
if( pcache1.pStart==0 ){
PgHdr1 *p;
pcache1EnterMutex();
while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
pcache1EnterMutex(&pcache1.grp);
while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
pcache1PinPage(p);
pcache1RemoveFromHash(p);
pcache1FreePage(p);
}
pcache1LeaveMutex();
pcache1LeaveMutex(&pcache1.grp);
}
return nFree;
}
@ -791,12 +954,12 @@ void sqlite3PcacheStats(
){
PgHdr1 *p;
int nRecyclable = 0;
for(p=pcache1.pLruHead; p; p=p->pLruNext){
for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
nRecyclable++;
}
*pnCurrent = pcache1.nCurrentPage;
*pnMax = pcache1.nMaxPage;
*pnMin = pcache1.nMinPage;
*pnCurrent = pcache1.grp.nCurrentPage;
*pnMax = pcache1.grp.nMaxPage;
*pnMin = pcache1.grp.nMinPage;
*pnRecyclable = nRecyclable;
}
#endif

View File

@ -35,7 +35,7 @@ static u8 getSafetyLevel(const char *z){
static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2};
int i, n;
if( sqlite3Isdigit(*z) ){
return (u8)atoi(z);
return (u8)sqlite3Atoi(z);
}
n = sqlite3Strlen30(z);
for(i=0; i<ArraySize(iLength); i++){
@ -76,7 +76,7 @@ static int getAutoVacuum(const char *z){
if( 0==sqlite3StrICmp(z, "none") ) return BTREE_AUTOVACUUM_NONE;
if( 0==sqlite3StrICmp(z, "full") ) return BTREE_AUTOVACUUM_FULL;
if( 0==sqlite3StrICmp(z, "incremental") ) return BTREE_AUTOVACUUM_INCR;
i = atoi(z);
i = sqlite3Atoi(z);
return (u8)((i>=0&&i<=2)?i:0);
}
#endif /* ifndef SQLITE_OMIT_AUTOVACUUM */
@ -172,6 +172,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
{ "empty_result_callbacks", SQLITE_NullCallback },
{ "legacy_file_format", SQLITE_LegacyFileFmt },
{ "fullfsync", SQLITE_FullFSync },
{ "checkpoint_fullfsync", SQLITE_CkptFullFSync },
{ "reverse_unordered_selects", SQLITE_ReverseOrder },
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
{ "automatic_index", SQLITE_AutoIndex },
@ -383,7 +384,7 @@ void sqlite3Pragma(
sqlite3VdbeChangeP1(v, addr+1, iDb);
sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE);
}else{
int size = atoi(zRight);
int size = sqlite3Atoi(zRight);
if( size<0 ) size = -size;
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3VdbeAddOp2(v, OP_Integer, size, 1);
@ -412,35 +413,13 @@ void sqlite3Pragma(
/* Malloc may fail when setting the page-size, as there is an internal
** buffer that the pager module resizes using sqlite3_realloc().
*/
db->nextPagesize = atoi(zRight);
db->nextPagesize = sqlite3Atoi(zRight);
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
db->mallocFailed = 1;
}
}
}else
/*
** PRAGMA [database.]max_page_count
** PRAGMA [database.]max_page_count=N
**
** The first form reports the current setting for the
** maximum number of pages in the database file. The
** second form attempts to change this setting. Both
** forms return the current setting.
*/
if( sqlite3StrICmp(zLeft,"max_page_count")==0 ){
Btree *pBt = pDb->pBt;
int newMax = 0;
assert( pBt!=0 );
if( zRight ){
newMax = atoi(zRight);
}
if( ALWAYS(pBt) ){
newMax = sqlite3BtreeMaxPageCount(pBt, newMax);
}
returnSingleInt(pParse, "max_page_count", newMax);
}else
/*
** PRAGMA [database.]secure_delete
** PRAGMA [database.]secure_delete=ON/OFF
@ -467,19 +446,33 @@ void sqlite3Pragma(
}else
/*
** PRAGMA [database.]max_page_count
** PRAGMA [database.]max_page_count=N
**
** The first form reports the current setting for the
** maximum number of pages in the database file. The
** second form attempts to change this setting. Both
** forms return the current setting.
**
** PRAGMA [database.]page_count
**
** Return the number of pages in the specified database.
*/
if( sqlite3StrICmp(zLeft,"page_count")==0 ){
if( sqlite3StrICmp(zLeft,"page_count")==0
|| sqlite3StrICmp(zLeft,"max_page_count")==0
){
int iReg;
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3CodeVerifySchema(pParse, iDb);
iReg = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
if( zLeft[0]=='p' ){
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
}else{
sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3Atoi(zRight));
}
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "page_count", SQLITE_STATIC);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
}else
/*
@ -587,7 +580,7 @@ void sqlite3Pragma(
Pager *pPager = sqlite3BtreePager(pDb->pBt);
i64 iLimit = -2;
if( zRight ){
sqlite3Atoi64(zRight, &iLimit);
sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8);
if( iLimit<-1 ) iLimit = -1;
}
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
@ -701,7 +694,7 @@ void sqlite3Pragma(
if( !zRight ){
returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
}else{
int size = atoi(zRight);
int size = sqlite3Atoi(zRight);
if( size<0 ) size = -size;
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
@ -1094,7 +1087,7 @@ void sqlite3Pragma(
/* Set the maximum error count */
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
if( zRight ){
mxErr = atoi(zRight);
sqlite3GetInt32(zRight, &mxErr);
if( mxErr<=0 ){
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
}
@ -1351,7 +1344,7 @@ void sqlite3Pragma(
};
int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie);
sqlite3VdbeChangeP1(v, addr, iDb);
sqlite3VdbeChangeP1(v, addr+1, atoi(zRight));
sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight));
sqlite3VdbeChangeP1(v, addr+2, iDb);
sqlite3VdbeChangeP2(v, addr+2, iCookie);
}else{
@ -1412,8 +1405,7 @@ void sqlite3Pragma(
*/
if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){
if( zRight ){
int nAuto = atoi(zRight);
sqlite3_wal_autocheckpoint(db, nAuto);
sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight));
}
returnSingleInt(pParse, "wal_autocheckpoint",
db->xWalCallback==sqlite3WalDefaultHook ?
@ -1533,7 +1525,8 @@ void sqlite3Pragma(
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
if( db->autoCommit ){
sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
(db->flags&SQLITE_FullFSync)!=0);
(db->flags&SQLITE_FullFSync)!=0,
(db->flags&SQLITE_CkptFullFSync)!=0);
}
#endif
pragma_out:

View File

@ -79,7 +79,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
assert( db->init.busy );
db->init.iDb = iDb;
db->init.newTnum = atoi(argv[1]);
db->init.newTnum = sqlite3Atoi(argv[1]);
db->init.orphanTrigger = 0;
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
rc = db->errCode;
@ -628,13 +628,13 @@ static int sqlite3Prepare(
if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
"order", "from", "detail"
"selectid", "order", "from", "detail"
};
int iFirst, mx;
if( pParse->explain==2 ){
sqlite3VdbeSetNumCols(pParse->pVdbe, 3);
sqlite3VdbeSetNumCols(pParse->pVdbe, 4);
iFirst = 8;
mx = 11;
mx = 12;
}else{
sqlite3VdbeSetNumCols(pParse->pVdbe, 8);
iFirst = 0;
@ -784,7 +784,7 @@ int sqlite3_prepare_v2(
*/
static int sqlite3Prepare16(
sqlite3 *db, /* Database handle. */
const void *zSql, /* UTF-8 encoded SQL statement. */
const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
int saveSqlFlag, /* True to save SQL text into the sqlite3_stmt */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
@ -834,7 +834,7 @@ static int sqlite3Prepare16(
*/
int sqlite3_prepare16(
sqlite3 *db, /* Database handle. */
const void *zSql, /* UTF-8 encoded SQL statement. */
const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const void **pzTail /* OUT: End of parsed string */
@ -846,7 +846,7 @@ int sqlite3_prepare16(
}
int sqlite3_prepare16_v2(
sqlite3 *db, /* Database handle. */
const void *zSql, /* UTF-8 encoded SQL statement. */
const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const void **pzTail /* OUT: End of parsed string */

View File

@ -763,6 +763,7 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
return;
}
}else{
char *zOld = (p->zText==p->zBase ? 0 : p->zText);
i64 szNew = p->nChar;
szNew += N + 1;
if( szNew > p->mxAlloc ){
@ -773,13 +774,12 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
p->nAlloc = (int)szNew;
}
if( p->useMalloc==1 ){
zNew = sqlite3DbMallocRaw(p->db, p->nAlloc );
zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
}else{
zNew = sqlite3_malloc(p->nAlloc);
zNew = sqlite3_realloc(zOld, p->nAlloc);
}
if( zNew ){
memcpy(zNew, p->zText, p->nChar);
sqlite3StrAccumReset(p);
if( zOld==0 ) memcpy(zNew, p->zText, p->nChar);
p->zText = zNew;
}else{
p->mallocFailed = 1;
@ -934,21 +934,28 @@ char *sqlite3_mprintf(const char *zFormat, ...){
** current locale settings. This is important for SQLite because we
** are not able to use a "," as the decimal point in place of "." as
** specified by some locales.
**
** Oops: The first two arguments of sqlite3_snprintf() are backwards
** from the snprintf() standard. Unfortunately, it is too late to change
** this without breaking compatibility, so we just have to live with the
** mistake.
**
** sqlite3_vsnprintf() is the varargs version.
*/
char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
StrAccum acc;
if( n<=0 ) return zBuf;
sqlite3StrAccumInit(&acc, zBuf, n, 0);
acc.useMalloc = 0;
sqlite3VXPrintf(&acc, 0, zFormat, ap);
return sqlite3StrAccumFinish(&acc);
}
char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
char *z;
va_list ap;
StrAccum acc;
if( n<=0 ){
return zBuf;
}
sqlite3StrAccumInit(&acc, zBuf, n, 0);
acc.useMalloc = 0;
va_start(ap,zFormat);
sqlite3VXPrintf(&acc, 0, zFormat, ap);
z = sqlite3_vsnprintf(n, zBuf, zFormat, ap);
va_end(ap);
z = sqlite3StrAccumFinish(&acc);
return z;
}

View File

@ -442,7 +442,6 @@ static void pushOntoSorter(
sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor);
sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor);
sqlite3VdbeJumpHere(v, addr2);
pSelect->iLimit = 0;
}
}
@ -491,11 +490,13 @@ static void codeDistinct(
sqlite3ReleaseTempReg(pParse, r1);
}
#ifndef SQLITE_OMIT_SUBQUERY
/*
** Generate an error message when a SELECT is used within a subexpression
** (example: "a IN (SELECT * FROM table)") but it has more than 1 result
** column. We do this in a subroutine because the error occurs in multiple
** places.
** column. We do this in a subroutine because the error used to occur
** in multiple places. (The error only occurs in one place now, but we
** retain the subroutine to minimize code disruption.)
*/
static int checkForMultiColumnSelectError(
Parse *pParse, /* Parse context. */
@ -511,6 +512,7 @@ static int checkForMultiColumnSelectError(
return 0;
}
}
#endif
/*
** This routine generates the code for the inside of the inner loop
@ -590,10 +592,6 @@ static void selectInnerLoop(
}
}
if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
return;
}
switch( eDest ){
/* In this mode, write each query result to the key of the temporary
** table iParm.
@ -722,11 +720,11 @@ static void selectInnerLoop(
#endif
}
/* Jump to the end of the loop if the LIMIT is reached.
/* Jump to the end of the loop if the LIMIT is reached. Except, if
** there is a sorter, in which case the sorter has already limited
** the output for us.
*/
if( p->iLimit ){
assert( pOrderBy==0 ); /* If there is an ORDER BY, the call to
** pushOntoSorter() would have cleared p->iLimit */
if( pOrderBy==0 && p->iLimit ){
sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1);
}
}
@ -773,6 +771,88 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
return pInfo;
}
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** Name of the connection operator, used for error messages.
*/
static const char *selectOpName(int id){
char *z;
switch( id ){
case TK_ALL: z = "UNION ALL"; break;
case TK_INTERSECT: z = "INTERSECT"; break;
case TK_EXCEPT: z = "EXCEPT"; break;
default: z = "UNION"; break;
}
return z;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
#ifndef SQLITE_OMIT_EXPLAIN
/*
** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
** is a no-op. Otherwise, it adds a single row of output to the EQP result,
** where the caption is of the form:
**
** "USE TEMP B-TREE FOR xxx"
**
** where xxx is one of "DISTINCT", "ORDER BY" or "GROUP BY". Exactly which
** is determined by the zUsage argument.
*/
static void explainTempTable(Parse *pParse, const char *zUsage){
if( pParse->explain==2 ){
Vdbe *v = pParse->pVdbe;
char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage);
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
}
}
/*
** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
** is a no-op. Otherwise, it adds a single row of output to the EQP result,
** where the caption is of one of the two forms:
**
** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)"
** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)"
**
** where iSub1 and iSub2 are the integers passed as the corresponding
** function parameters, and op is the text representation of the parameter
** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT,
** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is
** false, or the second form if it is true.
*/
static void explainComposite(
Parse *pParse, /* Parse context */
int op, /* One of TK_UNION, TK_EXCEPT etc. */
int iSub1, /* Subquery id 1 */
int iSub2, /* Subquery id 2 */
int bUseTmp /* True if a temp table was used */
){
assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL );
if( pParse->explain==2 ){
Vdbe *v = pParse->pVdbe;
char *zMsg = sqlite3MPrintf(
pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2,
bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op)
);
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
}
}
/*
** Assign expression b to lvalue a. A second, no-op, version of this macro
** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code
** in sqlite3Select() to assign values to structure member variables that
** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the
** code with #ifndef directives.
*/
# define explainSetInteger(a, b) a = b
#else
/* No-op versions of the explainXXX() functions and macros. */
# define explainTempTable(y,z)
# define explainComposite(v,w,x,y,z)
# define explainSetInteger(y,z)
#endif
/*
** If the inner loop was generated using a non-null pOrderBy argument,
@ -861,10 +941,6 @@ static void generateSortTail(
sqlite3ReleaseTempReg(pParse, regRow);
sqlite3ReleaseTempReg(pParse, regRowid);
/* LIMIT has been implemented by the pushOntoSorter() routine.
*/
assert( p->iLimit==0 );
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, addrContinue);
@ -1124,22 +1200,6 @@ static void generateColumnNames(
generateColumnTypes(pParse, pTabList, pEList);
}
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** Name of the connection operator, used for error messages.
*/
static const char *selectOpName(int id){
char *z;
switch( id ){
case TK_ALL: z = "UNION ALL"; break;
case TK_INTERSECT: z = "INTERSECT"; break;
case TK_EXCEPT: z = "EXCEPT"; break;
default: z = "UNION"; break;
}
return z;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/*
** Given a an expression list (which is really the list of expressions
** that form the result set of a SELECT statement) compute appropriate
@ -1302,6 +1362,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
assert( db->lookaside.bEnabled==0 );
pTab->nRef = 1;
pTab->zName = 0;
pTab->nRowEst = 1000000;
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect);
pTab->iPKey = -1;
@ -1372,6 +1433,8 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
VdbeComment((v, "LIMIT counter"));
if( n==0 ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
}else{
if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n;
}
}else{
sqlite3ExprCode(pParse, p->pLimit, iLimit);
@ -1472,6 +1535,10 @@ static int multiSelect(
SelectDest dest; /* Alternative data destination */
Select *pDelete = 0; /* Chain of simple selects to delete */
sqlite3 *db; /* Database connection */
#ifndef SQLITE_OMIT_EXPLAIN
int iSub1; /* EQP id of left-hand query */
int iSub2; /* EQP id of right-hand query */
#endif
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
@ -1503,6 +1570,7 @@ static int multiSelect(
if( dest.eDest==SRT_EphemTab ){
assert( p->pEList );
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr);
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
dest.eDest = SRT_Table;
}
@ -1528,9 +1596,11 @@ static int multiSelect(
switch( p->op ){
case TK_ALL: {
int addr = 0;
int nLimit;
assert( !pPrior->pLimit );
pPrior->pLimit = p->pLimit;
pPrior->pOffset = p->pOffset;
explainSetInteger(iSub1, pParse->iNextSelectId);
rc = sqlite3Select(pParse, pPrior, &dest);
p->pLimit = 0;
p->pOffset = 0;
@ -1544,10 +1614,18 @@ static int multiSelect(
addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit);
VdbeComment((v, "Jump ahead if LIMIT reached"));
}
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &dest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
p->pPrior = pPrior;
p->nSelectRow += pPrior->nSelectRow;
if( pPrior->pLimit
&& sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
&& p->nSelectRow > (double)nLimit
){
p->nSelectRow = (double)nLimit;
}
if( addr ){
sqlite3VdbeJumpHere(v, addr);
}
@ -1591,6 +1669,7 @@ static int multiSelect(
*/
assert( !pPrior->pOrderBy );
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
explainSetInteger(iSub1, pParse->iNextSelectId);
rc = sqlite3Select(pParse, pPrior, &uniondest);
if( rc ){
goto multi_select_end;
@ -1610,6 +1689,7 @@ static int multiSelect(
pOffset = p->pOffset;
p->pOffset = 0;
uniondest.eDest = op;
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &uniondest);
testcase( rc!=SQLITE_OK );
/* Query flattening in sqlite3Select() might refill p->pOrderBy.
@ -1618,6 +1698,7 @@ static int multiSelect(
pDelete = p->pPrior;
p->pPrior = pPrior;
p->pOrderBy = 0;
if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow;
sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->pOffset = pOffset;
@ -1675,6 +1756,7 @@ static int multiSelect(
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
explainSetInteger(iSub1, pParse->iNextSelectId);
rc = sqlite3Select(pParse, pPrior, &intersectdest);
if( rc ){
goto multi_select_end;
@ -1691,10 +1773,12 @@ static int multiSelect(
pOffset = p->pOffset;
p->pOffset = 0;
intersectdest.iParm = tab2;
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &intersectdest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
p->pPrior = pPrior;
if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->pOffset = pOffset;
@ -1727,6 +1811,8 @@ static int multiSelect(
}
}
explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL);
/* Compute collating sequences used by
** temporary tables needed to implement the compound select.
** Attach the KeyInfo structure to all temporary tables.
@ -2070,6 +2156,10 @@ static int multiSelectOrderBy(
ExprList *pOrderBy; /* The ORDER BY clause */
int nOrderBy; /* Number of terms in the ORDER BY clause */
int *aPermute; /* Mapping from ORDER BY terms to result set columns */
#ifndef SQLITE_OMIT_EXPLAIN
int iSub1; /* EQP id of left-hand query */
int iSub2; /* EQP id of right-hand query */
#endif
assert( p->pOrderBy!=0 );
assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */
@ -2181,7 +2271,6 @@ static int multiSelectOrderBy(
/* Separate the left and the right query from one another
*/
p->pPrior = 0;
pPrior->pRightmost = 0;
sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
if( pPrior->pPrior==0 ){
sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
@ -2224,6 +2313,7 @@ static int multiSelectOrderBy(
*/
VdbeNoopComment((v, "Begin coroutine for left SELECT"));
pPrior->iLimit = regLimitA;
explainSetInteger(iSub1, pParse->iNextSelectId);
sqlite3Select(pParse, pPrior, &destA);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA);
sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
@ -2238,6 +2328,7 @@ static int multiSelectOrderBy(
savedOffset = p->iOffset;
p->iLimit = regLimitB;
p->iOffset = 0;
explainSetInteger(iSub2, pParse->iNextSelectId);
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
@ -2274,6 +2365,7 @@ static int multiSelectOrderBy(
sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA);
p->nSelectRow += pPrior->nSelectRow;
}
/* Generate a subroutine to run when the results from select B
@ -2281,6 +2373,7 @@ static int multiSelectOrderBy(
*/
if( op==TK_INTERSECT ){
addrEofB = addrEofA;
if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
}else{
VdbeNoopComment((v, "eof-B subroutine"));
addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd);
@ -2368,6 +2461,7 @@ static int multiSelectOrderBy(
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
explainComposite(pParse, p->op, iSub1, iSub2, 0);
return SQLITE_OK;
}
#endif
@ -3101,6 +3195,7 @@ static int selectExpander(Walker *pWalker, Select *p){
while( pSel->pPrior ){ pSel = pSel->pPrior; }
selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
pTab->iPKey = -1;
pTab->nRowEst = 1000000;
pTab->tabFlags |= TF_Ephemeral;
#endif
}else{
@ -3465,7 +3560,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0);
sqlite3ExprCodeExprList(pParse, pList, regAgg, 1);
}else{
nArg = 0;
regAgg = 0;
@ -3594,6 +3689,11 @@ int sqlite3Select(
int iEnd; /* Address of the end of the query */
sqlite3 *db; /* The database connection */
#ifndef SQLITE_OMIT_EXPLAIN
int iRestoreSelectId = pParse->iSelectId;
pParse->iSelectId = pParse->iNextSelectId++;
#endif
db = pParse->db;
if( p==0 || db->mallocFailed || pParse->nErr ){
return 1;
@ -3625,6 +3725,15 @@ int sqlite3Select(
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto select_end;
/* If writing to memory or generating a set
** only a single column may be output.
*/
#ifndef SQLITE_OMIT_SUBQUERY
if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
goto select_end;
}
#endif
/* Generate code for all sub-queries in the FROM clause
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
@ -3656,8 +3765,10 @@ int sqlite3Select(
}else{
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
assert( pItem->isPopulated==0 );
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
pItem->isPopulated = 1;
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
}
if( /*pParse->nErr ||*/ db->mallocFailed ){
goto select_end;
@ -3691,19 +3802,12 @@ int sqlite3Select(
mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT];
if( mxSelect && cnt>mxSelect ){
sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
return 1;
goto select_end;
}
}
return multiSelect(pParse, p, pDest);
}
#endif
/* If writing to memory or generating a set
** only a single column may be output.
*/
#ifndef SQLITE_OMIT_SUBQUERY
if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
goto select_end;
rc = multiSelect(pParse, p, pDest);
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
return rc;
}
#endif
@ -3715,7 +3819,6 @@ int sqlite3Select(
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
pGroupBy = p->pGroupBy;
p->selFlags &= ~SF_Distinct;
isDistinct = 0;
}
/* If there is both a GROUP BY and an ORDER BY clause and they are
@ -3758,17 +3861,19 @@ int sqlite3Select(
/* Set the limiter.
*/
iEnd = sqlite3VdbeMakeLabel(v);
p->nSelectRow = (double)LARGEST_INT64;
computeLimitRegisters(pParse, p, iEnd);
/* Open a virtual index to use for the distinct set.
*/
if( isDistinct ){
if( p->selFlags & SF_Distinct ){
KeyInfo *pKeyInfo;
assert( isAgg || pGroupBy );
distinct = pParse->nTab++;
pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
}else{
distinct = -1;
}
@ -3780,6 +3885,7 @@ int sqlite3Select(
*/
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
if( pWInfo==0 ) goto select_end;
if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
@ -3824,6 +3930,9 @@ int sqlite3Select(
for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){
pItem->iAlias = 0;
}
if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100;
}else{
p->nSelectRow = (double)1;
}
@ -3920,6 +4029,9 @@ int sqlite3Select(
int nCol;
int nGroupBy;
explainTempTable(pParse,
isDistinct && !(p->selFlags&SF_Distinct)?"DISTINCT":"GROUP BY");
groupBySort = 1;
nGroupBy = pGroupBy->nExpr;
nCol = nGroupBy + 1;
@ -4181,10 +4293,15 @@ int sqlite3Select(
} /* endif aggregate query */
if( distinct>=0 ){
explainTempTable(pParse, "DISTINCT");
}
/* If there is an ORDER BY clause, then we need to sort the results
** and send them to the callback one by one.
*/
if( pOrderBy ){
explainTempTable(pParse, "ORDER BY");
generateSortTail(pParse, p, v, pEList->nExpr, pDest);
}
@ -4201,6 +4318,7 @@ int sqlite3Select(
** successful coding of the SELECT.
*/
select_end:
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
/* Identify column names if results of the SELECT are to be output.
*/

View File

@ -38,10 +38,14 @@
# include <unistd.h>
#endif
#ifdef HAVE_EDITLINE
# include <editline/editline.h>
#endif
#if defined(HAVE_READLINE) && HAVE_READLINE==1
# include <readline/readline.h>
# include <readline/history.h>
#else
#endif
#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
# define readline(p) local_getline(p,stdin)
# define add_history(X)
# define read_history(X)
@ -980,7 +984,7 @@ static int display_stats(
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 Allocations: %d (max %d)\n", iCur, iHiwtr);
fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr);
/*
** Not currently used by the CLI.
** iHiwtr = iCur = -1;
@ -1019,6 +1023,12 @@ static int display_stats(
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);
fprintf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr);
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);
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);
@ -2538,6 +2548,7 @@ int main(int argc, char **argv){
/* 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,
** and the first command to execute.
*/
for(i=1; i<argc-1; i++){
@ -2556,6 +2567,22 @@ int main(int argc, char **argv){
*/
}else if( strcmp(argv[i],"-batch")==0 ){
stdin_is_interactive = 0;
}else if( strcmp(argv[i],"-heap")==0 ){
int j, c;
const char *zSize;
sqlite3_int64 szHeap;
zSize = argv[++i];
szHeap = atoi(zSize);
for(j=0; (c = zSize[j])!=0; j++){
if( c=='M' ){ szHeap *= 1000000; break; }
if( c=='K' ){ szHeap *= 1000; break; }
if( c=='G' ){ szHeap *= 1000000000; break; }
}
if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
#endif
}
}
if( i<argc ){
@ -2662,6 +2689,8 @@ int main(int argc, char **argv){
stdin_is_interactive = 1;
}else if( strcmp(z,"-batch")==0 ){
stdin_is_interactive = 0;
}else if( strcmp(z,"-heap")==0 ){
i++;
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
usage(1);
}else{
@ -2723,11 +2752,7 @@ int main(int argc, char **argv){
}
set_table_name(&data, 0);
if( data.db ){
if( sqlite3_close(data.db)!=SQLITE_OK ){
fprintf(stderr,"Error: cannot close database \"%s\"\n",
sqlite3_errmsg(db));
rc++;
}
sqlite3_close(data.db);
}
return rc;
}

File diff suppressed because it is too large Load Diff

View File

@ -191,6 +191,27 @@ struct sqlite3_api_routines {
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
};
/*
@ -370,6 +391,27 @@ struct sqlite3_api_routines {
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#endif /* SQLITE_CORE */
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;

View File

@ -114,15 +114,21 @@
#endif
/*
** The SQLITE_THREADSAFE macro must be defined as either 0 or 1.
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe. 1 means the library is serialized which is the highest
** level of threadsafety. 2 means the libary is multithreaded - multiple
** threads can use SQLite as long as no two threads try to use the same
** database connection at the same time.
**
** Older versions of SQLite used an optional THREADSAFE macro.
** We support that for legacy
** We support that for legacy.
*/
#if !defined(SQLITE_THREADSAFE)
#if defined(THREADSAFE)
# define SQLITE_THREADSAFE THREADSAFE
#else
# define SQLITE_THREADSAFE 1
# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */
#endif
#endif
@ -600,6 +606,7 @@ typedef struct Expr Expr;
typedef struct ExprList ExprList;
typedef struct ExprSpan ExprSpan;
typedef struct FKey FKey;
typedef struct FuncDestructor FuncDestructor;
typedef struct FuncDef FuncDef;
typedef struct FuncDefHash FuncDefHash;
typedef struct IdList IdList;
@ -730,6 +737,7 @@ struct Lookaside {
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
int nOut; /* Number of buffers currently checked out */
int mxOut; /* Highwater mark for nOut */
int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */
LookasideSlot *pFree; /* List of available buffers */
void *pStart; /* First byte of available memory space */
void *pEnd; /* First byte past end of available space */
@ -808,6 +816,7 @@ struct sqlite3 {
struct Vdbe *pVdbe; /* List of active virtual machines */
int activeVdbeCnt; /* Number of VDBEs currently executing */
int writeVdbeCnt; /* Number of active VDBEs that are writing */
int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
void (*xTrace)(void*,const char*); /* Trace function */
void *pTraceArg; /* Argument to the trace function */
void (*xProfile)(void*,const char*,u64); /* Profiling function */
@ -907,13 +916,14 @@ struct sqlite3 {
#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */
#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */
#define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */
#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
#define SQLITE_CkptFullFSync 0x00400000 /* Use full fsync for checkpoint */
#define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */
#define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */
#define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */
#define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */
#define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */
#define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */
#define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */
/*
** Bits of the sqlite3.flags field that are used by the
@ -926,6 +936,7 @@ struct sqlite3 {
#define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */
#define SQLITE_IndexCover 0x10 /* Disable index covering table */
#define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */
#define SQLITE_FactorOutConst 0x40 /* Disable factoring out constants */
#define SQLITE_OptMask 0xff /* Mask of all disablable opts */
/*
@ -956,6 +967,27 @@ struct FuncDef {
void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */
char *zName; /* SQL name of the function. */
FuncDef *pHash; /* Next with a different name but the same hash */
FuncDestructor *pDestructor; /* Reference counted destructor function */
};
/*
** This structure encapsulates a user-function destructor callback (as
** configured using create_function_v2()) and a reference counter. When
** create_function_v2() is called to create a function with a destructor,
** a single object of this type is allocated. FuncDestructor.nRef is set to
** the number of FuncDef objects created (either 1 or 3, depending on whether
** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor
** member of each of the new FuncDef objects is set to point to the allocated
** FuncDestructor.
**
** Thereafter, when one of the FuncDef objects is deleted, the reference
** count on this object is decremented. When it reaches 0, the destructor
** is invoked and the FuncDestructor structure freed.
*/
struct FuncDestructor {
int nRef;
void (*xDestroy)(void *);
void *pUserData;
};
/*
@ -996,15 +1028,15 @@ struct FuncDef {
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0}
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
pArg, 0, xFunc, 0, 0, #zName, 0}
pArg, 0, xFunc, 0, 0, #zName, 0, 0}
#define LIKEFUNC(zName, nArg, arg, flags) \
{nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0}
{nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
{nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0}
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
/*
** All current savepoints are stored in a linked list starting at
@ -1224,6 +1256,7 @@ struct Table {
Column *aCol; /* Information about each column */
Index *pIndex; /* List of SQL indexes on this table. */
int tnum; /* Root BTree node for this table (see note above) */
unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
Select *pSelect; /* NULL for tables. Points to definition if a view. */
u16 nRef; /* Number of pointers to this Table */
u8 tabFlags; /* Mask of TF_* values */
@ -1792,6 +1825,9 @@ struct SrcList {
u8 isPopulated; /* Temporary table associated with SELECT is populated */
u8 jointype; /* Type of join between this able and the previous */
u8 notIndexed; /* True if there is a NOT INDEXED clause */
#ifndef SQLITE_OMIT_EXPLAIN
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
#endif
int iCursor; /* The VDBE cursor number used to access this table */
Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */
@ -1830,6 +1866,7 @@ struct SrcList {
struct WherePlan {
u32 wsFlags; /* WHERE_* flags that describe the strategy */
u32 nEq; /* Number of == constraints */
double nRow; /* Estimated number of rows (for EQP) */
union {
Index *pIdx; /* Index when WHERE_INDEXED is true */
struct WhereTerm *pTerm; /* WHERE clause term for OR-search */
@ -1914,6 +1951,7 @@ struct WhereInfo {
int nLevel; /* Number of nested loop */
struct WhereClause *pWC; /* Decomposition of the WHERE clause */
double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
double nRowOut; /* Estimated number of output rows */
WhereLevel a[1]; /* Information about each nest loop in WHERE */
};
@ -1989,6 +2027,7 @@ struct Select {
Expr *pOffset; /* OFFSET expression. NULL means not used. */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
double nSelectRow; /* Estimated number of result rows */
};
/*
@ -2184,6 +2223,11 @@ struct Parse {
int nHeight; /* Expression tree height of current sub-select */
Table *pZombieTab; /* List of Table objects to delete after code gen */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
#ifndef SQLITE_OMIT_EXPLAIN
int iSelectId;
int iNextSelectId;
#endif
};
#ifdef SQLITE_OMIT_VIRTUALTABLE
@ -2478,7 +2522,6 @@ int sqlite3CantopenError(int);
** Internal function prototypes
*/
int sqlite3StrICmp(const char *, const char *);
int sqlite3IsNumber(const char*, int*, u8);
int sqlite3Strlen30(const char*);
#define sqlite3StrNICmp sqlite3_strnicmp
@ -2502,7 +2545,7 @@ void *sqlite3PageMalloc(int);
void sqlite3PageFree(void*);
void sqlite3MemSetDefault(void);
void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
int sqlite3MemoryAlarm(void (*)(void*, sqlite3_int64, int), void*, sqlite3_int64);
int sqlite3HeapNearlyFull(void);
/*
** On systems with ample stack space and that support alloca(), make
@ -2673,7 +2716,6 @@ void sqlite3ExprCachePop(Parse*, int);
void sqlite3ExprCacheRemove(Parse*, int, int);
void sqlite3ExprCacheClear(Parse*);
void sqlite3ExprCacheAffinityChange(Parse*, int, int);
void sqlite3ExprHardCopy(Parse*,int,int);
int sqlite3ExprCode(Parse*, Expr*, int);
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
int sqlite3ExprCodeTarget(Parse*, Expr*, int);
@ -2793,17 +2835,15 @@ void sqlite3DeferForeignKey(Parse*, int);
#endif
void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
void sqlite3Detach(Parse*, Expr*);
int sqlite3BtreeFactory(sqlite3 *db, const char *zFilename,
int omitJournal, int nCache, int flags, Btree **ppBtree);
int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
int sqlite3FixSrcList(DbFixer*, SrcList*);
int sqlite3FixSelect(DbFixer*, Select*);
int sqlite3FixExpr(DbFixer*, Expr*);
int sqlite3FixExprList(DbFixer*, ExprList*);
int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
int sqlite3AtoF(const char *z, double*);
int sqlite3AtoF(const char *z, double*, int, u8);
int sqlite3GetInt32(const char *, int*);
int sqlite3FitsIn64Bits(const char *, int);
int sqlite3Atoi(const char*);
int sqlite3Utf16ByteLen(const void *pData, int nChar);
int sqlite3Utf8CharLen(const char *pData, int nByte);
int sqlite3Utf8Read(const u8*, const u8**);
@ -2849,7 +2889,7 @@ void sqlite3TableAffinityStr(Vdbe *, Table *);
char sqlite3CompareAffinity(Expr *pExpr, char aff2);
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
char sqlite3ExprAffinity(Expr *pExpr);
int sqlite3Atoi64(const char*, i64*);
int sqlite3Atoi64(const char*, i64*, int, u8);
void sqlite3Error(sqlite3*, int, const char*,...);
void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
@ -2920,7 +2960,9 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*));
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
FuncDestructor *pDestructor
);
int sqlite3ApiExit(sqlite3 *db, int);
int sqlite3OpenTempDatabase(Parse *);

View File

@ -116,6 +116,22 @@ int sqlite3_db_status(
break;
}
case SQLITE_DBSTATUS_LOOKASIDE_HIT:
case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE:
case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: {
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT );
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
*pCurrent = 0;
*pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT];
if( resetFlag ){
db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
}
break;
}
/*
** Return an approximation for the amount of memory currently used
** by all pagers associated with the given database connection. The

View File

@ -670,6 +670,7 @@ static void DbUpdateHandler(
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1));
Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
}
static void tclCollateNeeded(
@ -3014,22 +3015,31 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** if the extension only supplies one new name!) The "sqlite" command is
** used to open a new SQLite database. See the DbMain() routine above
** for additional information.
**
** The EXTERN macros are required by TCL in order to work on windows.
*/
int Sqlite3_Init(Tcl_Interp *interp){
EXTERN int Sqlite3_Init(Tcl_Interp *interp){
Tcl_InitStubs(interp, "8.4", 0);
Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION);
#ifndef SQLITE_3_SUFFIX_ONLY
/* The "sqlite" alias is undocumented. It is here only to support
** legacy scripts. All new scripts should use only the "sqlite3"
** command.
*/
Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
Tcl_PkgProvide(interp, "sqlite", PACKAGE_VERSION);
#endif
return TCL_OK;
}
int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
#ifndef SQLITE_3_SUFFIX_ONLY
@ -3567,6 +3577,15 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitetestintarray_Init(Tcl_Interp*);
extern int Sqlitetestvfs_Init(Tcl_Interp *);
extern int SqlitetestStat_Init(Tcl_Interp*);
extern int Sqlitetestrtree_Init(Tcl_Interp*);
extern int Sqlitequota_Init(Tcl_Interp*);
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
#ifdef SQLITE_ENABLE_ZIPVFS
extern int Zipvfs_Init(Tcl_Interp*);
Zipvfs_Init(interp);
#endif
Sqliteconfig_Init(interp);
Sqlitetest1_Init(interp);
@ -3595,6 +3614,10 @@ static void init_all(Tcl_Interp *interp){
Sqlitetestintarray_Init(interp);
Sqlitetestvfs_Init(interp);
SqlitetestStat_Init(interp);
Sqlitetestrtree_Init(interp);
Sqlitequota_Init(interp);
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);

View File

@ -1235,7 +1235,7 @@ static int sqlite3_mprintf_int64(
return TCL_ERROR;
}
for(i=2; i<5; i++){
if( !sqlite3Atoi64(argv[i], &a[i-2]) ){
if( sqlite3Atoi64(argv[i], &a[i-2], 1000000, SQLITE_UTF8) ){
Tcl_AppendResult(interp, "argument is not a valid 64-bit integer", 0);
return TCL_ERROR;
}
@ -1590,6 +1590,81 @@ static int test_table_column_metadata(
#ifndef SQLITE_OMIT_INCRBLOB
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{
int notUsed;
Tcl_Channel channel;
ClientData instanceData;
channel = Tcl_GetChannel(interp, z, &notUsed);
if( !channel ) return TCL_ERROR;
Tcl_Flush(channel);
Tcl_Seek(channel, 0, SEEK_SET);
instanceData = Tcl_GetChannelInstanceData(channel);
*ppBlob = *((sqlite3_blob **)instanceData);
}
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
**
@ -1611,13 +1686,10 @@ static int test_blob_read(
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
Tcl_Channel channel;
ClientData instanceData;
sqlite3_blob *pBlob;
int notUsed;
int nByte;
int iOffset;
unsigned char *zBuf;
unsigned char *zBuf = 0;
int rc;
if( objc!=4 ){
@ -1625,19 +1697,16 @@ static int test_blob_read(
return TCL_ERROR;
}
channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
if( !channel
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
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)
|| nByte<0 || iOffset<0
){
return TCL_ERROR;
}
instanceData = Tcl_GetChannelInstanceData(channel);
pBlob = *((sqlite3_blob **)instanceData);
zBuf = (unsigned char *)Tcl_Alloc(nByte);
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));
@ -1669,10 +1738,7 @@ static int test_blob_write(
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
Tcl_Channel channel;
ClientData instanceData;
sqlite3_blob *pBlob;
int notUsed;
int iOffset;
int rc;
@ -1684,14 +1750,11 @@ static int test_blob_write(
return TCL_ERROR;
}
channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
if( !channel || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){
return TCL_ERROR;
}
instanceData = Tcl_GetChannelInstanceData(channel);
pBlob = *((sqlite3_blob **)instanceData);
zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
return TCL_ERROR;
@ -1703,6 +1766,33 @@ static int test_blob_write(
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 */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
Tcl_WideInt iRowid;
sqlite3_blob *pBlob;
int rc;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL ROWID");
return TCL_ERROR;
}
if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
if( Tcl_GetWideIntFromObj(interp, objv[2], &iRowid) ) return TCL_ERROR;
rc = sqlite3_blob_reopen(pBlob, iRowid);
if( rc!=SQLITE_OK ){
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
}
return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
}
#endif
/*
@ -1790,6 +1880,129 @@ static int test_create_collation_v2(
return TCL_OK;
}
/*
** USAGE: sqlite3_create_function_v2 DB NAME NARG ENC ?SWITCHES?
**
** Available switches are:
**
** -func SCRIPT
** -step SCRIPT
** -final SCRIPT
** -destroy SCRIPT
*/
typedef struct CreateFunctionV2 CreateFunctionV2;
struct CreateFunctionV2 {
Tcl_Interp *interp;
Tcl_Obj *pFunc; /* Script for function invocation */
Tcl_Obj *pStep; /* Script for agg. step invocation */
Tcl_Obj *pFinal; /* Script for agg. finalization invocation */
Tcl_Obj *pDestroy; /* Destructor script */
};
static void cf2Func(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
}
static void cf2Step(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
}
static void cf2Final(sqlite3_context *ctx){
}
static void cf2Destroy(void *pUser){
CreateFunctionV2 *p = (CreateFunctionV2 *)pUser;
if( p->interp && p->pDestroy ){
int rc = Tcl_EvalObjEx(p->interp, p->pDestroy, 0);
if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp);
}
if( p->pFunc ) Tcl_DecrRefCount(p->pFunc);
if( p->pStep ) Tcl_DecrRefCount(p->pStep);
if( p->pFinal ) Tcl_DecrRefCount(p->pFinal);
if( p->pDestroy ) Tcl_DecrRefCount(p->pDestroy);
sqlite3_free(p);
}
static int test_create_function_v2(
ClientData clientData, /* Not used */
Tcl_Interp *interp, /* The invoking TCL interpreter */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
const char *zFunc;
int nArg;
int enc;
CreateFunctionV2 *p;
int i;
int rc;
struct EncTable {
const char *zEnc;
int enc;
} aEnc[] = {
{"utf8", SQLITE_UTF8 },
{"utf16", SQLITE_UTF16 },
{"utf16le", SQLITE_UTF16LE },
{"utf16be", SQLITE_UTF16BE },
{"any", SQLITE_ANY },
{"0", 0 }
};
if( objc<5 || (objc%2)==0 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB NAME NARG ENC SWITCHES...");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
zFunc = Tcl_GetString(objv[2]);
if( Tcl_GetIntFromObj(interp, objv[3], &nArg) ) return TCL_ERROR;
if( Tcl_GetIndexFromObjStruct(interp, objv[4], aEnc, sizeof(aEnc[0]),
"encoding", 0, &enc)
){
return TCL_ERROR;
}
enc = aEnc[enc].enc;
p = sqlite3_malloc(sizeof(CreateFunctionV2));
assert( p );
memset(p, 0, sizeof(CreateFunctionV2));
p->interp = interp;
for(i=5; i<objc; i+=2){
int iSwitch;
const char *azSwitch[] = {"-func", "-step", "-final", "-destroy", 0};
if( Tcl_GetIndexFromObj(interp, objv[i], azSwitch, "switch", 0, &iSwitch) ){
sqlite3_free(p);
return TCL_ERROR;
}
switch( iSwitch ){
case 0: p->pFunc = objv[i+1]; break;
case 1: p->pStep = objv[i+1]; break;
case 2: p->pFinal = objv[i+1]; break;
case 3: p->pDestroy = objv[i+1]; break;
}
}
if( p->pFunc ) p->pFunc = Tcl_DuplicateObj(p->pFunc);
if( p->pStep ) p->pStep = Tcl_DuplicateObj(p->pStep);
if( p->pFinal ) p->pFinal = Tcl_DuplicateObj(p->pFinal);
if( p->pDestroy ) p->pDestroy = Tcl_DuplicateObj(p->pDestroy);
if( p->pFunc ) Tcl_IncrRefCount(p->pFunc);
if( p->pStep ) Tcl_IncrRefCount(p->pStep);
if( p->pFinal ) Tcl_IncrRefCount(p->pFinal);
if( p->pDestroy ) Tcl_IncrRefCount(p->pDestroy);
rc = sqlite3_create_function_v2(db, zFunc, nArg, enc, (void *)p,
(p->pFunc ? cf2Func : 0),
(p->pStep ? cf2Step : 0),
(p->pFinal ? cf2Final : 0),
cf2Destroy
);
if( rc!=SQLITE_OK ){
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC?
*/
@ -2086,6 +2299,33 @@ static int test_next_stmt(
return TCL_OK;
}
/*
** Usage: sqlite3_stmt_readonly STMT
**
** Return true if STMT is a NULL pointer or a pointer to a statement
** that is guaranteed to leave the database unmodified.
*/
static int test_stmt_readonly(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3_stmt *pStmt;
int rc;
if( objc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetStringFromObj(objv[0], 0), " STMT", 0);
return TCL_ERROR;
}
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
rc = sqlite3_stmt_readonly(pStmt);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc));
return TCL_OK;
}
/*
** Usage: sqlite3_reset STMT
@ -2291,7 +2531,7 @@ static int test_collate_func(
int nB, const void *zB
){
Tcl_Interp *i = pTestCollateInterp;
int encin = (int)pCtx;
int encin = SQLITE_PTR_TO_INT(pCtx);
int res;
int n;
@ -2420,7 +2660,7 @@ static void test_collate_needed_cb(
}
zNeededCollation[i] = 0;
sqlite3_create_collation(
db, "test_collate", ENC(db), (void *)enc, test_collate_func);
db, "test_collate", ENC(db), SQLITE_INT_TO_PTR(enc), test_collate_func);
}
/*
@ -2469,8 +2709,8 @@ static int alignmentCollFunc(
){
int rc, n;
n = nKey1<nKey2 ? nKey1 : nKey2;
if( nKey1>0 && 1==(1&(int)pKey1) ) unaligned_string_counter++;
if( nKey2>0 && 1==(1&(int)pKey2) ) unaligned_string_counter++;
if( nKey1>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey1))) ) unaligned_string_counter++;
if( nKey2>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey2))) ) unaligned_string_counter++;
rc = memcmp(pKey1, pKey2, n);
if( rc==0 ){
rc = nKey1 - nKey2;
@ -4266,20 +4506,17 @@ static int test_soft_heap_limit(
int objc,
Tcl_Obj *CONST objv[]
){
static int softHeapLimit = 0;
int amt;
sqlite3_int64 amt;
sqlite3_int64 N = -1;
if( objc!=1 && objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "?N?");
return TCL_ERROR;
}
amt = softHeapLimit;
if( objc==2 ){
int N;
if( Tcl_GetIntFromObj(interp, objv[1], &N) ) return TCL_ERROR;
sqlite3_soft_heap_limit(N);
softHeapLimit = N;
if( Tcl_GetWideIntFromObj(interp, objv[1], &N) ) return TCL_ERROR;
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(amt));
amt = sqlite3_soft_heap_limit64(N);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(amt));
return TCL_OK;
}
@ -4564,13 +4801,13 @@ static int file_control_test(
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
rc = sqlite3_file_control(db, 0, 0, &iArg);
assert( rc==SQLITE_ERROR );
assert( rc==SQLITE_NOTFOUND );
rc = sqlite3_file_control(db, "notadatabase", SQLITE_FCNTL_LOCKSTATE, &iArg);
assert( rc==SQLITE_ERROR );
rc = sqlite3_file_control(db, "main", -1, &iArg);
assert( rc==SQLITE_ERROR );
assert( rc==SQLITE_NOTFOUND );
rc = sqlite3_file_control(db, "temp", -1, &iArg);
assert( rc==SQLITE_ERROR );
assert( rc==SQLITE_NOTFOUND || rc==SQLITE_ERROR );
return TCL_OK;
}
@ -5021,6 +5258,122 @@ static int runAsObjProc(
return cmdInfo.objProc(cmdInfo.objClientData, interp, objc-1, objv+1);
}
#ifndef SQLITE_OMIT_EXPLAIN
/*
** WARNING: The following function, printExplainQueryPlan() is an exact
** copy of example code from eqp.in (eqp.html). If this code is modified,
** then the documentation copy needs to be modified as well.
*/
/*
** Argument pStmt is a prepared SQL statement. This function compiles
** an EXPLAIN QUERY PLAN command to report on the prepared statement,
** and prints the report to stdout using printf().
*/
int printExplainQueryPlan(sqlite3_stmt *pStmt){
const char *zSql; /* Input SQL */
char *zExplain; /* SQL with EXPLAIN QUERY PLAN prepended */
sqlite3_stmt *pExplain; /* Compiled EXPLAIN QUERY PLAN command */
int rc; /* Return code from sqlite3_prepare_v2() */
zSql = sqlite3_sql(pStmt);
if( zSql==0 ) return SQLITE_ERROR;
zExplain = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zSql);
if( zExplain==0 ) return SQLITE_NOMEM;
rc = sqlite3_prepare_v2(sqlite3_db_handle(pStmt), zExplain, -1, &pExplain, 0);
sqlite3_free(zExplain);
if( rc!=SQLITE_OK ) return rc;
while( SQLITE_ROW==sqlite3_step(pExplain) ){
int iSelectid = sqlite3_column_int(pExplain, 0);
int iOrder = sqlite3_column_int(pExplain, 1);
int iFrom = sqlite3_column_int(pExplain, 2);
const char *zDetail = (const char *)sqlite3_column_text(pExplain, 3);
printf("%d %d %d %s\n", iSelectid, iOrder, iFrom, zDetail);
}
return sqlite3_finalize(pExplain);
}
static int test_print_eqp(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc;
sqlite3_stmt *pStmt;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "STMT");
return TCL_ERROR;
}
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
rc = printExplainQueryPlan(pStmt);
Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0);
return TCL_OK;
}
#endif /* SQLITE_OMIT_EXPLAIN */
/*
** optimization_control DB OPT BOOLEAN
**
** Enable or disable query optimizations using the sqlite3_test_control()
** interface. Disable if BOOLEAN is false and enable if BOOLEAN is true.
** OPT is the name of the optimization to be disabled.
*/
static int optimization_control(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int i;
sqlite3 *db;
const char *zOpt;
int onoff;
int mask;
static const struct {
const char *zOptName;
int mask;
} aOpt[] = {
{ "all", SQLITE_OptMask },
{ "query-flattener", SQLITE_QueryFlattener },
{ "column-cache", SQLITE_ColumnCache },
{ "index-sort", SQLITE_IndexSort },
{ "index-search", SQLITE_IndexSearch },
{ "index-cover", SQLITE_IndexCover },
{ "groupby-order", SQLITE_GroupByOrder },
{ "factor-constants", SQLITE_FactorOutConst },
};
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB OPT BOOLEAN");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
if( Tcl_GetBooleanFromObj(interp, objv[3], &onoff) ) return TCL_ERROR;
zOpt = Tcl_GetString(objv[2]);
for(i=0; i<sizeof(aOpt)/sizeof(aOpt[0]); i++){
if( strcmp(zOpt, aOpt[i].zOptName)==0 ){
mask = aOpt[i].mask;
break;
}
}
if( onoff ) mask = ~mask;
if( i>=sizeof(aOpt)/sizeof(aOpt[0]) ){
Tcl_AppendResult(interp, "unknown optimization - should be one of:",
(char*)0);
for(i=0; i<sizeof(aOpt)/sizeof(aOpt[0]); i++){
Tcl_AppendResult(interp, " ", aOpt[i].zOptName);
}
return TCL_ERROR;
}
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, mask);
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
@ -5124,6 +5477,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_step", test_step ,0 },
{ "sqlite3_sql", test_sql ,0 },
{ "sqlite3_next_stmt", test_next_stmt ,0 },
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
{ "sqlite3_release_memory", test_release_memory, 0},
{ "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},
@ -5138,6 +5492,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "save_prng_state", save_prng_state, 0 },
{ "restore_prng_state", restore_prng_state, 0 },
{ "reset_prng_state", reset_prng_state, 0 },
{ "optimization_control", optimization_control,0},
{ "tcl_objproc", runAsObjProc, 0 },
/* sqlite3_column_*() API */
@ -5187,6 +5542,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
/* Functions from os.h */
#ifndef SQLITE_OMIT_UTF16
@ -5205,15 +5561,19 @@ 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_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
{ "sqlite3_unlock_notify", test_unlock_notify, 0 },
#endif
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
{ "test_sqlite3_log", test_sqlite3_log, 0 },
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
{ "test_sqlite3_log", test_sqlite3_log, 0 },
{ "print_explain_query_plan", test_print_eqp, 0 },
};
static int bitmask_size = sizeof(Bitmask)*8;
int i;

View File

@ -53,7 +53,7 @@ static sqlite3 sDb;
static int nRefSqlite3 = 0;
/*
** Usage: btree_open FILENAME NCACHE FLAGS
** Usage: btree_open FILENAME NCACHE
**
** Open a new database
*/
@ -64,22 +64,21 @@ static int btree_open(
const char **argv /* Text of each argument */
){
Btree *pBt;
int rc, nCache, flags;
int rc, nCache;
char zBuf[100];
if( argc!=4 ){
if( argc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" FILENAME NCACHE FLAGS\"", 0);
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR;
nRefSqlite3++;
if( nRefSqlite3==1 ){
sDb.pVfs = sqlite3_vfs_find(0);
sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
sqlite3_mutex_enter(sDb.mutex);
}
rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags,
rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, errorName(rc), 0);

View File

@ -133,6 +133,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "autoindex", "1", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_AUTORESET
Tcl_SetVar2(interp, "sqlite_options", "autoreset", "0", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "autoreset", "1", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_AUTOVACUUM
Tcl_SetVar2(interp, "sqlite_options", "autovacuum", "0", TCL_GLOBAL_ONLY);
#else
@ -523,6 +529,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef YYTRACKMAXSTACKDEPTH
Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "1", TCL_GLOBAL_ONLY);
#else

View File

@ -1290,10 +1290,13 @@ static int test_db_status(
const char *zName;
int op;
} aOp[] = {
{ "SQLITE_DBSTATUS_LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED },
{ "SQLITE_DBSTATUS_CACHE_USED", SQLITE_DBSTATUS_CACHE_USED },
{ "SQLITE_DBSTATUS_SCHEMA_USED", SQLITE_DBSTATUS_SCHEMA_USED },
{ "SQLITE_DBSTATUS_STMT_USED", SQLITE_DBSTATUS_STMT_USED }
{ "LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED },
{ "CACHE_USED", SQLITE_DBSTATUS_CACHE_USED },
{ "SCHEMA_USED", SQLITE_DBSTATUS_SCHEMA_USED },
{ "STMT_USED", SQLITE_DBSTATUS_STMT_USED },
{ "LOOKASIDE_HIT", SQLITE_DBSTATUS_LOOKASIDE_HIT },
{ "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE },
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL }
};
Tcl_Obj *pResult;
if( objc!=4 ){
@ -1302,6 +1305,8 @@ static int test_db_status(
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
zOpName = Tcl_GetString(objv[2]);
if( memcmp(zOpName, "SQLITE_", 7)==0 ) zOpName += 7;
if( memcmp(zOpName, "DBSTATUS_", 9)==0 ) zOpName += 9;
for(i=0; i<ArraySize(aOp); i++){
if( strcmp(aOp[i].zName, zOpName)==0 ){
op = aOp[i].op;

973
src/test_multiplex.c Normal file
View File

@ -0,0 +1,973 @@
/*
** 2010 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.
**
*************************************************************************
**
** This file contains a VFS "shim" - a layer that sits in between the
** pager and the real VFS.
**
** This particular shim enforces a multiplex system on DB files.
** This shim shards/partitions a single DB file into smaller
** "chunks" such that the total DB file size may exceed the maximum
** file size of the underlying file system.
**
*/
#include "sqlite3.h"
#include <string.h>
#include <assert.h>
#include "sqliteInt.h"
/************************ Shim Definitions ******************************/
/* This is the limit on the chunk size. It may be changed by calling
** the sqlite3_multiplex_set() interface.
*/
#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000
/* Default limit on number of chunks. Care should be taken
** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
** format specifier. It may be changed by calling
** the sqlite3_multiplex_set() interface.
*/
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32
/* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the
** last SQLITE_MULTIPLEX_EXT_SZ characters of the
** filename will be overwritten, otherwise, the
** multiplex extension is simply appended to the filename.
** Ex. (undefined) test.db -> test.db01
** (defined) test.db -> test.01
** Chunk 0 does not have a modified extension.
*/
#define SQLITE_MULTIPLEX_EXT_FMT "%02d"
#define SQLITE_MULTIPLEX_EXT_SZ 2
/************************ Object Definitions ******************************/
/* Forward declaration of all object types */
typedef struct multiplexGroup multiplexGroup;
typedef struct multiplexConn multiplexConn;
/*
** A "multiplex group" is a collection of files that collectively
** makeup a single SQLite DB file. This allows the size of the DB
** to exceed the limits imposed by the file system.
**
** There is an instance of the following object for each defined multiplex
** group.
*/
struct multiplexGroup {
sqlite3_file **pReal; /* Handles to each chunk */
char *bOpen; /* 0 if chunk not opened */
char *zName; /* Base filename of this group */
int nName; /* Length of base filename */
int flags; /* Flags used for original opening */
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
};
/*
** An instance of the following object represents each open connection
** to a file that is multiplex'ed. This object is a
** subclass of sqlite3_file. The sqlite3_file object for the underlying
** VFS is appended to this structure.
*/
struct multiplexConn {
sqlite3_file base; /* Base class - must be first */
multiplexGroup *pGroup; /* The underlying group of files */
};
/************************* Global Variables **********************************/
/*
** All global variables used by this file are containing within the following
** gMultiplex structure.
*/
static struct {
/* The pOrigVfs is the real, original underlying VFS implementation.
** Most operations pass-through to the real VFS. This value is read-only
** during operation. It is only modified at start-time and thus does not
** require a mutex.
*/
sqlite3_vfs *pOrigVfs;
/* The sThisVfs is the VFS structure used by this shim. It is initialized
** at start-time and thus does not require a mutex
*/
sqlite3_vfs sThisVfs;
/* The sIoMethods defines the methods used by sqlite3_file objects
** associated with this shim. It is initialized at start-time and does
** not require a mutex.
**
** When the underlying VFS is called to open a file, it might return
** either a version 1 or a version 2 sqlite3_file object. This shim
** has to create a wrapper sqlite3_file of the same version. Hence
** there are two I/O method structures, one for version 1 and the other
** for version 2.
*/
sqlite3_io_methods sIoMethodsV1;
sqlite3_io_methods sIoMethodsV2;
/* True when this shim has been initialized.
*/
int isInitialized;
/* For run-time access any of the other global data structures in this
** shim, the following mutex must be held.
*/
sqlite3_mutex *pMutex;
/* List of multiplexGroup objects.
*/
multiplexGroup *pGroups;
/* Chunk params.
*/
int nChunkSize;
int nMaxChunks;
/* Storage for temp file names. Allocated during
** initialization to the max pathname of the underlying VFS.
*/
char *zName;
} gMultiplex;
/************************* Utility Routines *********************************/
/*
** Acquire and release the mutex used to serialize access to the
** list of multiplexGroups.
*/
static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
/* Translate an sqlite3_file* that is really a multiplexGroup* into
** the sqlite3_file* for the underlying original VFS.
*/
static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
multiplexGroup *pGroup = pConn->pGroup;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
if( iChunk<gMultiplex.nMaxChunks ){
sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */
if( !pGroup->bOpen[iChunk] ){
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
if( iChunk ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
#endif
}
*rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags);
if( *rc==SQLITE_OK ){
pGroup->bOpen[iChunk] = -1;
return pSubOpen;
}
return NULL;
}
*rc = SQLITE_OK;
return pSubOpen;
}
*rc = SQLITE_FULL;
return NULL;
}
/************************* VFS Method Wrappers *****************************/
/*
** This is the xOpen method used for the "multiplex" VFS.
**
** Most of the work is done by the underlying original VFS. This method
** simply links the new file into the appropriate multiplex group if it is a
** file that needs to be tracked.
*/
static int multiplexOpen(
sqlite3_vfs *pVfs, /* The multiplex VFS */
const char *zName, /* Name of file to be opened */
sqlite3_file *pConn, /* Fill in this file descriptor */
int flags, /* Flags to control the opening */
int *pOutFlags /* Flags showing results of opening */
){
int rc; /* Result code */
multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
sqlite3_file *pSubOpen; /* Real file descriptor */
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int nName = sqlite3Strlen30(zName);
int i;
int sz;
UNUSED_PARAMETER(pVfs);
/* We need to create a group structure and manage
** access to this group of files.
*/
multiplexEnter();
pMultiplexOpen = (multiplexConn*)pConn;
/* allocate space for group */
sz = sizeof(multiplexGroup) /* multiplexGroup */
+ (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */
+ (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) /* *pReal */
+ gMultiplex.nMaxChunks /* bOpen[] */
+ nName + 1; /* zName */
#ifndef SQLITE_MULTIPLEX_EXT_OVWR
sz += SQLITE_MULTIPLEX_EXT_SZ;
assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
#else
assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
assert(nName < pOrigVfs->mxPathname);
#endif
pGroup = sqlite3_malloc( sz );
if( pGroup==0 ){
rc=SQLITE_NOMEM;
}else{
/* assign pointers to extra space allocated */
char *p = (char *)&pGroup[1];
pMultiplexOpen->pGroup = pGroup;
memset(pGroup, 0, sz);
pGroup->pReal = (sqlite3_file **)p;
p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks);
for(i=0; i<gMultiplex.nMaxChunks; i++){
pGroup->pReal[i] = (sqlite3_file *)p;
p += pOrigVfs->szOsFile;
}
pGroup->bOpen = p;
p += gMultiplex.nMaxChunks;
pGroup->zName = p;
/* save off base filename, name length, and original open flags */
memcpy(pGroup->zName, zName, nName+1);
pGroup->nName = nName;
pGroup->flags = flags;
pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
if( pSubOpen ){
if( pSubOpen->pMethods->iVersion==1 ){
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
}else{
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
}
/* place this group at the head of our list */
pGroup->pNext = gMultiplex.pGroups;
if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
gMultiplex.pGroups = pGroup;
}else{
sqlite3_free(pGroup);
}
}
multiplexLeave();
return rc;
}
/*
** This is the xDelete method used for the "multiplex" VFS.
** It attempts to delete the filename specified, as well
** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension.
*/
static int multiplexDelete(
sqlite3_vfs *pVfs, /* The multiplex VFS */
const char *zName, /* Name of file to delete */
int syncDir
){
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int rc = SQLITE_OK;
int nName = sqlite3Strlen30(zName);
int i;
UNUSED_PARAMETER(pVfs);
multiplexEnter();
memcpy(gMultiplex.zName, zName, nName+1);
for(i=0; i<gMultiplex.nMaxChunks; i++){
int rc2;
int exists = 0;
if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
}
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
if( rc2==SQLITE_OK && exists){
/* if it exists, delete it */
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
if( rc2!=SQLITE_OK ) rc = rc2;
}else{
/* stop at first "gap" */
break;
}
}
multiplexLeave();
return rc;
}
static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);
}
static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){
return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);
}
static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){
return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);
}
static void multiplexDlError(sqlite3_vfs *a, int b, char *c){
gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);
}
static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){
return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);
}
static void multiplexDlClose(sqlite3_vfs *a, void *b){
gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);
}
static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){
return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);
}
static int multiplexSleep(sqlite3_vfs *a, int b){
return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);
}
static int multiplexCurrentTime(sqlite3_vfs *a, double *b){
return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);
}
static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){
return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
}
static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
}
/************************ I/O Method Wrappers *******************************/
/* xClose requests get passed through to the original VFS.
** We loop over all open chunk handles and close them.
** The group structure for this file is unlinked from
** our list of groups and freed.
*/
static int multiplexClose(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
int i;
multiplexEnter();
/* close any open handles */
for(i=0; i<gMultiplex.nMaxChunks; i++){
if( pGroup->bOpen[i] ){
sqlite3_file *pSubOpen = pGroup->pReal[i];
int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
if( rc2!=SQLITE_OK ) rc = rc2;
pGroup->bOpen[i] = 0;
}
}
/* remove from linked list */
if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
if( pGroup->pPrev ){
pGroup->pPrev->pNext = pGroup->pNext;
}else{
gMultiplex.pGroups = pGroup->pNext;
}
sqlite3_free(pGroup);
multiplexLeave();
return rc;
}
/* Pass xRead requests thru to the original VFS after
** determining the correct chunk to operate on.
** Break up reads across chunk boundaries.
*/
static int multiplexRead(
sqlite3_file *pConn,
void *pBuf,
int iAmt,
sqlite3_int64 iOfst
){
multiplexConn *p = (multiplexConn*)pConn;
int rc = SQLITE_OK;
multiplexEnter();
while( iAmt > 0 ){
int i = (int)(iOfst/gMultiplex.nChunkSize);
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
if( pSubOpen ){
int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
if( extra<0 ) extra = 0;
iAmt -= extra;
rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
}else{
rc = SQLITE_IOERR_READ;
break;
}
}
multiplexLeave();
return rc;
}
/* Pass xWrite requests thru to the original VFS after
** determining the correct chunk to operate on.
** Break up writes across chunk boundaries.
*/
static int multiplexWrite(
sqlite3_file *pConn,
const void *pBuf,
int iAmt,
sqlite3_int64 iOfst
){
multiplexConn *p = (multiplexConn*)pConn;
int rc = SQLITE_OK;
multiplexEnter();
while( iAmt > 0 ){
int i = (int)(iOfst/gMultiplex.nChunkSize);
sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
if( pSubOpen ){
int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
if( extra<0 ) extra = 0;
iAmt -= extra;
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
}else{
rc = SQLITE_IOERR_WRITE;
break;
}
}
multiplexLeave();
return rc;
}
/* Pass xTruncate requests thru to the original VFS after
** determining the correct chunk to operate on. Delete any
** chunks above the truncate mark.
*/
static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
int rc2;
int i;
sqlite3_file *pSubOpen;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
multiplexEnter();
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
/* delete the chunks above the truncate limit */
for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
/* close any open chunks before deleting them */
if( pGroup->bOpen[i] ){
pSubOpen = pGroup->pReal[i];
rc2 = pSubOpen->pMethods->xClose(pSubOpen);
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
pGroup->bOpen[i] = 0;
}
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
}
pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
if( pSubOpen ){
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
if( rc2!=SQLITE_OK ) rc = rc2;
}else{
rc = SQLITE_IOERR_TRUNCATE;
}
multiplexLeave();
return rc;
}
/* Pass xSync requests through to the original VFS without change
*/
static int multiplexSync(sqlite3_file *pConn, int flags){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
int i;
multiplexEnter();
for(i=0; i<gMultiplex.nMaxChunks; i++){
/* if we don't have it open, we don't need to sync it */
if( pGroup->bOpen[i] ){
sqlite3_file *pSubOpen = pGroup->pReal[i];
int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
if( rc2!=SQLITE_OK ) rc = rc2;
}
}
multiplexLeave();
return rc;
}
/* Pass xFileSize requests through to the original VFS.
** Aggregate the size of all the chunks before returning.
*/
static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
int rc2;
int i;
multiplexEnter();
*pSize = 0;
for(i=0; i<gMultiplex.nMaxChunks; i++){
sqlite3_file *pSubOpen = NULL;
/* if not opened already, check to see if the chunk exists */
if( pGroup->bOpen[i] ){
pSubOpen = pGroup->pReal[i];
}else{
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int exists = 0;
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
#else
sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
}
rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
if( rc2==SQLITE_OK && exists){
/* if it exists, open it */
pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
}else{
/* stop at first "gap" */
break;
}
}
if( pSubOpen ){
sqlite3_int64 sz;
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( rc2!=SQLITE_OK ){
rc = rc2;
}else{
if( sz>gMultiplex.nChunkSize ){
rc = SQLITE_IOERR_FSTAT;
}
*pSize += sz;
}
}else{
break;
}
}
multiplexLeave();
return rc;
}
/* Pass xLock requests through to the original VFS unchanged.
*/
static int multiplexLock(sqlite3_file *pConn, int lock){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xLock(pSubOpen, lock);
}
return SQLITE_BUSY;
}
/* Pass xUnlock requests through to the original VFS unchanged.
*/
static int multiplexUnlock(sqlite3_file *pConn, int lock){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
}
return SQLITE_IOERR_UNLOCK;
}
/* Pass xCheckReservedLock requests through to the original VFS unchanged.
*/
static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
}
return SQLITE_IOERR_CHECKRESERVEDLOCK;
}
/* Pass xFileControl requests through to the original VFS unchanged.
*/
static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen;
if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
}
return SQLITE_ERROR;
}
/* Pass xSectorSize requests through to the original VFS unchanged.
*/
static int multiplexSectorSize(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xSectorSize(pSubOpen);
}
return SQLITE_DEFAULT_SECTOR_SIZE;
}
/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
*/
static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
}
return 0;
}
/* Pass xShmMap requests through to the original VFS unchanged.
*/
static int multiplexShmMap(
sqlite3_file *pConn, /* Handle open on database file */
int iRegion, /* Region to retrieve */
int szRegion, /* Size of regions */
int bExtend, /* True to extend file if necessary */
void volatile **pp /* OUT: Mapped memory */
){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
}
return SQLITE_IOERR;
}
/* Pass xShmLock requests through to the original VFS unchanged.
*/
static int multiplexShmLock(
sqlite3_file *pConn, /* Database file holding the shared memory */
int ofst, /* First lock to acquire or release */
int n, /* Number of locks to acquire or release */
int flags /* What to do with the lock */
){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
}
return SQLITE_BUSY;
}
/* Pass xShmBarrier requests through to the original VFS unchanged.
*/
static void multiplexShmBarrier(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
pSubOpen->pMethods->xShmBarrier(pSubOpen);
}
}
/* Pass xShmUnmap requests through to the original VFS unchanged.
*/
static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
if( pSubOpen ){
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
}
return SQLITE_OK;
}
/************************** Public Interfaces *****************************/
/*
** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName
** as the VFS that does the actual work. Use the default if
** zOrigVfsName==NULL.
**
** The multiplex VFS shim is named "multiplex". It will become the default
** VFS if makeDefault is non-zero.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
** during start-up.
*/
int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
sqlite3_vfs *pOrigVfs;
if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
if( pOrigVfs==0 ) return SQLITE_ERROR;
assert( pOrigVfs!=&gMultiplex.sThisVfs );
gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( !gMultiplex.pMutex ){
return SQLITE_NOMEM;
}
gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname);
if( !gMultiplex.zName ){
sqlite3_mutex_free(gMultiplex.pMutex);
return SQLITE_NOMEM;
}
gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
gMultiplex.pGroups = NULL;
gMultiplex.isInitialized = 1;
gMultiplex.pOrigVfs = pOrigVfs;
gMultiplex.sThisVfs = *pOrigVfs;
gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
gMultiplex.sThisVfs.zName = "multiplex";
gMultiplex.sThisVfs.xOpen = multiplexOpen;
gMultiplex.sThisVfs.xDelete = multiplexDelete;
gMultiplex.sThisVfs.xAccess = multiplexAccess;
gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
gMultiplex.sThisVfs.xDlError = multiplexDlError;
gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
gMultiplex.sThisVfs.xDlClose = multiplexDlClose;
gMultiplex.sThisVfs.xRandomness = multiplexRandomness;
gMultiplex.sThisVfs.xSleep = multiplexSleep;
gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;
gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;
gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;
gMultiplex.sIoMethodsV1.iVersion = 1;
gMultiplex.sIoMethodsV1.xClose = multiplexClose;
gMultiplex.sIoMethodsV1.xRead = multiplexRead;
gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
gMultiplex.sIoMethodsV1.xSync = multiplexSync;
gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
gMultiplex.sIoMethodsV1.xLock = multiplexLock;
gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics;
gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
gMultiplex.sIoMethodsV2.iVersion = 2;
gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
return SQLITE_OK;
}
/*
** Shutdown the multiplex system.
**
** All SQLite database connections must be closed before calling this
** routine.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
** shutting down in order to free all remaining multiplex groups.
*/
int sqlite3_multiplex_shutdown(void){
if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
if( gMultiplex.pGroups ) return SQLITE_MISUSE;
gMultiplex.isInitialized = 0;
sqlite3_free(gMultiplex.zName);
sqlite3_mutex_free(gMultiplex.pMutex);
sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
memset(&gMultiplex, 0, sizeof(gMultiplex));
return SQLITE_OK;
}
/*
** Adjust chunking params. VFS should be initialized first.
** No files should be open. Re-intializing will reset these
** to the default.
*/
int sqlite3_multiplex_set(
int nChunkSize, /* Max chunk size */
int nMaxChunks /* Max number of chunks */
){
if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
if( gMultiplex.pGroups ) return SQLITE_MISUSE;
if( nChunkSize<32 ) return SQLITE_MISUSE;
if( nMaxChunks<1 ) return SQLITE_MISUSE;
if( nMaxChunks>99 ) return SQLITE_MISUSE;
multiplexEnter();
gMultiplex.nChunkSize = nChunkSize;
gMultiplex.nMaxChunks = nMaxChunks;
multiplexLeave();
return SQLITE_OK;
}
/***************************** Test Code ***********************************/
#ifdef SQLITE_TEST
#include <tcl.h>
extern const char *sqlite3TestErrorName(int);
/*
** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
*/
static int test_multiplex_initialize(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zName; /* Name of new multiplex VFS */
int makeDefault; /* True to make the new VFS the default */
int rc; /* Value returned by multiplex_initialize() */
UNUSED_PARAMETER(clientData);
/* Process arguments */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
return TCL_ERROR;
}
zName = Tcl_GetString(objv[1]);
if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
if( zName[0]=='\0' ) zName = 0;
/* Call sqlite3_multiplex_initialize() */
rc = sqlite3_multiplex_initialize(zName, makeDefault);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tclcmd: sqlite3_multiplex_shutdown
*/
static int test_multiplex_shutdown(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc; /* Value returned by multiplex_shutdown() */
UNUSED_PARAMETER(clientData);
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
/* Call sqlite3_multiplex_shutdown() */
rc = sqlite3_multiplex_shutdown();
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
*/
static int test_multiplex_set(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int nChunkSize; /* Max chunk size */
int nMaxChunks; /* Max number of chunks */
int rc; /* Value returned by sqlite3_multiplex_set() */
UNUSED_PARAMETER(clientData);
/* Process arguments */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR;
/* Invoke sqlite3_multiplex_set() */
rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tclcmd: sqlite3_multiplex_dump
*/
static int test_multiplex_dump(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
Tcl_Obj *pResult;
Tcl_Obj *pGroupTerm;
multiplexGroup *pGroup;
int i;
int nChunks = 0;
UNUSED_PARAMETER(clientData);
UNUSED_PARAMETER(objc);
UNUSED_PARAMETER(objv);
pResult = Tcl_NewObj();
multiplexEnter();
for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
pGroupTerm = Tcl_NewObj();
pGroup->zName[pGroup->nName] = '\0';
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewStringObj(pGroup->zName, -1));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(pGroup->nName));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(pGroup->flags));
/* count number of chunks with open handles */
for(i=0; i<gMultiplex.nMaxChunks; i++){
if( pGroup->bOpen[i] ) nChunks++;
}
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(nChunks));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(gMultiplex.nChunkSize));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(gMultiplex.nMaxChunks));
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
}
multiplexLeave();
Tcl_SetObjResult(interp, pResult);
return TCL_OK;
}
/*
** This routine registers the custom TCL commands defined in this
** module. This should be the only procedure visible from outside
** of this module.
*/
int Sqlitemultiplex_Init(Tcl_Interp *interp){
static struct {
char *zName;
Tcl_ObjCmdProc *xProc;
} aCmd[] = {
{ "sqlite3_multiplex_initialize", test_multiplex_initialize },
{ "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
{ "sqlite3_multiplex_set", test_multiplex_set },
{ "sqlite3_multiplex_dump", test_multiplex_dump },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
}
return TCL_OK;
}
#endif

View File

@ -247,7 +247,7 @@ static int test_read_mutex_counters(
int ii;
char *aName[8] = {
"fast", "recursive", "static_master", "static_mem",
"static_open", "static_prng", "static_lru", "static_lru2"
"static_open", "static_prng", "static_lru", "static_pmem"
};
if( objc!=1 ){

952
src/test_quota.c Normal file
View File

@ -0,0 +1,952 @@
/*
** 2010 September 31
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains a VFS "shim" - a layer that sits in between the
** pager and the real VFS.
**
** This particular shim enforces a quota system on files. One or more
** database files are in a "quota group" that is defined by a GLOB
** pattern. A quota is set for the combined size of all files in the
** the group. A quota of zero means "no limit". If the total size
** of all files in the quota group is greater than the limit, then
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
**
** However, before returning SQLITE_FULL, the write requests invoke
** a callback function that is configurable for each quota group.
** This callback has the opportunity to enlarge the quota. If the
** callback does enlarge the quota such that the total size of all
** files within the group is less than the new quota, then the write
** continues as if nothing had happened.
*/
#include "sqlite3.h"
#include <string.h>
#include <assert.h>
/*
** For an build without mutexes, no-op the mutex calls.
*/
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
#define sqlite3_mutex_free(X)
#define sqlite3_mutex_enter(X)
#define sqlite3_mutex_try(X) SQLITE_OK
#define sqlite3_mutex_leave(X)
#define sqlite3_mutex_held(X) ((void)(X),1)
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */
/************************ Object Definitions ******************************/
/* Forward declaration of all object types */
typedef struct quotaGroup quotaGroup;
typedef struct quotaConn quotaConn;
typedef struct quotaFile quotaFile;
/*
** A "quota group" is a collection of files whose collective size we want
** to limit. Each quota group is defined by a GLOB pattern.
**
** There is an instance of the following object for each defined quota
** group. This object records the GLOB pattern that defines which files
** belong to the quota group. The object also remembers the size limit
** for the group (the quota) and the callback to be invoked when the
** sum of the sizes of the files within the group goes over the limit.
**
** A quota group must be established (using sqlite3_quota_set(...))
** prior to opening any of the database connections that access files
** within the quota group.
*/
struct quotaGroup {
const char *zPattern; /* Filename pattern to be quotaed */
sqlite3_int64 iLimit; /* Upper bound on total file size */
sqlite3_int64 iSize; /* Current size of all files */
void (*xCallback)( /* Callback invoked when going over quota */
const char *zFilename, /* Name of file whose size increases */
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
sqlite3_int64 iSize, /* Total size of all files in the group */
void *pArg /* Client data */
);
void *pArg; /* Third argument to the xCallback() */
void (*xDestroy)(void*); /* Optional destructor for pArg */
quotaGroup *pNext, **ppPrev; /* Doubly linked list of all quota objects */
quotaFile *pFiles; /* Files within this group */
};
/*
** An instance of this structure represents a single file that is part
** of a quota group. A single file can be opened multiple times. In
** order keep multiple openings of the same file from causing the size
** of the file to count against the quota multiple times, each file
** has a unique instance of this object and multiple open connections
** to the same file each point to a single instance of this object.
*/
struct quotaFile {
char *zFilename; /* Name of this file */
quotaGroup *pGroup; /* Quota group to which this file belongs */
sqlite3_int64 iSize; /* Current size of this file */
int nRef; /* Number of times this file is open */
quotaFile *pNext, **ppPrev; /* Linked list of files in the same group */
};
/*
** An instance of the following object represents each open connection
** to a file that participates in quota tracking. This object is a
** subclass of sqlite3_file. The sqlite3_file object for the underlying
** VFS is appended to this structure.
*/
struct quotaConn {
sqlite3_file base; /* Base class - must be first */
quotaFile *pFile; /* The underlying file */
/* The underlying VFS sqlite3_file is appended to this object */
};
/************************* Global Variables **********************************/
/*
** All global variables used by this file are containing within the following
** gQuota structure.
*/
static struct {
/* The pOrigVfs is the real, original underlying VFS implementation.
** Most operations pass-through to the real VFS. This value is read-only
** during operation. It is only modified at start-time and thus does not
** require a mutex.
*/
sqlite3_vfs *pOrigVfs;
/* The sThisVfs is the VFS structure used by this shim. It is initialized
** at start-time and thus does not require a mutex
*/
sqlite3_vfs sThisVfs;
/* The sIoMethods defines the methods used by sqlite3_file objects
** associated with this shim. It is initialized at start-time and does
** not require a mutex.
**
** When the underlying VFS is called to open a file, it might return
** either a version 1 or a version 2 sqlite3_file object. This shim
** has to create a wrapper sqlite3_file of the same version. Hence
** there are two I/O method structures, one for version 1 and the other
** for version 2.
*/
sqlite3_io_methods sIoMethodsV1;
sqlite3_io_methods sIoMethodsV2;
/* True when this shim as been initialized.
*/
int isInitialized;
/* For run-time access any of the other global data structures in this
** shim, the following mutex must be held.
*/
sqlite3_mutex *pMutex;
/* List of quotaGroup objects.
*/
quotaGroup *pGroup;
} gQuota;
/************************* Utility Routines *********************************/
/*
** Acquire and release the mutex used to serialize access to the
** list of quotaGroups.
*/
static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); }
static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); }
/* If the reference count and threshold for a quotaGroup are both
** zero, then destroy the quotaGroup.
*/
static void quotaGroupDeref(quotaGroup *pGroup){
if( pGroup->pFiles==0 && pGroup->iLimit==0 ){
*pGroup->ppPrev = pGroup->pNext;
if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev;
if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg);
sqlite3_free(pGroup);
}
}
/*
** Return TRUE if string z matches glob pattern zGlob.
**
** Globbing rules:
**
** '*' Matches any sequence of zero or more characters.
**
** '?' Matches exactly one character.
**
** [...] Matches one character from the enclosed list of
** characters.
**
** [^...] Matches one character not in the enclosed list.
**
*/
static int quotaStrglob(const char *zGlob, const char *z){
int c, c2;
int invert;
int seen;
while( (c = (*(zGlob++)))!=0 ){
if( c=='*' ){
while( (c=(*(zGlob++))) == '*' || c=='?' ){
if( c=='?' && (*(z++))==0 ) return 0;
}
if( c==0 ){
return 1;
}else if( c=='[' ){
while( *z && quotaStrglob(zGlob-1,z)==0 ){
z++;
}
return (*z)!=0;
}
while( (c2 = (*(z++)))!=0 ){
while( c2!=c ){
c2 = *(z++);
if( c2==0 ) return 0;
}
if( quotaStrglob(zGlob,z) ) return 1;
}
return 0;
}else if( c=='?' ){
if( (*(z++))==0 ) return 0;
}else if( c=='[' ){
int prior_c = 0;
seen = 0;
invert = 0;
c = *(z++);
if( c==0 ) return 0;
c2 = *(zGlob++);
if( c2=='^' ){
invert = 1;
c2 = *(zGlob++);
}
if( c2==']' ){
if( c==']' ) seen = 1;
c2 = *(zGlob++);
}
while( c2 && c2!=']' ){
if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
c2 = *(zGlob++);
if( c>=prior_c && c<=c2 ) seen = 1;
prior_c = 0;
}else{
if( c==c2 ){
seen = 1;
}
prior_c = c2;
}
c2 = *(zGlob++);
}
if( c2==0 || (seen ^ invert)==0 ) return 0;
}else{
if( c!=(*(z++)) ) return 0;
}
}
return *z==0;
}
/* Find a quotaGroup given the filename.
**
** Return a pointer to the quotaGroup object. Return NULL if not found.
*/
static quotaGroup *quotaGroupFind(const char *zFilename){
quotaGroup *p;
for(p=gQuota.pGroup; p && quotaStrglob(p->zPattern, zFilename)==0;
p=p->pNext){}
return p;
}
/* Translate an sqlite3_file* that is really a quotaConn* into
** the sqlite3_file* for the underlying original VFS.
*/
static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
quotaConn *p = (quotaConn*)pConn;
return (sqlite3_file*)&p[1];
}
/************************* VFS Method Wrappers *****************************/
/*
** This is the xOpen method used for the "quota" VFS.
**
** Most of the work is done by the underlying original VFS. This method
** simply links the new file into the appropriate quota group if it is a
** file that needs to be tracked.
*/
static int quotaOpen(
sqlite3_vfs *pVfs, /* The quota VFS */
const char *zName, /* Name of file to be opened */
sqlite3_file *pConn, /* Fill in this file descriptor */
int flags, /* Flags to control the opening */
int *pOutFlags /* Flags showing results of opening */
){
int rc; /* Result code */
quotaConn *pQuotaOpen; /* The new quota file descriptor */
quotaFile *pFile; /* Corresponding quotaFile obj */
quotaGroup *pGroup; /* The group file belongs to */
sqlite3_file *pSubOpen; /* Real file descriptor */
sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */
/* If the file is not a main database file or a WAL, then use the
** normal xOpen method.
*/
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
}
/* If the name of the file does not match any quota group, then
** use the normal xOpen method.
*/
quotaEnter();
pGroup = quotaGroupFind(zName);
if( pGroup==0 ){
rc = pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
}else{
/* If we get to this point, it means the file needs to be quota tracked.
*/
pQuotaOpen = (quotaConn*)pConn;
pSubOpen = quotaSubOpen(pConn);
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
if( rc==SQLITE_OK ){
for(pFile=pGroup->pFiles; pFile && strcmp(pFile->zFilename, zName);
pFile=pFile->pNext){}
if( pFile==0 ){
int nName = strlen(zName);
pFile = sqlite3_malloc( sizeof(*pFile) + nName + 1 );
if( pFile==0 ){
quotaLeave();
pSubOpen->pMethods->xClose(pSubOpen);
return SQLITE_NOMEM;
}
memset(pFile, 0, sizeof(*pFile));
pFile->zFilename = (char*)&pFile[1];
memcpy(pFile->zFilename, zName, nName+1);
pFile->pNext = pGroup->pFiles;
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
pFile->ppPrev = &pGroup->pFiles;
pGroup->pFiles = pFile;
pFile->pGroup = pGroup;
}
pFile->nRef++;
pQuotaOpen->pFile = pFile;
if( pSubOpen->pMethods->iVersion==1 ){
pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1;
}else{
pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2;
}
}
}
quotaLeave();
return rc;
}
/************************ I/O Method Wrappers *******************************/
/* xClose requests get passed through to the original VFS. But we
** also have to unlink the quotaConn from the quotaFile and quotaGroup.
** The quotaFile and/or quotaGroup are freed if they are no longer in use.
*/
static int quotaClose(sqlite3_file *pConn){
quotaConn *p = (quotaConn*)pConn;
quotaFile *pFile = p->pFile;
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
int rc;
rc = pSubOpen->pMethods->xClose(pSubOpen);
quotaEnter();
pFile->nRef--;
if( pFile->nRef==0 ){
quotaGroup *pGroup = pFile->pGroup;
pGroup->iSize -= pFile->iSize;
if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev;
*pFile->ppPrev = pFile->pNext;
quotaGroupDeref(pGroup);
sqlite3_free(pFile);
}
quotaLeave();
return rc;
}
/* Pass xRead requests directory thru to the original VFS without
** further processing.
*/
static int quotaRead(
sqlite3_file *pConn,
void *pBuf,
int iAmt,
sqlite3_int64 iOfst
){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
}
/* Check xWrite requests to see if they expand the file. If they do,
** the perform a quota check before passing them through to the
** original VFS.
*/
static int quotaWrite(
sqlite3_file *pConn,
const void *pBuf,
int iAmt,
sqlite3_int64 iOfst
){
quotaConn *p = (quotaConn*)pConn;
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
sqlite3_int64 iEnd = iOfst+iAmt;
quotaGroup *pGroup;
quotaFile *pFile = p->pFile;
sqlite3_int64 szNew;
if( pFile->iSize<iEnd ){
pGroup = pFile->pGroup;
quotaEnter();
szNew = pGroup->iSize - pFile->iSize + iEnd;
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
if( pGroup->xCallback ){
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
pGroup->pArg);
}
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
quotaLeave();
return SQLITE_FULL;
}
}
pGroup->iSize = szNew;
pFile->iSize = iEnd;
quotaLeave();
}
return pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
}
/* Pass xTruncate requests thru to the original VFS. If the
** success, update the file size.
*/
static int quotaTruncate(sqlite3_file *pConn, sqlite3_int64 size){
quotaConn *p = (quotaConn*)pConn;
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
int rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
quotaFile *pFile = p->pFile;
quotaGroup *pGroup;
if( rc==SQLITE_OK ){
quotaEnter();
pGroup = pFile->pGroup;
pGroup->iSize -= pFile->iSize;
pFile->iSize = size;
pGroup->iSize += size;
quotaLeave();
}
return rc;
}
/* Pass xSync requests through to the original VFS without change
*/
static int quotaSync(sqlite3_file *pConn, int flags){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xSync(pSubOpen, flags);
}
/* Pass xFileSize requests through to the original VFS but then
** update the quotaGroup with the new size before returning.
*/
static int quotaFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
quotaConn *p = (quotaConn*)pConn;
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
quotaFile *pFile = p->pFile;
quotaGroup *pGroup;
sqlite3_int64 sz;
int rc;
rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( rc==SQLITE_OK ){
quotaEnter();
pGroup = pFile->pGroup;
pGroup->iSize -= pFile->iSize;
pFile->iSize = sz;
pGroup->iSize += sz;
quotaLeave();
*pSize = sz;
}
return rc;
}
/* Pass xLock requests through to the original VFS unchanged.
*/
static int quotaLock(sqlite3_file *pConn, int lock){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xLock(pSubOpen, lock);
}
/* Pass xUnlock requests through to the original VFS unchanged.
*/
static int quotaUnlock(sqlite3_file *pConn, int lock){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
}
/* Pass xCheckReservedLock requests through to the original VFS unchanged.
*/
static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
}
/* Pass xFileControl requests through to the original VFS unchanged.
*/
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
}
/* Pass xSectorSize requests through to the original VFS unchanged.
*/
static int quotaSectorSize(sqlite3_file *pConn){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xSectorSize(pSubOpen);
}
/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
*/
static int quotaDeviceCharacteristics(sqlite3_file *pConn){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
}
/* Pass xShmMap requests through to the original VFS unchanged.
*/
static int quotaShmMap(
sqlite3_file *pConn, /* Handle open on database file */
int iRegion, /* Region to retrieve */
int szRegion, /* Size of regions */
int bExtend, /* True to extend file if necessary */
void volatile **pp /* OUT: Mapped memory */
){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
}
/* Pass xShmLock requests through to the original VFS unchanged.
*/
static int quotaShmLock(
sqlite3_file *pConn, /* Database file holding the shared memory */
int ofst, /* First lock to acquire or release */
int n, /* Number of locks to acquire or release */
int flags /* What to do with the lock */
){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
}
/* Pass xShmBarrier requests through to the original VFS unchanged.
*/
static void quotaShmBarrier(sqlite3_file *pConn){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
pSubOpen->pMethods->xShmBarrier(pSubOpen);
}
/* Pass xShmUnmap requests through to the original VFS unchanged.
*/
static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
}
/************************** Public Interfaces *****************************/
/*
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
** as the VFS that does the actual work. Use the default if
** zOrigVfsName==NULL.
**
** The quota VFS shim is named "quota". It will become the default
** VFS if makeDefault is non-zero.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
** during start-up.
*/
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){
sqlite3_vfs *pOrigVfs;
if( gQuota.isInitialized ) return SQLITE_MISUSE;
pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
if( pOrigVfs==0 ) return SQLITE_ERROR;
assert( pOrigVfs!=&gQuota.sThisVfs );
gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( !gQuota.pMutex ){
return SQLITE_NOMEM;
}
gQuota.isInitialized = 1;
gQuota.pOrigVfs = pOrigVfs;
gQuota.sThisVfs = *pOrigVfs;
gQuota.sThisVfs.xOpen = quotaOpen;
gQuota.sThisVfs.szOsFile += sizeof(quotaConn);
gQuota.sThisVfs.zName = "quota";
gQuota.sIoMethodsV1.iVersion = 1;
gQuota.sIoMethodsV1.xClose = quotaClose;
gQuota.sIoMethodsV1.xRead = quotaRead;
gQuota.sIoMethodsV1.xWrite = quotaWrite;
gQuota.sIoMethodsV1.xTruncate = quotaTruncate;
gQuota.sIoMethodsV1.xSync = quotaSync;
gQuota.sIoMethodsV1.xFileSize = quotaFileSize;
gQuota.sIoMethodsV1.xLock = quotaLock;
gQuota.sIoMethodsV1.xUnlock = quotaUnlock;
gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock;
gQuota.sIoMethodsV1.xFileControl = quotaFileControl;
gQuota.sIoMethodsV1.xSectorSize = quotaSectorSize;
gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics;
gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1;
gQuota.sIoMethodsV2.iVersion = 2;
gQuota.sIoMethodsV2.xShmMap = quotaShmMap;
gQuota.sIoMethodsV2.xShmLock = quotaShmLock;
gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier;
gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap;
sqlite3_vfs_register(&gQuota.sThisVfs, makeDefault);
return SQLITE_OK;
}
/*
** Shutdown the quota system.
**
** All SQLite database connections must be closed before calling this
** routine.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly one while
** shutting down in order to free all remaining quota groups.
*/
int sqlite3_quota_shutdown(void){
quotaGroup *pGroup;
if( gQuota.isInitialized==0 ) return SQLITE_MISUSE;
for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
if( pGroup->pFiles ) return SQLITE_MISUSE;
}
while( gQuota.pGroup ){
pGroup = gQuota.pGroup;
gQuota.pGroup = pGroup->pNext;
pGroup->iLimit = 0;
quotaGroupDeref(pGroup);
}
gQuota.isInitialized = 0;
sqlite3_mutex_free(gQuota.pMutex);
sqlite3_vfs_unregister(&gQuota.sThisVfs);
memset(&gQuota, 0, sizeof(gQuota));
return SQLITE_OK;
}
/*
** Create or destroy a quota group.
**
** The quota group is defined by the zPattern. When calling this routine
** with a zPattern for a quota group that already exists, this routine
** merely updates the iLimit, xCallback, and pArg values for that quota
** group. If zPattern is new, then a new quota group is created.
**
** If the iLimit for a quota group is set to zero, then the quota group
** is disabled and will be deleted when the last database connection using
** the quota group is closed.
**
** Calling this routine on a zPattern that does not exist and with a
** zero iLimit is a no-op.
**
** A quota group must exist with a non-zero iLimit prior to opening
** database connections if those connections are to participate in the
** quota group. Creating a quota group does not affect database connections
** that are already open.
*/
int sqlite3_quota_set(
const char *zPattern, /* The filename pattern */
sqlite3_int64 iLimit, /* New quota to set for this quota group */
void (*xCallback)( /* Callback invoked when going over quota */
const char *zFilename, /* Name of file whose size increases */
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
sqlite3_int64 iSize, /* Total size of all files in the group */
void *pArg /* Client data */
),
void *pArg, /* client data passed thru to callback */
void (*xDestroy)(void*) /* Optional destructor for pArg */
){
quotaGroup *pGroup;
quotaEnter();
pGroup = gQuota.pGroup;
while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){
pGroup = pGroup->pNext;
}
if( pGroup==0 ){
int nPattern = strlen(zPattern);
if( iLimit<=0 ){
quotaLeave();
return SQLITE_OK;
}
pGroup = sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 );
if( pGroup==0 ){
quotaLeave();
return SQLITE_NOMEM;
}
memset(pGroup, 0, sizeof(*pGroup));
pGroup->zPattern = (char*)&pGroup[1];
memcpy((char *)pGroup->zPattern, zPattern, nPattern+1);
if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext;
pGroup->pNext = gQuota.pGroup;
pGroup->ppPrev = &gQuota.pGroup;
gQuota.pGroup = pGroup;
}
pGroup->iLimit = iLimit;
pGroup->xCallback = xCallback;
if( pGroup->xDestroy && pGroup->pArg!=pArg ){
pGroup->xDestroy(pGroup->pArg);
}
pGroup->pArg = pArg;
pGroup->xDestroy = xDestroy;
quotaGroupDeref(pGroup);
quotaLeave();
return SQLITE_OK;
}
/***************************** Test Code ***********************************/
#ifdef SQLITE_TEST
#include <tcl.h>
/*
** Argument passed to a TCL quota-over-limit callback.
*/
typedef struct TclQuotaCallback TclQuotaCallback;
struct TclQuotaCallback {
Tcl_Interp *interp; /* Interpreter in which to run the script */
Tcl_Obj *pScript; /* Script to be run */
};
extern const char *sqlite3TestErrorName(int);
/*
** This is the callback from a quota-over-limit.
*/
static void tclQuotaCallback(
const char *zFilename, /* Name of file whose size increases */
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
sqlite3_int64 iSize, /* Total size of all files in the group */
void *pArg /* Client data */
){
TclQuotaCallback *p; /* Callback script object */
Tcl_Obj *pEval; /* Script to evaluate */
Tcl_Obj *pVarname; /* Name of variable to pass as 2nd arg */
unsigned int rnd; /* Random part of pVarname */
int rc; /* Tcl error code */
p = (TclQuotaCallback *)pArg;
if( p==0 ) return;
pVarname = Tcl_NewStringObj("::piLimit_", -1);
Tcl_IncrRefCount(pVarname);
sqlite3_randomness(sizeof(rnd), (void *)&rnd);
Tcl_AppendObjToObj(pVarname, Tcl_NewIntObj((int)(rnd&0x7FFFFFFF)));
Tcl_ObjSetVar2(p->interp, pVarname, 0, Tcl_NewWideIntObj(*piLimit), 0);
pEval = Tcl_DuplicateObj(p->pScript);
Tcl_IncrRefCount(pEval);
Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zFilename, -1));
Tcl_ListObjAppendElement(0, pEval, pVarname);
Tcl_ListObjAppendElement(0, pEval, Tcl_NewWideIntObj(iSize));
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
if( rc==TCL_OK ){
Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0);
rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit);
Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0);
}
Tcl_DecrRefCount(pEval);
Tcl_DecrRefCount(pVarname);
if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp);
}
/*
** Destructor for a TCL quota-over-limit callback.
*/
static void tclCallbackDestructor(void *pObj){
TclQuotaCallback *p = (TclQuotaCallback*)pObj;
if( p ){
Tcl_DecrRefCount(p->pScript);
sqlite3_free((char *)p);
}
}
/*
** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT
*/
static int test_quota_initialize(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zName; /* Name of new quota VFS */
int makeDefault; /* True to make the new VFS the default */
int rc; /* Value returned by quota_initialize() */
/* Process arguments */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
return TCL_ERROR;
}
zName = Tcl_GetString(objv[1]);
if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
if( zName[0]=='\0' ) zName = 0;
/* Call sqlite3_quota_initialize() */
rc = sqlite3_quota_initialize(zName, makeDefault);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_shutdown
*/
static int test_quota_shutdown(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc; /* Value returned by quota_shutdown() */
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
/* Call sqlite3_quota_shutdown() */
rc = sqlite3_quota_shutdown();
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT
*/
static int test_quota_set(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zPattern; /* File pattern to configure */
sqlite3_int64 iLimit; /* Initial quota in bytes */
Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */
int rc; /* Value returned by quota_set() */
TclQuotaCallback *p; /* Callback object */
int nScript; /* Length of callback script */
void (*xDestroy)(void*); /* Optional destructor for pArg */
void (*xCallback)(const char *, sqlite3_int64 *, sqlite3_int64, void *);
/* Process arguments */
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "PATTERN LIMIT SCRIPT");
return TCL_ERROR;
}
zPattern = Tcl_GetString(objv[1]);
if( Tcl_GetWideIntFromObj(interp, objv[2], &iLimit) ) return TCL_ERROR;
pScript = objv[3];
Tcl_GetStringFromObj(pScript, &nScript);
if( nScript>0 ){
/* Allocate a TclQuotaCallback object */
p = (TclQuotaCallback *)sqlite3_malloc(sizeof(TclQuotaCallback));
if( !p ){
Tcl_SetResult(interp, (char *)"SQLITE_NOMEM", TCL_STATIC);
return TCL_OK;
}
memset(p, 0, sizeof(TclQuotaCallback));
p->interp = interp;
Tcl_IncrRefCount(pScript);
p->pScript = pScript;
xDestroy = tclCallbackDestructor;
xCallback = tclQuotaCallback;
}else{
p = 0;
xDestroy = 0;
xCallback = 0;
}
/* Invoke sqlite3_quota_set() */
rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_dump
*/
static int test_quota_dump(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
Tcl_Obj *pResult;
Tcl_Obj *pGroupTerm;
Tcl_Obj *pFileTerm;
quotaGroup *pGroup;
quotaFile *pFile;
pResult = Tcl_NewObj();
quotaEnter();
for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){
pGroupTerm = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewStringObj(pGroup->zPattern, -1));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewWideIntObj(pGroup->iLimit));
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewWideIntObj(pGroup->iSize));
for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
pFileTerm = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, pFileTerm,
Tcl_NewStringObj(pFile->zFilename, -1));
Tcl_ListObjAppendElement(interp, pFileTerm,
Tcl_NewWideIntObj(pFile->iSize));
Tcl_ListObjAppendElement(interp, pFileTerm,
Tcl_NewWideIntObj(pFile->nRef));
Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm);
}
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
}
quotaLeave();
Tcl_SetObjResult(interp, pResult);
return TCL_OK;
}
/*
** This routine registers the custom TCL commands defined in this
** module. This should be the only procedure visible from outside
** of this module.
*/
int Sqlitequota_Init(Tcl_Interp *interp){
static struct {
char *zName;
Tcl_ObjCmdProc *xProc;
} aCmd[] = {
{ "sqlite3_quota_initialize", test_quota_initialize },
{ "sqlite3_quota_shutdown", test_quota_shutdown },
{ "sqlite3_quota_set", test_quota_set },
{ "sqlite3_quota_dump", test_quota_dump },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
}
return TCL_OK;
}
#endif

294
src/test_rtree.c Normal file
View File

@ -0,0 +1,294 @@
/*
** 2010 August 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.
**
*************************************************************************
** Code for testing all sorts of SQLite interfaces. This code
** is not included in the SQLite library.
*/
#include <sqlite3.h>
/* Solely for the UNUSED_PARAMETER() macro. */
#include "sqliteInt.h"
/*
** Type used to cache parameter information for the "circle" r-tree geometry
** callback.
*/
typedef struct Circle Circle;
struct Circle {
struct Box {
double xmin;
double xmax;
double ymin;
double ymax;
} aBox[2];
double centerx;
double centery;
double radius;
};
/*
** Destructor function for Circle objects allocated by circle_geom().
*/
static void circle_del(void *p){
sqlite3_free(p);
}
/*
** Implementation of "circle" r-tree geometry callback.
*/
static int circle_geom(
sqlite3_rtree_geometry *p,
int nCoord,
double *aCoord,
int *pRes
){
int i; /* Iterator variable */
Circle *pCircle; /* Structure defining circular region */
double xmin, xmax; /* X dimensions of box being tested */
double ymin, ymax; /* X dimensions of box being tested */
if( p->pUser==0 ){
/* If pUser is still 0, then the parameter values have not been tested
** for correctness or stored into a Circle structure yet. Do this now. */
/* This geometry callback is for use with a 2-dimensional r-tree table.
** Return an error if the table does not have exactly 2 dimensions. */
if( nCoord!=4 ) return SQLITE_ERROR;
/* Test that the correct number of parameters (3) have been supplied,
** and that the parameters are in range (that the radius of the circle
** radius is greater than zero). */
if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
/* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
** if the allocation fails. */
pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
if( !pCircle ) return SQLITE_NOMEM;
p->xDelUser = circle_del;
/* Record the center and radius of the circular region. One way that
** tested bounding boxes that intersect the circular region are detected
** is by testing if each corner of the bounding box lies within radius
** units of the center of the circle. */
pCircle->centerx = p->aParam[0];
pCircle->centery = p->aParam[1];
pCircle->radius = p->aParam[2];
/* Define two bounding box regions. The first, aBox[0], extends to
** infinity in the X dimension. It covers the same range of the Y dimension
** as the circular region. The second, aBox[1], extends to infinity in
** the Y dimension and is constrained to the range of the circle in the
** X dimension.
**
** Then imagine each box is split in half along its short axis by a line
** that intersects the center of the circular region. A bounding box
** being tested can be said to intersect the circular region if it contains
** points from each half of either of the two infinite bounding boxes.
*/
pCircle->aBox[0].xmin = pCircle->centerx;
pCircle->aBox[0].xmax = pCircle->centerx;
pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
pCircle->aBox[1].ymin = pCircle->centery;
pCircle->aBox[1].ymax = pCircle->centery;
}
pCircle = (Circle *)p->pUser;
xmin = aCoord[0];
xmax = aCoord[1];
ymin = aCoord[2];
ymax = aCoord[3];
/* Check if any of the 4 corners of the bounding-box being tested lie
** inside the circular region. If they do, then the bounding-box does
** intersect the region of interest. Set the output variable to true and
** return SQLITE_OK in this case. */
for(i=0; i<4; i++){
double x = (i&0x01) ? xmax : xmin;
double y = (i&0x02) ? ymax : ymin;
double d2;
d2 = (x-pCircle->centerx)*(x-pCircle->centerx);
d2 += (y-pCircle->centery)*(y-pCircle->centery);
if( d2<(pCircle->radius*pCircle->radius) ){
*pRes = 1;
return SQLITE_OK;
}
}
/* Check if the bounding box covers any other part of the circular region.
** See comments above for a description of how this test works. If it does
** cover part of the circular region, set the output variable to true
** and return SQLITE_OK. */
for(i=0; i<2; i++){
if( xmin<=pCircle->aBox[i].xmin
&& xmax>=pCircle->aBox[i].xmax
&& ymin<=pCircle->aBox[i].ymin
&& ymax>=pCircle->aBox[i].ymax
){
*pRes = 1;
return SQLITE_OK;
}
}
/* The specified bounding box does not intersect the circular region. Set
** the output variable to zero and return SQLITE_OK. */
*pRes = 0;
return SQLITE_OK;
}
/* END of implementation of "circle" geometry callback.
**************************************************************************
*************************************************************************/
#include <assert.h>
#include "tcl.h"
typedef struct Cube Cube;
struct Cube {
double x;
double y;
double z;
double width;
double height;
double depth;
};
static void cube_context_free(void *p){
sqlite3_free(p);
}
/*
** The context pointer registered along with the 'cube' callback is
** always ((void *)&gHere). This is just to facilitate testing, it is not
** actually used for anything.
*/
static int gHere = 42;
/*
** Implementation of a simple r-tree geom callback to test for intersection
** of r-tree rows with a "cube" shape. Cubes are defined by six scalar
** coordinates as follows:
**
** cube(x, y, z, width, height, depth)
**
** The width, height and depth parameters must all be greater than zero.
*/
static int cube_geom(
sqlite3_rtree_geometry *p,
int nCoord,
double *aCoord,
int *piRes
){
Cube *pCube = (Cube *)p->pUser;
assert( p->pContext==(void *)&gHere );
if( pCube==0 ){
if( p->nParam!=6 || nCoord!=6
|| p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0
){
return SQLITE_ERROR;
}
pCube = (Cube *)sqlite3_malloc(sizeof(Cube));
if( !pCube ){
return SQLITE_NOMEM;
}
pCube->x = p->aParam[0];
pCube->y = p->aParam[1];
pCube->z = p->aParam[2];
pCube->width = p->aParam[3];
pCube->height = p->aParam[4];
pCube->depth = p->aParam[5];
p->pUser = (void *)pCube;
p->xDelUser = cube_context_free;
}
assert( nCoord==6 );
*piRes = 0;
if( aCoord[0]<=(pCube->x+pCube->width)
&& aCoord[1]>=pCube->x
&& aCoord[2]<=(pCube->y+pCube->height)
&& aCoord[3]>=pCube->y
&& aCoord[4]<=(pCube->z+pCube->depth)
&& aCoord[5]>=pCube->z
){
*piRes = 1;
}
return SQLITE_OK;
}
static int register_cube_geom(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
#ifndef SQLITE_ENABLE_RTREE
UNUSED_PARAMETER(clientData);
UNUSED_PARAMETER(interp);
UNUSED_PARAMETER(objc);
UNUSED_PARAMETER(objv);
#else
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
extern const char *sqlite3TestErrorName(int);
sqlite3 *db;
int rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
#endif
return TCL_OK;
}
static int register_circle_geom(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
#ifndef SQLITE_ENABLE_RTREE
UNUSED_PARAMETER(clientData);
UNUSED_PARAMETER(interp);
UNUSED_PARAMETER(objc);
UNUSED_PARAMETER(objv);
#else
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
extern const char *sqlite3TestErrorName(int);
sqlite3 *db;
int rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
#endif
return TCL_OK;
}
int Sqlitetestrtree_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0);
Tcl_CreateObjCommand(interp, "register_circle_geom",register_circle_geom,0,0);
return TCL_OK;
}

356
src/test_superlock.c Normal file
View File

@ -0,0 +1,356 @@
/*
** 2010 November 19
**
** 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.
**
*************************************************************************
** Example code for obtaining an exclusive lock on an SQLite database
** file. This method is complicated, but works for both WAL and rollback
** mode database files. The interface to the example code in this file
** consists of the following two functions:
**
** sqlite3demo_superlock()
** sqlite3demo_superunlock()
*/
#include <sqlite3.h>
#include <string.h> /* memset(), strlen() */
#include <assert.h> /* assert() */
/*
** A structure to collect a busy-handler callback and argument and a count
** of the number of times it has been invoked.
*/
struct SuperlockBusy {
int (*xBusy)(void*,int); /* Pointer to busy-handler function */
void *pBusyArg; /* First arg to pass to xBusy */
int nBusy; /* Number of times xBusy has been invoked */
};
typedef struct SuperlockBusy SuperlockBusy;
/*
** An instance of the following structure is allocated for each active
** superlock. The opaque handle returned by sqlite3demo_superlock() is
** actually a pointer to an instance of this structure.
*/
struct Superlock {
sqlite3 *db; /* Database handle used to lock db */
int bWal; /* True if db is a WAL database */
};
typedef struct Superlock Superlock;
/*
** The pCtx pointer passed to this function is actually a pointer to a
** SuperlockBusy structure. Invoke the busy-handler function encapsulated
** by the structure and return the result.
*/
static int superlockBusyHandler(void *pCtx, int UNUSED){
SuperlockBusy *pBusy = (SuperlockBusy *)pCtx;
if( pBusy->xBusy==0 ) return 0;
return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++);
}
/*
** This function is used to determine if the main database file for
** connection db is open in WAL mode or not. If no error occurs and the
** database file is in WAL mode, set *pbWal to true and return SQLITE_OK.
** If it is not in WAL mode, set *pbWal to false.
**
** If an error occurs, return an SQLite error code. The value of *pbWal
** is undefined in this case.
*/
static int superlockIsWal(Superlock *pLock){
int rc; /* Return Code */
sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */
rc = sqlite3_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
pLock->bWal = 0;
if( SQLITE_ROW==sqlite3_step(pStmt) ){
const char *zMode = (const char *)sqlite3_column_text(pStmt, 0);
if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){
pLock->bWal = 1;
}
}
return sqlite3_finalize(pStmt);
}
/*
** Obtain an exclusive shm-lock on nByte bytes starting at offset idx
** of the file fd. If the lock cannot be obtained immediately, invoke
** the busy-handler until either it is obtained or the busy-handler
** callback returns 0.
*/
static int superlockShmLock(
sqlite3_file *fd, /* Database file handle */
int idx, /* Offset of shm-lock to obtain */
int nByte, /* Number of consective bytes to lock */
SuperlockBusy *pBusy /* Busy-handler wrapper object */
){
int rc;
int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock;
do {
rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE);
}while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) );
return rc;
}
/*
** Obtain the extra locks on the database file required for WAL databases.
** Invoke the supplied busy-handler as required.
*/
static int superlockWalLock(
sqlite3 *db, /* Database handle open on WAL database */
SuperlockBusy *pBusy /* Busy handler wrapper object */
){
int rc; /* Return code */
sqlite3_file *fd = 0; /* Main database file handle */
void volatile *p = 0; /* Pointer to first page of shared memory */
/* Obtain a pointer to the sqlite3_file object open on the main db file. */
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
if( rc!=SQLITE_OK ) return rc;
/* Obtain the "recovery" lock. Normally, this lock is only obtained by
** clients running database recovery.
*/
rc = superlockShmLock(fd, 2, 1, pBusy);
if( rc!=SQLITE_OK ) return rc;
/* Zero the start of the first shared-memory page. This means that any
** clients that open read or write transactions from this point on will
** have to run recovery before proceeding. Since they need the "recovery"
** lock that this process is holding to do that, no new read or write
** transactions may now be opened. Nor can a checkpoint be run, for the
** same reason.
*/
rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p);
if( rc!=SQLITE_OK ) return rc;
memset((void *)p, 0, 32);
/* Obtain exclusive locks on all the "read-lock" slots. Once these locks
** are held, it is guaranteed that there are no active reader, writer or
** checkpointer clients.
*/
rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
return rc;
}
/*
** Release a superlock held on a database file. The argument passed to
** this function must have been obtained from a successful call to
** sqlite3demo_superlock().
*/
void sqlite3demo_superunlock(void *pLock){
Superlock *p = (Superlock *)pLock;
if( p->bWal ){
int rc; /* Return code */
int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE;
sqlite3_file *fd = 0;
rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
if( rc==SQLITE_OK ){
fd->pMethods->xShmLock(fd, 2, 1, flags);
fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
}
}
sqlite3_close(p->db);
sqlite3_free(p);
}
/*
** Obtain a superlock on the database file identified by zPath, using the
** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is
** returned and output variable *ppLock is populated with an opaque handle
** that may be used with sqlite3demo_superunlock() to release the lock.
**
** If an error occurs, *ppLock is set to 0 and an SQLite error code
** (e.g. SQLITE_BUSY) is returned.
**
** If a required lock cannot be obtained immediately and the xBusy parameter
** to this function is not NULL, then xBusy is invoked in the same way
** as a busy-handler registered with SQLite (using sqlite3_busy_handler())
** until either the lock can be obtained or the busy-handler function returns
** 0 (indicating "give up").
*/
int sqlite3demo_superlock(
const char *zPath, /* Path to database file to lock */
const char *zVfs, /* VFS to use to access database file */
int (*xBusy)(void*,int), /* Busy handler callback */
void *pBusyArg, /* Context arg for busy handler */
void **ppLock /* OUT: Context to pass to superunlock() */
){
SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */
int rc; /* Return code */
Superlock *pLock;
pLock = sqlite3_malloc(sizeof(Superlock));
if( !pLock ) return SQLITE_NOMEM;
memset(pLock, 0, sizeof(Superlock));
/* Open a database handle on the file to superlock. */
rc = sqlite3_open_v2(
zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs
);
/* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not
** a WAL database, this is all we need to do.
**
** A wrapper function is used to invoke the busy-handler instead of
** registering the busy-handler function supplied by the user directly
** with SQLite. This is because the same busy-handler function may be
** invoked directly later on when attempting to obtain the extra locks
** required in WAL mode. By using the wrapper, we are able to guarantee
** that the "nBusy" integer parameter passed to the users busy-handler
** represents the total number of busy-handler invocations made within
** this call to sqlite3demo_superlock(), including any made during the
** "BEGIN EXCLUSIVE".
*/
if( rc==SQLITE_OK ){
busy.xBusy = xBusy;
busy.pBusyArg = pBusyArg;
sqlite3_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy);
rc = sqlite3_exec(pLock->db, "BEGIN EXCLUSIVE", 0, 0, 0);
}
/* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL
** database, call superlockWalLock() to obtain the extra locks required
** to prevent readers, writers and/or checkpointers from accessing the
** db while this process is holding the superlock.
**
** Before attempting any WAL locks, commit the transaction started above
** to drop the WAL read and write locks currently held. Otherwise, the
** new WAL locks may conflict with the old.
*/
if( rc==SQLITE_OK ){
if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = superlockWalLock(pLock->db, &busy);
}
}
}
if( rc!=SQLITE_OK ){
sqlite3demo_superunlock(pLock);
*ppLock = 0;
}else{
*ppLock = pLock;
}
return rc;
}
/*
** End of example code. Everything below here is the test harness.
**************************************************************************
**************************************************************************
*************************************************************************/
#ifdef SQLITE_TEST
#include <tcl.h>
struct InterpAndScript {
Tcl_Interp *interp;
Tcl_Obj *pScript;
};
typedef struct InterpAndScript InterpAndScript;
static void superunlock_del(ClientData cd){
sqlite3demo_superunlock((void *)cd);
}
static int superunlock_cmd(
ClientData cd,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
return TCL_OK;
}
static int superlock_busy(void *pCtx, int nBusy){
InterpAndScript *p = (InterpAndScript *)pCtx;
Tcl_Obj *pEval; /* Script to evaluate */
int iVal = 0; /* Value to return */
pEval = Tcl_DuplicateObj(p->pScript);
Tcl_IncrRefCount(pEval);
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
Tcl_DecrRefCount(pEval);
return iVal;
}
/*
** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
*/
static int superlock_cmd(
ClientData cd,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
void *pLock; /* Lock context */
char *zPath;
char *zVfs = 0;
InterpAndScript busy = {0, 0};
int (*xBusy)(void*,int) = 0; /* Busy handler callback */
int rc; /* Return code from sqlite3demo_superlock() */
if( objc<3 || objc>5 ){
Tcl_WrongNumArgs(
interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
return TCL_ERROR;
}
zPath = Tcl_GetString(objv[2]);
if( objc>3 ){
zVfs = Tcl_GetString(objv[3]);
if( strlen(zVfs)==0 ) zVfs = 0;
}
if( objc>4 ){
busy.interp = interp;
busy.pScript = objv[4];
xBusy = superlock_busy;
}
rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
assert( rc==SQLITE_OK || pLock==0 );
assert( rc!=SQLITE_OK || pLock!=0 );
if( rc!=SQLITE_OK ){
extern const char *sqlite3ErrStr(int);
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
return TCL_ERROR;
}
Tcl_CreateObjCommand(
interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
);
Tcl_SetObjResult(interp, objv[1]);
return TCL_OK;
}
int SqliteSuperlock_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
return TCL_OK;
}
#endif

View File

@ -26,6 +26,7 @@
** -default BOOLEAN (True to make the vfs default. Default false)
** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile)
** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname)
** -iversion INTEGER (Value for sqlite3_vfs.iVersion)
*/
#include "sqlite3.h"
@ -539,6 +540,7 @@ static int tvfsOpen(
pFd->zFilename = zName;
pFd->pVfs = pVfs;
pFd->pReal = (sqlite3_file *)&pFd[1];
memset(pTestfile, 0, sizeof(TestvfsFile));
pTestfile->pFd = pFd;
/* Evaluate the Tcl script:

View File

@ -638,6 +638,7 @@ static void updateVirtualTable(
assert( v );
ephemTab = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
/* fill the ephemeral table
*/

View File

@ -215,6 +215,12 @@ int sqlite3Dequote(char *z){
/*
** Some systems have stricmp(). Others have strcasecmp(). Because
** there is no consistency, we will define our own.
**
** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows
** applications and extensions to compare the contents of two buffers
** containing UTF-8 strings in a case-independent fashion, using the same
** definition of case independence that SQLite uses internally when
** comparing identifiers.
*/
int sqlite3StrICmp(const char *zLeft, const char *zRight){
register unsigned char *a, *b;
@ -232,121 +238,111 @@ int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
}
/*
** Return TRUE if z is a pure numeric string. Return FALSE and leave
** *realnum unchanged if the string contains any character which is not
** part of a number.
** The string z[] is an text representation of a real number.
** Convert this string to a double and write it into *pResult.
**
** If the string is pure numeric, set *realnum to TRUE if the string
** contains the '.' character or an "E+000" style exponentiation suffix.
** Otherwise set *realnum to FALSE. Note that just becaue *realnum is
** false does not mean that the number can be successfully converted into
** an integer - it might be too big.
** The string z[] is length bytes in length (bytes, not characters) and
** uses the encoding enc. The string is not necessarily zero-terminated.
**
** An empty string is considered non-numeric.
** Return TRUE if the result is a valid real number (or integer) and FALSE
** if the string is empty or contains extraneous text. Valid numbers
** are in one of these formats:
**
** [+-]digits[E[+-]digits]
** [+-]digits.[digits][E[+-]digits]
** [+-].digits[E[+-]digits]
**
** Leading and trailing whitespace is ignored for the purpose of determining
** validity.
**
** If some prefix of the input string is a valid number, this routine
** returns FALSE but it still converts the prefix and writes the result
** into *pResult.
*/
int sqlite3IsNumber(const char *z, int *realnum, u8 enc){
int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
#ifndef SQLITE_OMIT_FLOATING_POINT
int incr = (enc==SQLITE_UTF8?1:2);
if( enc==SQLITE_UTF16BE ) z++;
if( *z=='-' || *z=='+' ) z += incr;
if( !sqlite3Isdigit(*z) ){
return 0;
}
z += incr;
*realnum = 0;
while( sqlite3Isdigit(*z) ){ z += incr; }
#ifndef SQLITE_OMIT_FLOATING_POINT
if( *z=='.' ){
z += incr;
if( !sqlite3Isdigit(*z) ) return 0;
while( sqlite3Isdigit(*z) ){ z += incr; }
*realnum = 1;
}
if( *z=='e' || *z=='E' ){
z += incr;
if( *z=='+' || *z=='-' ) z += incr;
if( !sqlite3Isdigit(*z) ) return 0;
while( sqlite3Isdigit(*z) ){ z += incr; }
*realnum = 1;
}
#endif
return *z==0;
}
/*
** The string z[] is an ASCII representation of a real number.
** Convert this string to a double.
**
** This routine assumes that z[] really is a valid number. If it
** is not, the result is undefined.
**
** This routine is used instead of the library atof() function because
** the library atof() might want to use "," as the decimal point instead
** of "." depending on how locale is set. But that would cause problems
** for SQL. So this routine always uses "." regardless of locale.
*/
int sqlite3AtoF(const char *z, double *pResult){
#ifndef SQLITE_OMIT_FLOATING_POINT
const char *zBegin = z;
const char *zEnd = z + length;
/* sign * significand * (10 ^ (esign * exponent)) */
int sign = 1; /* sign of significand */
i64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int sign = 1; /* sign of significand */
i64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int eValid = 1; /* True exponent is either not used or is well-formed */
double result;
int nDigits = 0;
*pResult = 0.0; /* Default return value, in case of an error */
if( enc==SQLITE_UTF16BE ) z++;
/* skip leading spaces */
while( sqlite3Isspace(*z) ) z++;
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
if( z>=zEnd ) return 0;
/* get sign of significand */
if( *z=='-' ){
sign = -1;
z++;
z+=incr;
}else if( *z=='+' ){
z++;
z+=incr;
}
/* skip leading zeroes */
while( z[0]=='0' ) z++, nDigits++;
while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++;
/* copy max significant digits to significand */
while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
z++, nDigits++;
z+=incr, nDigits++;
}
/* skip non-significant significand digits
** (increase exponent by d to shift decimal left) */
while( sqlite3Isdigit(*z) ) z++, nDigits++, d++;
while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++;
if( z>=zEnd ) goto do_atof_calc;
/* if decimal point is present */
if( *z=='.' ){
z++;
z+=incr;
/* copy digits from after decimal to significand
** (decrease exponent by d to shift decimal right) */
while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
z++, nDigits++, d--;
z+=incr, nDigits++, d--;
}
/* skip non-significant digits */
while( sqlite3Isdigit(*z) ) z++, nDigits++;
while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++;
}
if( z>=zEnd ) goto do_atof_calc;
/* if exponent is present */
if( *z=='e' || *z=='E' ){
z++;
z+=incr;
eValid = 0;
if( z>=zEnd ) goto do_atof_calc;
/* get sign of exponent */
if( *z=='-' ){
esign = -1;
z++;
z+=incr;
}else if( *z=='+' ){
z++;
z+=incr;
}
/* copy digits to exponent */
while( sqlite3Isdigit(*z) ){
while( z<zEnd && sqlite3Isdigit(*z) ){
e = e*10 + (*z - '0');
z++;
z+=incr;
eValid = 1;
}
}
/* skip trailing spaces */
if( nDigits && eValid ){
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
}
do_atof_calc:
/* adjust exponent by d, and update sign */
e = (e*esign) + d;
if( e<0 ) {
@ -405,10 +401,10 @@ int sqlite3AtoF(const char *z, double *pResult){
/* store the result */
*pResult = result;
/* return number of characters used */
return (int)(z - zBegin);
/* return true if number and no extra non-whitespace chracters after */
return z>=zEnd && nDigits>0 && eValid;
#else
return sqlite3Atoi64(z, pResult);
return !sqlite3Atoi64(z, pResult, length, enc);
#endif /* SQLITE_OMIT_FLOATING_POINT */
}
@ -416,20 +412,26 @@ int sqlite3AtoF(const char *z, double *pResult){
** Compare the 19-character string zNum against the text representation
** value 2^63: 9223372036854775808. Return negative, zero, or positive
** if zNum is less than, equal to, or greater than the string.
** Note that zNum must contain exactly 19 characters.
**
** Unlike memcmp() this routine is guaranteed to return the difference
** in the values of the last digit if the only difference is in the
** last digit. So, for example,
**
** compare2pow63("9223372036854775800")
** compare2pow63("9223372036854775800", 1)
**
** will return -8.
*/
static int compare2pow63(const char *zNum){
int c;
c = memcmp(zNum,"922337203685477580",18)*10;
static int compare2pow63(const char *zNum, int incr){
int c = 0;
int i;
/* 012345678901234567 */
const char *pow63 = "922337203685477580";
for(i=0; c==0 && i<18; i++){
c = (zNum[i*incr]-pow63[i])*10;
}
if( c==0 ){
c = zNum[18] - '8';
c = zNum[18*incr] - '8';
testcase( c==(-1) );
testcase( c==0 );
testcase( c==(+1) );
@ -439,94 +441,60 @@ static int compare2pow63(const char *zNum){
/*
** Return TRUE if zNum is a 64-bit signed integer and write
** the value of the integer into *pNum. If zNum is not an integer
** or is an integer that is too large to be expressed with 64 bits,
** then return false.
** Convert zNum to a 64-bit signed integer and write
** the value of the integer into *pNum.
** If zNum is exactly 9223372036854665808, return 2.
** This is a special case as the context will determine
** if it is too big (used as a negative).
** If zNum is not an integer or is an integer that
** is too large to be expressed with 64 bits,
** then return 1. Otherwise return 0.
**
** When this routine was originally written it dealt with only
** 32-bit numbers. At that time, it was much faster than the
** atoi() library routine in RedHat 7.2.
** length is the number of bytes in the string (bytes, not characters).
** The string is not necessarily zero-terminated. The encoding is
** given by enc.
*/
int sqlite3Atoi64(const char *zNum, i64 *pNum){
int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
int incr = (enc==SQLITE_UTF8?1:2);
i64 v = 0;
int neg;
int i, c;
int neg = 0; /* assume positive */
int i;
int c = 0;
const char *zStart;
while( sqlite3Isspace(*zNum) ) zNum++;
const char *zEnd = zNum + length;
if( enc==SQLITE_UTF16BE ) zNum++;
while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
if( zNum>=zEnd ) goto do_atoi_calc;
if( *zNum=='-' ){
neg = 1;
zNum++;
zNum+=incr;
}else if( *zNum=='+' ){
neg = 0;
zNum++;
}else{
neg = 0;
zNum+=incr;
}
do_atoi_calc:
zStart = zNum;
while( zNum[0]=='0' ){ zNum++; } /* Skip over leading zeros. Ticket #2454 */
for(i=0; (c=zNum[i])>='0' && c<='9'; i++){
while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
v = v*10 + c - '0';
}
*pNum = neg ? -v : v;
testcase( i==18 );
testcase( i==19 );
testcase( i==20 );
if( c!=0 || (i==0 && zStart==zNum) || i>19 ){
if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){
/* zNum is empty or contains non-numeric text or is longer
** than 19 digits (thus guaranting that it is too large) */
return 0;
}else if( i<19 ){
/* Less than 19 digits, so we know that it fits in 64 bits */
** than 19 digits (thus guaranteeing that it is too large) */
return 1;
}else if( i<19*incr ){
/* Less than 19 digits, so we know that it fits in 64 bits */
return 0;
}else{
/* 19-digit numbers must be no larger than 9223372036854775807 if positive
** or 9223372036854775808 if negative. Note that 9223372036854665808
** is 2^63. */
return compare2pow63(zNum)<neg;
}
}
/*
** The string zNum represents an unsigned integer. The zNum string
** consists of one or more digit characters and is terminated by
** a zero character. Any stray characters in zNum result in undefined
** behavior.
**
** If the unsigned integer that zNum represents will fit in a
** 64-bit signed integer, return TRUE. Otherwise return FALSE.
**
** If the negFlag parameter is true, that means that zNum really represents
** a negative number. (The leading "-" is omitted from zNum.) This
** parameter is needed to determine a boundary case. A string
** of "9223373036854775808" returns false if negFlag is false or true
** if negFlag is true.
**
** Leading zeros are ignored.
*/
int sqlite3FitsIn64Bits(const char *zNum, int negFlag){
int i;
int neg = 0;
assert( zNum[0]>='0' && zNum[0]<='9' ); /* zNum is an unsigned number */
if( negFlag ) neg = 1-neg;
while( *zNum=='0' ){
zNum++; /* Skip leading zeros. Ticket #2454 */
}
for(i=0; zNum[i]; i++){ assert( zNum[i]>='0' && zNum[i]<='9' ); }
testcase( i==18 );
testcase( i==19 );
testcase( i==20 );
if( i<19 ){
/* Guaranteed to fit if less than 19 digits */
return 1;
}else if( i>19 ){
/* Guaranteed to be too big if greater than 19 digits */
return 0;
}else{
/* Compare against 2^63. */
return compare2pow63(zNum)<neg;
** is 2^63. Return 1 if to large */
c=compare2pow63(zNum, incr);
if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */
return c<neg ? 0 : 1;
}
}
@ -573,6 +541,16 @@ int sqlite3GetInt32(const char *zNum, int *pValue){
return 1;
}
/*
** Return a 32-bit integer value extracted from a string. If the
** string is not an integer, just return 0.
*/
int sqlite3Atoi(const char *z){
int x = 0;
if( z ) sqlite3GetInt32(z, &x);
return x;
}
/*
** The variable-length integer encoding is as follows:
**

View File

@ -110,6 +110,10 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
return SQLITE_ERROR;
}
if( db->activeVdbeCnt>1 ){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
return SQLITE_ERROR;
}
/* Save the current value of the database flags so that it can be
** restored before returning. Then set the writable-schema flag, and

View File

@ -46,6 +46,17 @@
#include "sqliteInt.h"
#include "vdbeInt.h"
/*
** Invoke this macro on memory cells just prior to changing the
** value of the cell. This macro verifies that shallow copies are
** not misused.
*/
#ifdef SQLITE_DEBUG
# define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M)
#else
# define memAboutToChange(P,M)
#endif
/*
** The following global variable is incremented every time a cursor
** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test
@ -238,31 +249,17 @@ static VdbeCursor *allocateCursor(
*/
static void applyNumericAffinity(Mem *pRec){
if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){
int realnum;
double rValue;
i64 iValue;
u8 enc = pRec->enc;
sqlite3VdbeMemNulTerminate(pRec);
if( (pRec->flags&MEM_Str) && sqlite3IsNumber(pRec->z, &realnum, enc) ){
i64 value;
char *zUtf8 = pRec->z;
#ifndef SQLITE_OMIT_UTF16
if( enc!=SQLITE_UTF8 ){
assert( pRec->db );
zUtf8 = sqlite3Utf16to8(pRec->db, pRec->z, pRec->n, enc);
if( !zUtf8 ) return;
}
#endif
if( !realnum && sqlite3Atoi64(zUtf8, &value) ){
pRec->u.i = value;
MemSetTypeFlag(pRec, MEM_Int);
}else{
sqlite3AtoF(zUtf8, &pRec->r);
MemSetTypeFlag(pRec, MEM_Real);
}
#ifndef SQLITE_OMIT_UTF16
if( enc!=SQLITE_UTF8 ){
sqlite3DbFree(pRec->db, zUtf8);
}
#endif
if( (pRec->flags&MEM_Str)==0 ) return;
if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
pRec->u.i = iValue;
pRec->flags |= MEM_Int;
}else{
pRec->r = rValue;
pRec->flags |= MEM_Real;
}
}
}
@ -314,13 +311,13 @@ static void applyAffinity(
** into a numeric representation. Use either INTEGER or REAL whichever
** is appropriate. But only do the conversion if it is possible without
** loss of information and return the revised type of the argument.
**
** This is an EXPERIMENTAL api and is subject to change or removal.
*/
int sqlite3_value_numeric_type(sqlite3_value *pVal){
Mem *pMem = (Mem*)pVal;
applyNumericAffinity(pMem);
sqlite3VdbeMemStoreType(pMem);
if( pMem->type==SQLITE_TEXT ){
applyNumericAffinity(pMem);
sqlite3VdbeMemStoreType(pMem);
}
return pMem->type;
}
@ -667,6 +664,7 @@ int sqlite3VdbeExec(
assert( pOp->p2>0 );
assert( pOp->p2<=p->nMem );
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
sqlite3VdbeMemReleaseExternal(pOut);
pOut->flags = MEM_Int;
}
@ -676,25 +674,30 @@ int sqlite3VdbeExec(
if( (pOp->opflags & OPFLG_IN1)!=0 ){
assert( pOp->p1>0 );
assert( pOp->p1<=p->nMem );
assert( memIsValid(&aMem[pOp->p1]) );
REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
}
if( (pOp->opflags & OPFLG_IN2)!=0 ){
assert( pOp->p2>0 );
assert( pOp->p2<=p->nMem );
assert( memIsValid(&aMem[pOp->p2]) );
REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_IN3)!=0 ){
assert( pOp->p3>0 );
assert( pOp->p3<=p->nMem );
assert( memIsValid(&aMem[pOp->p3]) );
REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
}
if( (pOp->opflags & OPFLG_OUT2)!=0 ){
assert( pOp->p2>0 );
assert( pOp->p2<=p->nMem );
memAboutToChange(p, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_OUT3)!=0 ){
assert( pOp->p3>0 );
assert( pOp->p3<=p->nMem );
memAboutToChange(p, &aMem[pOp->p3]);
}
#endif
@ -756,6 +759,7 @@ case OP_Goto: { /* jump */
case OP_Gosub: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
assert( (pIn1->flags & MEM_Dyn)==0 );
memAboutToChange(p, pIn1);
pIn1->flags = MEM_Int;
pIn1->u.i = pc;
REGISTER_TRACE(pOp->p1, pIn1);
@ -961,11 +965,7 @@ case OP_Null: { /* out2-prerelease */
/* Opcode: Blob P1 P2 * P4
**
** P4 points to a blob of data P1 bytes long. Store this
** blob in register P2. This instruction is not coded directly
** by the compiler. Instead, the compiler layer specifies
** an OP_HexBlob opcode, with the hex string representation of
** the blob as P4. This opcode is transformed to an OP_Blob
** the first time it is executed.
** blob in register P2.
*/
case OP_Blob: { /* out2-prerelease */
assert( pOp->p1 <= SQLITE_MAX_LENGTH );
@ -1019,6 +1019,8 @@ case OP_Move: {
while( n-- ){
assert( pOut<=&aMem[p->nMem] );
assert( pIn1<=&aMem[p->nMem] );
assert( memIsValid(pIn1) );
memAboutToChange(p, pOut);
zMalloc = pOut->zMalloc;
pOut->zMalloc = 0;
sqlite3VdbeMemMove(pOut, pIn1);
@ -1064,6 +1066,9 @@ case OP_SCopy: { /* in1, out2 */
pOut = &aMem[pOp->p2];
assert( pOut!=pIn1 );
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
#ifdef SQLITE_DEBUG
if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1;
#endif
REGISTER_TRACE(pOp->p2, pOut);
break;
}
@ -1122,6 +1127,10 @@ case OP_ResultRow: {
*/
pMem = p->pResultSet = &aMem[pOp->p1];
for(i=0; i<pOp->p2; i++){
assert( memIsValid(&pMem[i]) );
Deephemeralize(&pMem[i]);
assert( (pMem[i].flags & MEM_Ephem)==0
|| (pMem[i].flags & (MEM_Str|MEM_Blob))==0 );
sqlite3VdbeMemNulTerminate(&pMem[i]);
sqlite3VdbeMemStoreType(&pMem[i]);
REGISTER_TRACE(pOp->p1+i, &pMem[i]);
@ -1347,12 +1356,17 @@ case OP_Function: {
n = pOp->p5;
apVal = p->apArg;
assert( apVal || n==0 );
assert( pOp->p3>0 && pOp->p3<=p->nMem );
pOut = &aMem[pOp->p3];
memAboutToChange(p, pOut);
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) );
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
pArg = &aMem[pOp->p2];
for(i=0; i<n; i++, pArg++){
assert( memIsValid(pArg) );
apVal[i] = pArg;
Deephemeralize(pArg);
sqlite3VdbeMemStoreType(pArg);
REGISTER_TRACE(pOp->p2+i, pArg);
}
@ -1366,8 +1380,6 @@ case OP_Function: {
ctx.pFunc = ctx.pVdbeFunc->pFunc;
}
assert( pOp->p3>0 && pOp->p3<=p->nMem );
pOut = &aMem[pOp->p3];
ctx.s.flags = MEM_Null;
ctx.s.db = db;
ctx.s.xDel = 0;
@ -1387,7 +1399,7 @@ case OP_Function: {
assert( pOp[-1].opcode==OP_CollSeq );
ctx.pColl = pOp[-1].p4.pColl;
}
(*ctx.pFunc->xFunc)(&ctx, n, apVal);
(*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
if( db->mallocFailed ){
/* Even though a malloc() has failed, the implementation of the
** user function may have called an sqlite3_result_XXX() function
@ -1439,7 +1451,7 @@ case OP_Function: {
/* Opcode: ShiftLeft P1 P2 P3 * *
**
** Shift the integer value in register P2 to the left by the
** number of bits specified by the integer in regiser P1.
** number of bits specified by the integer in register P1.
** Store the result in register P3.
** If either input is NULL, the result is NULL.
*/
@ -1487,6 +1499,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
*/
case OP_AddImm: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
pIn1->u.i += pOp->p2;
break;
@ -1546,6 +1559,7 @@ case OP_RealAffinity: { /* in1 */
*/
case OP_ToText: { /* same as TK_TO_TEXT, in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
if( pIn1->flags & MEM_Null ) break;
assert( MEM_Str==(MEM_Blob>>3) );
pIn1->flags |= (pIn1->flags&MEM_Blob)>>3;
@ -1592,16 +1606,14 @@ case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */
*/
case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){
sqlite3VdbeMemNumerify(pIn1);
}
sqlite3VdbeMemNumerify(pIn1);
break;
}
#endif /* SQLITE_OMIT_CAST */
/* Opcode: ToInt P1 * * * *
**
** Force the value in register P1 be an integer. If
** Force the value in register P1 to be an integer. If
** The value is currently a real number, drop its fractional part.
** If the value is text or blob, try to convert it to an integer using the
** equivalent of atoi() and store 0 if no such conversion is possible.
@ -1628,6 +1640,7 @@ case OP_ToInt: { /* same as TK_TO_INT, in1 */
*/
case OP_ToReal: { /* same as TK_TO_REAL, in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
if( (pIn1->flags & MEM_Null)==0 ){
sqlite3VdbeMemRealify(pIn1);
}
@ -1642,7 +1655,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
**
** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL
** bit is clear then fall thru if either operand is NULL.
** bit is clear then fall through if either operand is NULL.
**
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
@ -1770,6 +1783,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Int);
pOut->u.i = res;
REGISTER_TRACE(pOp->p2, pOut);
@ -1801,8 +1815,8 @@ case OP_Permutation: {
/* Opcode: Compare P1 P2 P3 P4 *
**
** Compare to vectors of registers in reg(P1)..reg(P1+P3-1) (all this
** one "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this
** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
** the comparison for use by the next OP_Jump instruct.
**
** P4 is a KeyInfo structure that defines collating sequences and sort
@ -1842,6 +1856,8 @@ case OP_Compare: {
#endif /* SQLITE_DEBUG */
for(i=0; i<n; i++){
idx = aPermute ? aPermute[i] : i;
assert( memIsValid(&aMem[p1+idx]) );
assert( memIsValid(&aMem[p2+idx]) );
REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
assert( i<pKeyInfo->nField );
@ -2067,6 +2083,7 @@ case OP_Column: {
assert( p1<p->nCursor );
assert( pOp->p3>0 && pOp->p3<=p->nMem );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
MemSetTypeFlag(pDest, MEM_Null);
zRec = 0;
@ -2114,6 +2131,7 @@ case OP_Column: {
}else if( pC->pseudoTableReg>0 ){
pReg = &aMem[pC->pseudoTableReg];
assert( pReg->flags & MEM_Blob );
assert( memIsValid(pReg) );
payloadSize = pReg->n;
zRec = pReg->z;
pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr;
@ -2336,6 +2354,7 @@ case OP_Affinity: {
pIn1 = &aMem[pOp->p1];
while( (cAff = *(zAffinity++))!=0 ){
assert( pIn1 <= &p->aMem[p->nMem] );
assert( memIsValid(pIn1) );
ExpandBlob(pIn1);
applyAffinity(pIn1, cAff, encoding);
pIn1++;
@ -2345,12 +2364,9 @@ case OP_Affinity: {
/* Opcode: MakeRecord P1 P2 P3 P4 *
**
** Convert P2 registers beginning with P1 into a single entry
** suitable for use as a data record in a database table or as a key
** in an index. The details of the format are irrelevant as long as
** the OP_Column opcode can decode the record later.
** Refer to source code comments for the details of the record
** format.
** Convert P2 registers beginning with P1 into the [record format]
** use as a data record in a database table or as a key
** in an index. The OP_Column opcode can decode the record later.
**
** P4 may be a string that is P2 characters long. The nth character of the
** string indicates the column affinity that should be used for the nth
@ -2405,10 +2421,16 @@ case OP_MakeRecord: {
pLast = &pData0[nField-1];
file_format = p->minWriteFileFormat;
/* Identify the output register */
assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
pOut = &aMem[pOp->p3];
memAboutToChange(p, pOut);
/* Loop through the elements that will make up the record to figure
** out how much space is required for the new record.
*/
for(pRec=pData0; pRec<=pLast; pRec++){
assert( memIsValid(pRec) );
if( zAffinity ){
applyAffinity(pRec, zAffinity[pRec-pData0], encoding);
}
@ -2443,8 +2465,6 @@ case OP_MakeRecord: {
** be one of the input registers (because the following call to
** sqlite3VdbeMemGrow() could clobber the value before it is used).
*/
assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
pOut = &aMem[pOp->p3];
if( sqlite3VdbeMemGrow(pOut, (int)nByte, 0) ){
goto no_mem;
}
@ -2613,6 +2633,7 @@ case OP_Savepoint: {
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
sqlite3ExpirePreparedStatements(db);
sqlite3ResetInternalSchema(db, 0);
db->flags = (db->flags | SQLITE_InternChanges);
}
}
@ -2991,6 +3012,8 @@ case OP_OpenWrite: {
assert( p2>0 );
assert( p2<=p->nMem );
pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
sqlite3VdbeMemIntegerify(pIn2);
p2 = (int)pIn2->u.i;
/* The p2 value always comes from a prior OP_CreateTable opcode and
@ -3013,6 +3036,7 @@ case OP_OpenWrite: {
pCur = allocateCursor(p, pOp->p1, nField, iDb, 1);
if( pCur==0 ) goto no_mem;
pCur->nullRow = 1;
pCur->isOrdered = 1;
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
pCur->pKeyInfo = pKeyInfo;
@ -3063,7 +3087,7 @@ case OP_OpenWrite: {
case OP_OpenAutoindex:
case OP_OpenEphemeral: {
VdbeCursor *pCx;
static const int openFlags =
static const int vfsFlags =
SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXCLUSIVE |
@ -3074,21 +3098,21 @@ case OP_OpenEphemeral: {
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags,
&pCx->pBt);
rc = sqlite3BtreeOpen(0, db, &pCx->pBt,
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
}
if( rc==SQLITE_OK ){
/* If a transient index is required, create it by calling
** sqlite3BtreeCreateTable() with the BTREE_ZERODATA flag before
** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
** opening it. If a transient table is required, just use the
** automatically created table with root-page 1 (an INTKEY table).
** automatically created table with root-page 1 (an BLOB_INTKEY table).
*/
if( pOp->p4.pKeyInfo ){
int pgno;
assert( pOp->p4type==P4_KEYINFO );
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_ZERODATA);
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY);
if( rc==SQLITE_OK ){
assert( pgno==MASTER_ROOT+1 );
rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1,
@ -3102,6 +3126,7 @@ case OP_OpenEphemeral: {
pCx->isTable = 1;
}
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
pCx->isIndex = !pCx->isTable;
break;
}
@ -3217,6 +3242,7 @@ case OP_SeekGt: { /* jump, in3 */
assert( OP_SeekLe == OP_SeekLt+1 );
assert( OP_SeekGe == OP_SeekLt+2 );
assert( OP_SeekGt == OP_SeekLt+3 );
assert( pC->isOrdered );
if( pC->pCursor!=0 ){
oc = pOp->opcode;
pC->nullRow = 0;
@ -3299,6 +3325,9 @@ case OP_SeekGt: { /* jump, in3 */
assert( oc!=OP_SeekLt || r.flags==0 );
r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
ExpandBlob(r.aMem);
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res);
if( rc!=SQLITE_OK ){
@ -3423,11 +3452,14 @@ case OP_Found: { /* jump, in3 */
r.pKeyInfo = pC->pKeyInfo;
r.nField = (u16)pOp->p4.i;
r.aMem = pIn3;
#ifdef SQLITE_DEBUG
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
r.flags = UNPACKED_PREFIX_MATCH;
pIdxKey = &r;
}else{
assert( pIn3->flags & MEM_Blob );
ExpandBlob(pIn3);
assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z,
aTempRec, sizeof(aTempRec));
if( pIdxKey==0 ){
@ -3520,6 +3552,9 @@ case OP_IsUnique: { /* jump, in3 */
r.nField = nField + 1;
r.flags = UNPACKED_PREFIX_SEARCH;
r.aMem = aMx;
#ifdef SQLITE_DEBUG
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
/* Extract the value of R from register P3. */
sqlite3VdbeMemIntegerify(pIn3);
@ -3542,7 +3577,7 @@ case OP_IsUnique: { /* jump, in3 */
**
** Use the content of register P3 as a integer key. If a record
** with that key does not exist in table of P1, then jump to P2.
** If the record does exist, then fall thru. The cursor is left
** If the record does exist, then fall through. The cursor is left
** pointing to the record if it exists.
**
** The difference between this operation and NotFound is that this
@ -3696,7 +3731,9 @@ case OP_NewRowid: { /* out2-prerelease */
/* Assert that P3 is a valid memory cell. */
assert( pOp->p3<=p->nMem );
pMem = &aMem[pOp->p3];
memAboutToChange(p, pMem);
}
assert( memIsValid(pMem) );
REGISTER_TRACE(pOp->p3, pMem);
sqlite3VdbeMemIntegerify(pMem);
@ -3715,29 +3752,36 @@ case OP_NewRowid: { /* out2-prerelease */
sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0);
}
if( pC->useRandomRowid ){
/* IMPLEMENTATION-OF: R-48598-02938 If the largest ROWID is equal to the
/* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the
** largest possible integer (9223372036854775807) then the database
** engine starts picking candidate ROWIDs at random until it finds one
** that is not previously used.
*/
** engine starts picking positive candidate ROWIDs at random until
** it finds one that is not previously used. */
assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is
** an AUTOINCREMENT table. */
/* on the first attempt, simply do one more than previous */
v = db->lastRowid;
v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
v++; /* ensure non-zero */
cnt = 0;
do{
if( cnt==0 && (v&0xffffff)==v ){
v++;
while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v,
0, &res))==SQLITE_OK)
&& (res==0)
&& (++cnt<100)){
/* collision - try another random rowid */
sqlite3_randomness(sizeof(v), &v);
if( cnt<5 ){
/* try "small" random rowids for the initial attempts */
v &= 0xffffff;
}else{
sqlite3_randomness(sizeof(v), &v);
if( cnt<5 ) v &= 0xffffff;
v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
}
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v, 0, &res);
cnt++;
}while( cnt<100 && rc==SQLITE_OK && res==0 );
v++; /* ensure non-zero */
}
if( rc==SQLITE_OK && res==0 ){
rc = SQLITE_FULL; /* IMP: R-38219-53002 */
goto abort_due_to_error;
}
assert( v>0 ); /* EV: R-40812-03570 */
}
pC->rowidIsValid = 0;
pC->deferredMoveto = 0;
@ -3805,6 +3849,7 @@ case OP_InsertInt: {
pData = &aMem[pOp->p2];
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( memIsValid(pData) );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->pCursor!=0 );
@ -3815,6 +3860,7 @@ case OP_InsertInt: {
if( pOp->opcode==OP_Insert ){
pKey = &aMem[pOp->p3];
assert( pKey->flags & MEM_Int );
assert( memIsValid(pKey) );
REGISTER_TRACE(pOp->p3, pKey);
iKey = pKey->u.i;
}else{
@ -3962,6 +4008,7 @@ case OP_RowData: {
i64 n64;
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
/* Note that RowKey and RowData are really exactly the same instruction */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
@ -4290,6 +4337,9 @@ case OP_IdxDelete: {
r.nField = (u16)pOp->p3;
r.flags = 0;
r.aMem = &aMem[pOp->p2];
#ifdef SQLITE_DEBUG
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
if( rc==SQLITE_OK && res==0 ){
rc = sqlite3BtreeDelete(pCrsr);
@ -4370,6 +4420,7 @@ case OP_IdxGE: { /* jump */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->isOrdered );
if( ALWAYS(pC->pCursor!=0) ){
assert( pC->deferredMoveto==0 );
assert( pOp->p5==0 || pOp->p5==1 );
@ -4382,6 +4433,9 @@ case OP_IdxGE: { /* jump */
r.flags = UNPACKED_IGNORE_ROWID;
}
r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res);
if( pOp->opcode==OP_IdxLT ){
res = -res;
@ -4481,6 +4535,8 @@ case OP_Clear: {
if( pOp->p3 ){
p->nChange += nChange;
if( pOp->p3>0 ){
assert( memIsValid(&aMem[pOp->p3]) );
memAboutToChange(p, &aMem[pOp->p3]);
aMem[pOp->p3].u.i += nChange;
}
}
@ -4522,9 +4578,9 @@ case OP_CreateTable: { /* out2-prerelease */
assert( pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
/* flags = BTREE_INTKEY; */
flags = BTREE_LEAFDATA|BTREE_INTKEY;
flags = BTREE_INTKEY;
}else{
flags = BTREE_ZERODATA;
flags = BTREE_BLOBKEY;
}
rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags);
pOut->u.i = pgno;
@ -4843,6 +4899,7 @@ case OP_Program: { /* jump */
pProgram = pOp->p4.pProgram;
pRt = &aMem[pOp->p3];
assert( memIsValid(pRt) );
assert( pProgram->nOp>0 );
/* If the p5 flag is clear, then recursive invocation of triggers is
@ -5012,6 +5069,7 @@ case OP_MemMax: { /* in2 */
}else{
pIn1 = &aMem[pOp->p1];
}
assert( memIsValid(pIn1) );
sqlite3VdbeMemIntegerify(pIn1);
pIn2 = &aMem[pOp->p2];
sqlite3VdbeMemIntegerify(pIn2);
@ -5096,7 +5154,9 @@ case OP_AggStep: {
apVal = p->apArg;
assert( apVal || n==0 );
for(i=0; i<n; i++, pRec++){
assert( memIsValid(pRec) );
apVal[i] = pRec;
memAboutToChange(p, pRec);
sqlite3VdbeMemStoreType(pRec);
}
ctx.pFunc = pOp->p4.pFunc;
@ -5116,7 +5176,7 @@ case OP_AggStep: {
assert( pOp[-1].opcode==OP_CollSeq );
ctx.pColl = pOp[-1].p4.pColl;
}
(ctx.pFunc->xStep)(&ctx, n, apVal);
(ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */
if( ctx.isError ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
rc = ctx.isError;
@ -5491,6 +5551,7 @@ case OP_VFilter: { /* jump */
pQuery = &aMem[pOp->p3];
pArgc = &pQuery[1];
pCur = p->apCsr[pOp->p1];
assert( memIsValid(pQuery) );
REGISTER_TRACE(pOp->p3, pQuery);
assert( pCur->pVtabCursor );
pVtabCursor = pCur->pVtabCursor;
@ -5546,6 +5607,7 @@ case OP_VColumn: {
assert( pCur->pVtabCursor );
assert( pOp->p3>0 && pOp->p3<=p->nMem );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
if( pCur->nullRow ){
sqlite3VdbeMemSetNull(pDest);
break;
@ -5644,10 +5706,12 @@ case OP_VRename: {
pVtab = pOp->p4.pVtab->pVtab;
pName = &aMem[pOp->p1];
assert( pVtab->pModule->xRename );
assert( memIsValid(pName) );
REGISTER_TRACE(pOp->p1, pName);
assert( pName->flags & MEM_Str );
rc = pVtab->pModule->xRename(pVtab, pName->z);
importVtabErrMsg(p, pVtab);
p->expired = 0;
break;
}
@ -5694,6 +5758,8 @@ case OP_VUpdate: {
apArg = p->apArg;
pX = &aMem[pOp->p3];
for(i=0; i<nArg; i++){
assert( memIsValid(pX) );
memAboutToChange(p, pX);
sqlite3VdbeMemStoreType(pX);
apArg[i] = pX;
pX++;
@ -5721,6 +5787,32 @@ case OP_Pagecount: { /* out2-prerelease */
}
#endif
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
/* Opcode: MaxPgcnt P1 P2 P3 * *
**
** Try to set the maximum page count for database P1 to the value in P3.
** Do not let the maximum page count fall below the current page count and
** do not change the maximum page count value if P3==0.
**
** Store the maximum page count after the change in register P2.
*/
case OP_MaxPgcnt: { /* out2-prerelease */
unsigned int newMax;
Btree *pBt;
pBt = db->aDb[pOp->p1].pBt;
newMax = 0;
if( pOp->p3 ){
newMax = sqlite3BtreeLastPage(pBt);
if( newMax < (unsigned)pOp->p3 ) newMax = (unsigned)pOp->p3;
}
pOut->u.i = sqlite3BtreeMaxPageCount(pBt, newMax);
break;
}
#endif
#ifndef SQLITE_OMIT_TRACE
/* Opcode: Trace * * * P4 *
**

View File

@ -39,16 +39,14 @@ typedef unsigned char Bool;
**
** Every cursor that the virtual machine has open is represented by an
** instance of the following structure.
**
** If the VdbeCursor.isTriggerRow flag is set it means that this cursor is
** really a single row that represents the NEW or OLD pseudo-table of
** a row trigger. The data for the row is stored in VdbeCursor.pData and
** the rowid is in VdbeCursor.iKey.
*/
struct VdbeCursor {
BtCursor *pCursor; /* The cursor structure of the backend */
Btree *pBt; /* Separate file holding temporary table */
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
int iDb; /* Index of cursor database in db->aDb[] (or -1) */
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
int pseudoTableReg; /* Register holding pseudotable content. */
int nField; /* Number of fields in the header */
Bool zeroed; /* True if zeroed out and ready for reuse */
Bool rowidIsValid; /* True if lastRowid is valid */
Bool atFirst; /* True if pointing to first entry */
@ -57,14 +55,12 @@ struct VdbeCursor {
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
Bool isTable; /* True if a table requiring integer keys */
Bool isIndex; /* True if an index containing keys only - no data */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
Btree *pBt; /* Separate file holding temporary table */
int pseudoTableReg; /* Register holding pseudotable content. */
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
int nField; /* Number of fields in the header */
i64 seqCount; /* Sequence counter */
Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
** OP_IsUnique opcode on this cursor. */
@ -96,26 +92,34 @@ typedef struct VdbeCursor VdbeCursor;
** restoring the state of the VM to as it was before the sub-program
** began executing.
**
** Frames are stored in a linked list headed at Vdbe.pParent. Vdbe.pParent
** is the parent of the current frame, or zero if the current frame
** is the main Vdbe program.
** The memory for a VdbeFrame object is allocated and managed by a memory
** cell in the parent (calling) frame. When the memory cell is deleted or
** overwritten, the VdbeFrame object is not freed immediately. Instead, it
** is linked into the Vdbe.pDelFrame list. The contents of the Vdbe.pDelFrame
** list is deleted when the VM is reset in VdbeHalt(). The reason for doing
** this instead of deleting the VdbeFrame immediately is to avoid recursive
** calls to sqlite3VdbeMemRelease() when the memory cells belonging to the
** child frame are released.
**
** The currently executing frame is stored in Vdbe.pFrame. Vdbe.pFrame is
** set to NULL if the currently executing frame is the main program.
*/
typedef struct VdbeFrame VdbeFrame;
struct VdbeFrame {
Vdbe *v; /* VM this frame belongs to */
int pc; /* Program Counter */
Op *aOp; /* Program instructions */
int pc; /* Program Counter in parent (calling) frame */
Op *aOp; /* Program instructions for parent frame */
int nOp; /* Size of aOp array */
Mem *aMem; /* Array of memory cells */
Mem *aMem; /* Array of memory cells for parent frame */
int nMem; /* Number of entries in aMem */
VdbeCursor **apCsr; /* Element of Vdbe cursors */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u16 nCursor; /* Number of entries in apCsr */
void *token; /* Copy of SubProgram.token */
int nChildMem; /* Number of memory cells for child frame */
int nChildCsr; /* Number of cursors for child frame */
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
int nChange; /* Statement changes (Vdbe.nChanges) */
VdbeFrame *pParent; /* Parent of this frame */
VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
};
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
@ -128,29 +132,27 @@ struct VdbeFrame {
/*
** Internally, the vdbe manipulates nearly all SQL values as Mem
** structures. Each Mem struct may cache multiple representations (string,
** integer etc.) of the same value. A value (and therefore Mem structure)
** has the following properties:
**
** Each value has a manifest type. The manifest type of the value stored
** in a Mem struct is returned by the MemType(Mem*) macro. The type is
** one of SQLITE_NULL, SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT or
** SQLITE_BLOB.
** integer etc.) of the same value.
*/
struct Mem {
sqlite3 *db; /* The associated database connection */
char *z; /* String or BLOB value */
double r; /* Real value */
union {
i64 i; /* Integer value. */
i64 i; /* Integer value used when MEM_Int is set in flags */
int nZero; /* Used when bit MEM_Zero is set in flags */
FuncDef *pDef; /* Used only when flags==MEM_Agg */
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
} u;
double r; /* Real value */
sqlite3 *db; /* The associated database connection */
char *z; /* String or BLOB value */
int n; /* Number of characters in string value, excluding '\0' */
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
#ifdef SQLITE_DEBUG
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */
#endif
void (*xDel)(void *); /* If not null, call this function to delete Mem.z */
char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */
};
@ -166,9 +168,6 @@ struct Mem {
** database (see below for exceptions). If the MEM_Term flag is also
** set, then the string is nul terminated. The MEM_Int and MEM_Real
** flags may coexist with the MEM_Str flag.
**
** Multiple of these values can appear in Mem.flags. But only one
** at a time can appear in Mem.type.
*/
#define MEM_Null 0x0001 /* Value is NULL */
#define MEM_Str 0x0002 /* Value is a string */
@ -177,6 +176,7 @@ struct Mem {
#define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
#define MEM_Invalid 0x0080 /* Value is undefined */
#define MEM_TypeMask 0x00ff /* Mask of type bits */
/* Whenever Mem contains a valid string or blob representation, one of
@ -190,19 +190,25 @@ struct Mem {
#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */
#ifdef SQLITE_OMIT_INCRBLOB
#undef MEM_Zero
#define MEM_Zero 0x0000
#endif
/*
** Clear any existing type flags from a Mem and replace them with f
*/
#define MemSetTypeFlag(p, f) \
((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
/*
** Return true if a memory cell is not marked as invalid. This macro
** is for use inside assert() statements only.
*/
#ifdef SQLITE_DEBUG
#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
#endif
/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
** additional information about auxiliary information bound to arguments
@ -244,23 +250,11 @@ struct sqlite3_context {
CollSeq *pColl; /* Collating sequence */
};
/*
** A Set structure is used for quick testing to see if a value
** is part of a small set. Sets are used to implement code like
** this:
** x.y IN ('hi','hoo','hum')
*/
typedef struct Set Set;
struct Set {
Hash hash; /* A set is just a hash table */
HashElem *prev; /* Previously accessed hash elemen */
};
/*
** An instance of the virtual machine. This structure contains the complete
** state of the virtual machine.
**
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_compile()
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
** is really a pointer to an instance of this structure.
**
** The Vdbe.inVtabMethod variable is set to non-zero for the duration of
@ -273,31 +267,31 @@ struct Set {
*/
struct Vdbe {
sqlite3 *db; /* The database connection that owns this statement */
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
int nOp; /* Number of instructions in the program */
int nOpAlloc; /* Number of slots allocated for aOp[] */
Op *aOp; /* Space to hold the virtual machine's program */
int nLabel; /* Number of labels used */
int nLabelAlloc; /* Number of slots allocated in aLabel[] */
int *aLabel; /* Space to hold the labels */
Mem *aMem; /* The memory locations */
Mem **apArg; /* Arguments to currently executing user function */
Mem *aColName; /* Column names to return */
Mem *pResultSet; /* Pointer to an array of results */
int nMem; /* Number of memory locations currently allocated */
int nOp; /* Number of instructions in the program */
int nOpAlloc; /* Number of slots allocated for aOp[] */
int nLabel; /* Number of labels used */
int nLabelAlloc; /* Number of slots allocated in aLabel[] */
int *aLabel; /* Space to hold the labels */
u16 nResColumn; /* Number of columns in one row of the result set */
u16 nCursor; /* Number of slots in apCsr[] */
u32 magic; /* Magic number for sanity checking */
char *zErrMsg; /* Error message written here */
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
VdbeCursor **apCsr; /* One element of this array for each open cursor */
u8 errorAction; /* Recovery action to do in case of an error */
u8 okVar; /* True if azVar[] has been initialized */
ynVar nVar; /* Number of entries in aVar[] */
Mem *aVar; /* Values for the OP_Variable opcode. */
char **azVar; /* Name of variables */
u32 magic; /* Magic number for sanity checking */
int nMem; /* Number of memory locations currently allocated */
Mem *aMem; /* The memory locations */
ynVar nVar; /* Number of entries in aVar[] */
u32 cacheCtr; /* VdbeCursor row cache generation counter */
int pc; /* The program counter */
int rc; /* Value to return */
char *zErrMsg; /* Error message written here */
u8 errorAction; /* Recovery action to do in case of an error */
u8 okVar; /* True if azVar[] has been initialized */
u8 explain; /* True if EXPLAIN present on SQL command */
u8 changeCntOn; /* True to update the change-counter */
u8 expired; /* True if the VM needs to be recompiled */
@ -309,18 +303,21 @@ struct Vdbe {
u8 isPrepareV2; /* True if prepared with prepare_v2() */
int nChange; /* Number of db changes made since last reset */
int btreeMask; /* Bitmask of db->aDb[] entries referenced */
i64 startTime; /* Time when query started - used for profiling */
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
int iStatement; /* Statement number (or 0 if has not opened stmt) */
int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
#ifndef SQLITE_OMIT_TRACE
i64 startTime; /* Time when query started - used for profiling */
#endif
i64 nFkConstraint; /* Number of imm. FK constraints this VM */
i64 nStmtDefCons; /* Number of def. constraints when stmt started */
int iStatement; /* Statement number (or 0 if has not opened stmt) */
char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
#ifdef SQLITE_DEBUG
FILE *trace; /* Write an execution trace here, if not NULL */
#endif
VdbeFrame *pFrame; /* Parent frame */
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
int nFrame; /* Number of frames in pFrame list */
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
@ -391,6 +388,10 @@ void sqlite3VdbeFrameDelete(VdbeFrame*);
int sqlite3VdbeFrameRestore(VdbeFrame *);
void sqlite3VdbeMemStoreType(Mem *pMem);
#ifdef SQLITE_DEBUG
void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*);
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
int sqlite3VdbeCheckFk(Vdbe *, int);
#else

View File

@ -65,6 +65,8 @@ static int vdbeSafetyNotNull(Vdbe *p){
int sqlite3_finalize(sqlite3_stmt *pStmt){
int rc;
if( pStmt==0 ){
/* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL
** pointer is a harmless no-op. */
rc = SQLITE_OK;
}else{
Vdbe *v = (Vdbe*)pStmt;
@ -141,7 +143,7 @@ const void *sqlite3_value_blob(sqlite3_value *pVal){
sqlite3VdbeMemExpandBlob(p);
p->flags &= ~MEM_Str;
p->flags |= MEM_Blob;
return p->z;
return p->n ? p->z : 0;
}else{
return sqlite3_value_text(pVal);
}
@ -343,11 +345,30 @@ static int sqlite3Step(Vdbe *p){
assert(p);
if( p->magic!=VDBE_MAGIC_RUN ){
/* We used to require that sqlite3_reset() be called before retrying
** sqlite3_step() after any error. But after 3.6.23, we changed this
** so that sqlite3_reset() would be called automatically instead of
** throwing the error.
** sqlite3_step() after any error or after SQLITE_DONE. But beginning
** with version 3.7.0, we changed this so that sqlite3_reset() would
** be called automatically instead of throwing the SQLITE_MISUSE error.
** This "automatic-reset" change is not technically an incompatibility,
** since any application that receives an SQLITE_MISUSE is broken by
** definition.
**
** Nevertheless, some published applications that were originally written
** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
** returns, and the so were broken by the automatic-reset change. As a
** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
** legacy behavior of returning SQLITE_MISUSE for cases where the
** previous sqlite3_step() returned something other than a SQLITE_LOCKED
** or SQLITE_BUSY error.
*/
#ifdef SQLITE_OMIT_AUTORESET
if( p->rc==SQLITE_BUSY || p->rc==SQLITE_LOCKED ){
sqlite3_reset((sqlite3_stmt*)p);
}else{
return SQLITE_MISUSE_BKPT;
}
#else
sqlite3_reset((sqlite3_stmt*)p);
#endif
}
/* Check that malloc() has not failed. If it has, return early. */
@ -389,7 +410,9 @@ static int sqlite3Step(Vdbe *p){
}else
#endif /* SQLITE_OMIT_EXPLAIN */
{
db->vdbeExecCnt++;
rc = sqlite3VdbeExec(p);
db->vdbeExecCnt--;
}
#ifndef SQLITE_OMIT_TRACE
@ -495,6 +518,12 @@ void *sqlite3_user_data(sqlite3_context *p){
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
**
** IMPLEMENTATION-OF: R-46798-50301 The sqlite3_context_db_handle() interface
** returns a copy of the pointer to the database connection (the 1st
** parameter) of the sqlite3_create_function() and
** sqlite3_create_function16() routines that originally registered the
** application defined function.
*/
sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
assert( p && p->pFunc );
@ -677,7 +706,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
#if defined(SQLITE_DEBUG) && defined(__GNUC__)
__attribute__((aligned(8)))
#endif
= {{0}, (double)0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
= {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
if( pVm && ALWAYS(pVm->db) ){
sqlite3_mutex_enter(pVm->db->mutex);
@ -704,8 +733,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
** sqlite3_column_real()
** sqlite3_column_bytes()
** sqlite3_column_bytes16()
**
** But not for sqlite3_column_blob(), which never calls malloc().
** sqiite3_column_blob()
*/
static void columnMallocFailure(sqlite3_stmt *pStmt)
{
@ -973,6 +1001,12 @@ static int vdbeUnbind(Vdbe *p, int i){
/* If the bit corresponding to this variable in Vdbe.expmask is set, then
** binding a new value to this variable invalidates the current query plan.
**
** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host
** parameter in the WHERE clause might influence the choice of query plan
** for a statement, then the statement will be automatically recompiled,
** as if there had been a schema change, on the first sqlite3_step() call
** following any change to the bindings of that parameter.
*/
if( p->isPrepareV2 &&
((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff)
@ -1009,6 +1043,8 @@ static int bindText(
rc = sqlite3ApiExit(p->db, rc);
}
sqlite3_mutex_leave(p->db->mutex);
}else if( xDel!=SQLITE_STATIC && xDel!=SQLITE_TRANSIENT ){
xDel((void*)zData);
}
return rc;
}
@ -1251,6 +1287,14 @@ sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){
return pStmt ? ((Vdbe*)pStmt)->db : 0;
}
/*
** Return true if the prepared statement is guaranteed to not modify the
** database.
*/
int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
return pStmt ? ((Vdbe*)pStmt)->readOnly : 1;
}
/*
** Return a pointer to the next prepared statement after pStmt associated
** with database connection pDb. If pStmt is NULL, return the first

View File

@ -408,7 +408,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
pOp->opflags = sqlite3OpcodeProperty[opcode];
if( opcode==OP_Function || opcode==OP_AggStep ){
if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
}else if( opcode==OP_Transaction && pOp->p2!=0 ){
}else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){
p->readOnly = 0;
#ifndef SQLITE_OMIT_VIRTUALTABLE
}else if( opcode==OP_VUpdate ){
@ -1182,12 +1182,10 @@ int sqlite3VdbeList(
pMem->type = SQLITE_INTEGER;
pMem++;
if( p->explain==1 ){
pMem->flags = MEM_Int;
pMem->u.i = pOp->p3; /* P3 */
pMem->type = SQLITE_INTEGER;
pMem++;
}
pMem->flags = MEM_Int;
pMem->u.i = pOp->p3; /* P3 */
pMem->type = SQLITE_INTEGER;
pMem++;
if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
assert( p->db->mallocFailed );
@ -1232,7 +1230,7 @@ int sqlite3VdbeList(
}
}
p->nResColumn = 8 - 5*(p->explain-1);
p->nResColumn = 8 - 4*(p->explain-1);
p->rc = SQLITE_OK;
rc = SQLITE_ROW;
}
@ -1539,6 +1537,11 @@ static void closeAllCursors(Vdbe *p){
if( p->aMem ){
releaseMemArray(&p->aMem[1], p->nMem);
}
while( p->pDelFrame ){
VdbeFrame *pDel = p->pDelFrame;
p->pDelFrame = pDel->pParent;
sqlite3VdbeFrameDelete(pDel);
}
}
/*
@ -1755,9 +1758,10 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
Btree *pBt = db->aDb[i].pBt;
if( sqlite3BtreeIsInTrans(pBt) ){
char const *zFile = sqlite3BtreeGetJournalname(pBt);
if( zFile==0 || zFile[0]==0 ){
if( zFile==0 ){
continue; /* Ignore TEMP and :memory: databases */
}
assert( zFile[0]!=0 );
if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){
needSync = 1;
}

View File

@ -26,11 +26,82 @@ struct Incrblob {
int flags; /* Copy of "flags" passed to sqlite3_blob_open() */
int nByte; /* Size of open blob, in bytes */
int iOffset; /* Byte offset of blob in cursor data */
int iCol; /* Table column this handle is open on */
BtCursor *pCsr; /* Cursor pointing at blob row */
sqlite3_stmt *pStmt; /* Statement holding cursor open */
sqlite3 *db; /* The associated database */
};
/*
** This function is used by both blob_open() and blob_reopen(). It seeks
** the b-tree cursor associated with blob handle p to point to row iRow.
** If successful, SQLITE_OK is returned and subsequent calls to
** sqlite3_blob_read() or sqlite3_blob_write() access the specified row.
**
** If an error occurs, or if the specified row does not exist or does not
** contain a value of type TEXT or BLOB in the column nominated when the
** blob handle was opened, then an error code is returned and *pzErr may
** be set to point to a buffer containing an error message. It is the
** responsibility of the caller to free the error message buffer using
** sqlite3DbFree().
**
** If an error does occur, then the b-tree cursor is closed. All subsequent
** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will
** immediately return SQLITE_ABORT.
*/
static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
int rc; /* Error code */
char *zErr = 0; /* Error message */
Vdbe *v = (Vdbe *)p->pStmt;
/* Set the value of the SQL statements only variable to integer iRow.
** This is done directly instead of using sqlite3_bind_int64() to avoid
** triggering asserts related to mutexes.
*/
assert( v->aVar[0].flags&MEM_Int );
v->aVar[0].u.i = iRow;
rc = sqlite3_step(p->pStmt);
if( rc==SQLITE_ROW ){
u32 type = v->apCsr[0]->aType[p->iCol];
if( type<12 ){
zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
type==0?"null": type==7?"real": "integer"
);
rc = SQLITE_ERROR;
sqlite3_finalize(p->pStmt);
p->pStmt = 0;
}else{
p->iOffset = v->apCsr[0]->aOffset[p->iCol];
p->nByte = sqlite3VdbeSerialTypeLen(type);
p->pCsr = v->apCsr[0]->pCursor;
sqlite3BtreeEnterCursor(p->pCsr);
sqlite3BtreeCacheOverflow(p->pCsr);
sqlite3BtreeLeaveCursor(p->pCsr);
}
}
if( rc==SQLITE_ROW ){
rc = SQLITE_OK;
}else if( p->pStmt ){
rc = sqlite3_finalize(p->pStmt);
p->pStmt = 0;
if( rc==SQLITE_OK ){
zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow);
rc = SQLITE_ERROR;
}else{
zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db));
}
}
assert( rc!=SQLITE_OK || zErr==0 );
assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE );
*pzErr = zErr;
return rc;
}
/*
** Open a blob handle.
*/
@ -71,29 +142,35 @@ int sqlite3_blob_open(
{OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
{OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */
{OP_NotExists, 0, 9, 1}, /* 6: Seek the cursor */
{OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */
{OP_Column, 0, 0, 1}, /* 7 */
{OP_ResultRow, 1, 0, 0}, /* 8 */
{OP_Close, 0, 0, 0}, /* 9 */
{OP_Halt, 0, 0, 0}, /* 10 */
{OP_Goto, 0, 5, 0}, /* 9 */
{OP_Close, 0, 0, 0}, /* 10 */
{OP_Halt, 0, 0, 0}, /* 11 */
};
Vdbe *v = 0;
int rc = SQLITE_OK;
char *zErr = 0;
Table *pTab;
Parse *pParse;
Parse *pParse = 0;
Incrblob *pBlob = 0;
flags = !!flags; /* flags = (flags ? 1 : 0); */
*ppBlob = 0;
sqlite3_mutex_enter(db->mutex);
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
if( !pBlob ) goto blob_open_out;
pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
if( pParse==0 ){
rc = SQLITE_NOMEM;
goto blob_open_out;
}
if( !pParse ) goto blob_open_out;
do {
memset(pParse, 0, sizeof(Parse));
pParse->db = db;
sqlite3DbFree(db, zErr);
zErr = 0;
sqlite3BtreeEnterAll(db);
pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
@ -119,7 +196,7 @@ int sqlite3_blob_open(
}
/* Now search pTab for the exact column. */
for(iCol=0; iCol < pTab->nCol; iCol++) {
for(iCol=0; iCol<pTab->nCol; iCol++) {
if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
break;
}
@ -173,11 +250,14 @@ int sqlite3_blob_open(
}
}
v = sqlite3VdbeCreate(db);
if( v ){
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db);
assert( pBlob->pStmt || db->mallocFailed );
if( pBlob->pStmt ){
Vdbe *v = (Vdbe *)pBlob->pStmt;
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
flags = !!flags; /* flags = (flags ? 1 : 0); */
/* Configure the OP_Transaction */
sqlite3VdbeChangeP1(v, 0, iDb);
@ -220,65 +300,25 @@ int sqlite3_blob_open(
}
}
pBlob->flags = flags;
pBlob->iCol = iCol;
pBlob->db = db;
sqlite3BtreeLeaveAll(db);
if( db->mallocFailed ){
goto blob_open_out;
}
sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow);
rc = sqlite3_step((sqlite3_stmt *)v);
if( rc!=SQLITE_ROW ){
nAttempt++;
rc = sqlite3_finalize((sqlite3_stmt *)v);
sqlite3DbFree(db, zErr);
zErr = sqlite3MPrintf(db, sqlite3_errmsg(db));
v = 0;
}
} while( nAttempt<5 && rc==SQLITE_SCHEMA );
if( rc==SQLITE_ROW ){
/* The row-record has been opened successfully. Check that the
** column in question contains text or a blob. If it contains
** text, it is up to the caller to get the encoding right.
*/
Incrblob *pBlob;
u32 type = v->apCsr[0]->aType[iCol];
if( type<12 ){
sqlite3DbFree(db, zErr);
zErr = sqlite3MPrintf(db, "cannot open value of type %s",
type==0?"null": type==7?"real": "integer"
);
rc = SQLITE_ERROR;
goto blob_open_out;
}
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
if( db->mallocFailed ){
sqlite3DbFree(db, pBlob);
goto blob_open_out;
}
pBlob->flags = flags;
pBlob->pCsr = v->apCsr[0]->pCursor;
sqlite3BtreeEnterCursor(pBlob->pCsr);
sqlite3BtreeCacheOverflow(pBlob->pCsr);
sqlite3BtreeLeaveCursor(pBlob->pCsr);
pBlob->pStmt = (sqlite3_stmt *)v;
pBlob->iOffset = v->apCsr[0]->aOffset[iCol];
pBlob->nByte = sqlite3VdbeSerialTypeLen(type);
pBlob->db = db;
*ppBlob = (sqlite3_blob *)pBlob;
rc = SQLITE_OK;
}else if( rc==SQLITE_OK ){
sqlite3DbFree(db, zErr);
zErr = sqlite3MPrintf(db, "no such rowid: %lld", iRow);
rc = SQLITE_ERROR;
}
sqlite3_bind_int64(pBlob->pStmt, 1, iRow);
rc = blobSeekToRow(pBlob, iRow, &zErr);
} while( (++nAttempt)<5 && rc==SQLITE_SCHEMA );
blob_open_out:
if( v && (rc!=SQLITE_OK || db->mallocFailed) ){
sqlite3VdbeFinalize(v);
if( rc==SQLITE_OK && db->mallocFailed==0 ){
*ppBlob = (sqlite3_blob *)pBlob;
}else{
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
sqlite3DbFree(db, pBlob);
}
sqlite3Error(db, rc, zErr);
sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
sqlite3StackFree(db, pParse);
rc = sqlite3ApiExit(db, rc);
@ -331,7 +371,7 @@ static int blobReadWrite(
/* Request is out of range. Return a transient error. */
rc = SQLITE_ERROR;
sqlite3Error(db, SQLITE_ERROR, 0);
} else if( v==0 ){
}else if( v==0 ){
/* If there is no statement handle, then the blob-handle has
** already been invalidated. Return SQLITE_ABORT in this case.
*/
@ -379,7 +419,47 @@ int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
*/
int sqlite3_blob_bytes(sqlite3_blob *pBlob){
Incrblob *p = (Incrblob *)pBlob;
return p ? p->nByte : 0;
return (p && p->pStmt) ? p->nByte : 0;
}
/*
** Move an existing blob handle to point to a different row of the same
** database table.
**
** If an error occurs, or if the specified row does not exist or does not
** contain a blob or text value, then an error code is returned and the
** database handle error code and message set. If this happens, then all
** subsequent calls to sqlite3_blob_xxx() functions (except blob_close())
** immediately return SQLITE_ABORT.
*/
int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
int rc;
Incrblob *p = (Incrblob *)pBlob;
sqlite3 *db;
if( p==0 ) return SQLITE_MISUSE_BKPT;
db = p->db;
sqlite3_mutex_enter(db->mutex);
if( p->pStmt==0 ){
/* If there is no statement handle, then the blob-handle has
** already been invalidated. Return SQLITE_ABORT in this case.
*/
rc = SQLITE_ABORT;
}else{
char *zErr;
rc = blobSeekToRow(p, iRow, &zErr);
if( rc!=SQLITE_OK ){
sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
}
assert( rc!=SQLITE_SCHEMA );
}
rc = sqlite3ApiExit(db, rc);
assert( rc==SQLITE_OK || p->pStmt==0 );
sqlite3_mutex_leave(db->mutex);
return rc;
}
#endif /* #ifndef SQLITE_OMIT_INCRBLOB */

View File

@ -132,6 +132,9 @@ int sqlite3VdbeMemMakeWriteable(Mem *pMem){
pMem->z[pMem->n] = 0;
pMem->z[pMem->n+1] = 0;
pMem->flags |= MEM_Term;
#ifdef SQLITE_DEBUG
pMem->pScopyFrom = 0;
#endif
}
return SQLITE_OK;
@ -252,7 +255,7 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
ctx.s.db = pMem->db;
ctx.pMem = pMem;
ctx.pFunc = pFunc;
pFunc->xFinalize(&ctx);
pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel );
sqlite3DbFree(pMem->db, pMem->zMalloc);
memcpy(pMem, &ctx.s, sizeof(ctx.s));
@ -365,13 +368,9 @@ i64 sqlite3VdbeIntValue(Mem *pMem){
return doubleToInt64(pMem->r);
}else if( flags & (MEM_Str|MEM_Blob) ){
i64 value;
pMem->flags |= MEM_Str;
if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
|| sqlite3VdbeMemNulTerminate(pMem) ){
return 0;
}
assert( pMem->z );
sqlite3Atoi64(pMem->z, &value);
assert( pMem->z || pMem->n==0 );
testcase( pMem->z==0 );
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
return value;
}else{
return 0;
@ -394,14 +393,7 @@ double sqlite3VdbeRealValue(Mem *pMem){
}else if( pMem->flags & (MEM_Str|MEM_Blob) ){
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
double val = (double)0;
pMem->flags |= MEM_Str;
if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
|| sqlite3VdbeMemNulTerminate(pMem) ){
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
return (double)0;
}
assert( pMem->z );
sqlite3AtoF(pMem->z, &val);
sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc);
return val;
}else{
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
@ -474,21 +466,19 @@ int sqlite3VdbeMemRealify(Mem *pMem){
** as much of the string as we can and ignore the rest.
*/
int sqlite3VdbeMemNumerify(Mem *pMem){
int rc;
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 );
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
rc = sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8);
if( rc ) return rc;
rc = sqlite3VdbeMemNulTerminate(pMem);
if( rc ) return rc;
if( sqlite3Atoi64(pMem->z, &pMem->u.i) ){
MemSetTypeFlag(pMem, MEM_Int);
}else{
pMem->r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
sqlite3VdbeIntegerAffinity(pMem);
if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){
MemSetTypeFlag(pMem, MEM_Int);
}else{
pMem->r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
sqlite3VdbeIntegerAffinity(pMem);
}
}
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
pMem->flags &= ~(MEM_Str|MEM_Blob);
return SQLITE_OK;
}
@ -497,7 +487,9 @@ int sqlite3VdbeMemNumerify(Mem *pMem){
*/
void sqlite3VdbeMemSetNull(Mem *pMem){
if( pMem->flags & MEM_Frame ){
sqlite3VdbeFrameDelete(pMem->u.pFrame);
VdbeFrame *pFrame = pMem->u.pFrame;
pFrame->pParent = pFrame->v->pDelFrame;
pFrame->v->pDelFrame = pFrame;
}
if( pMem->flags & MEM_RowSet ){
sqlite3RowSetClear(pMem->u.pRowSet);
@ -593,6 +585,28 @@ int sqlite3VdbeMemTooBig(Mem *p){
return 0;
}
#ifdef SQLITE_DEBUG
/*
** This routine prepares a memory cell for modication by breaking
** its link to a shallow copy and by marking any current shallow
** copies of this cell as invalid.
**
** This is used for testing and debugging only - to make sure shallow
** copies are not misused.
*/
void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
int i;
Mem *pX;
for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
if( pX->pScopyFrom==pMem ){
pX->flags |= MEM_Invalid;
pX->pScopyFrom = 0;
}
}
pMem->pScopyFrom = 0;
}
#endif /* SQLITE_DEBUG */
/*
** Size of struct Mem not including the Mem.zMalloc member.
*/
@ -961,7 +975,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
return 0;
}
}
sqlite3VdbeMemNulTerminate(pVal);
sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */
}else{
assert( (pVal->flags&MEM_Blob)==0 );
sqlite3VdbeMemStringify(pVal, enc);
@ -1009,6 +1023,8 @@ int sqlite3ValueFromExpr(
int op;
char *zVal = 0;
sqlite3_value *pVal = 0;
int negInt = 1;
const char *zNeg = "";
if( !pExpr ){
*ppVal = 0;
@ -1026,13 +1042,24 @@ int sqlite3ValueFromExpr(
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
#endif
/* Handle negative integers in a single step. This is needed in the
** case when the value is -9223372036854775808.
*/
if( op==TK_UMINUS
&& (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){
pExpr = pExpr->pLeft;
op = pExpr->op;
negInt = -1;
zNeg = "-";
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
pVal = sqlite3ValueNew(db);
if( pVal==0 ) goto no_mem;
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue);
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
}else{
zVal = sqlite3DbStrDup(db, pExpr->u.zToken);
zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
if( zVal==0 ) goto no_mem;
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT;
@ -1042,14 +1069,18 @@ int sqlite3ValueFromExpr(
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
}
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
if( enc!=SQLITE_UTF8 ){
sqlite3VdbeChangeEncoding(pVal, enc);
}
}else if( op==TK_UMINUS ) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
sqlite3VdbeMemNumerify(pVal);
pVal->u.i = -1 * pVal->u.i;
/* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */
pVal->r = (double)-1 * pVal->r;
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}
#ifndef SQLITE_OMIT_BLOB_LITERAL

View File

@ -44,9 +44,12 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
}
/*
** Return a pointer to a string in memory obtained form sqlite3DbMalloc() which
** holds a copy of zRawSql but with host parameters expanded to their
** current bindings.
** This function returns a pointer to a nul-terminated string in memory
** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the
** string contains a copy of zRawSql but with host parameters expanded to
** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1,
** then the returned string holds a copy of zRawSql with "-- " prepended
** to each line of text.
**
** The calling function is responsible for making sure the memory returned
** is eventually freed.
@ -77,63 +80,72 @@ char *sqlite3VdbeExpandSql(
sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
out.db = db;
while( zRawSql[0] ){
n = findNextHostParameter(zRawSql, &nToken);
assert( n>0 );
sqlite3StrAccumAppend(&out, zRawSql, n);
zRawSql += n;
assert( zRawSql[0] || nToken==0 );
if( nToken==0 ) break;
if( zRawSql[0]=='?' ){
if( nToken>1 ){
assert( sqlite3Isdigit(zRawSql[1]) );
sqlite3GetInt32(&zRawSql[1], &idx);
}else{
idx = nextIndex;
}
}else{
assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' );
testcase( zRawSql[0]==':' );
testcase( zRawSql[0]=='$' );
testcase( zRawSql[0]=='@' );
idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
assert( idx>0 );
if( db->vdbeExecCnt>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
sqlite3StrAccumAppend(&out, "-- ", 3);
sqlite3StrAccumAppend(&out, zStart, zRawSql-zStart);
}
zRawSql += nToken;
nextIndex = idx + 1;
assert( idx>0 && idx<=p->nVar );
pVar = &p->aVar[idx-1];
if( pVar->flags & MEM_Null ){
sqlite3StrAccumAppend(&out, "NULL", 4);
}else if( pVar->flags & MEM_Int ){
sqlite3XPrintf(&out, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
sqlite3XPrintf(&out, "%!.15g", pVar->r);
}else if( pVar->flags & MEM_Str ){
}else{
while( zRawSql[0] ){
n = findNextHostParameter(zRawSql, &nToken);
assert( n>0 );
sqlite3StrAccumAppend(&out, zRawSql, n);
zRawSql += n;
assert( zRawSql[0] || nToken==0 );
if( nToken==0 ) break;
if( zRawSql[0]=='?' ){
if( nToken>1 ){
assert( sqlite3Isdigit(zRawSql[1]) );
sqlite3GetInt32(&zRawSql[1], &idx);
}else{
idx = nextIndex;
}
}else{
assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' );
testcase( zRawSql[0]==':' );
testcase( zRawSql[0]=='$' );
testcase( zRawSql[0]=='@' );
idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
assert( idx>0 );
}
zRawSql += nToken;
nextIndex = idx + 1;
assert( idx>0 && idx<=p->nVar );
pVar = &p->aVar[idx-1];
if( pVar->flags & MEM_Null ){
sqlite3StrAccumAppend(&out, "NULL", 4);
}else if( pVar->flags & MEM_Int ){
sqlite3XPrintf(&out, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
sqlite3XPrintf(&out, "%!.15g", pVar->r);
}else if( pVar->flags & MEM_Str ){
#ifndef SQLITE_OMIT_UTF16
u8 enc = ENC(db);
if( enc!=SQLITE_UTF8 ){
Mem utf8;
memset(&utf8, 0, sizeof(utf8));
utf8.db = db;
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z);
sqlite3VdbeMemRelease(&utf8);
}else
u8 enc = ENC(db);
if( enc!=SQLITE_UTF8 ){
Mem utf8;
memset(&utf8, 0, sizeof(utf8));
utf8.db = db;
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z);
sqlite3VdbeMemRelease(&utf8);
}else
#endif
{
sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z);
{
sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z);
}
}else if( pVar->flags & MEM_Zero ){
sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
}else{
assert( pVar->flags & MEM_Blob );
sqlite3StrAccumAppend(&out, "x'", 2);
for(i=0; i<pVar->n; i++){
sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
}
}else if( pVar->flags & MEM_Zero ){
sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
}else{
assert( pVar->flags & MEM_Blob );
sqlite3StrAccumAppend(&out, "x'", 2);
for(i=0; i<pVar->n; i++){
sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
}
}
return sqlite3StrAccumFinish(&out);

View File

@ -371,7 +371,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
zWhere = sqlite3MPrintf(db, "name='%q'", pTab->zName);
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC);
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
@ -672,7 +672,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
}
db->pVTab = 0;
}else{
sqlite3Error(db, SQLITE_ERROR, zErr);
sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
rc = SQLITE_ERROR;
}

117
src/wal.c
View File

@ -428,6 +428,13 @@ struct Wal {
#endif
};
/*
** Candidate values for Wal.exclusiveMode.
*/
#define WAL_NORMAL_MODE 0
#define WAL_EXCLUSIVE_MODE 1
#define WAL_HEAPMEMORY_MODE 2
/*
** Each page of the wal-index mapping contains a hash-table made up of
** an array of HASHTABLE_NSLOT elements of the following type.
@ -451,14 +458,14 @@ typedef u16 ht_slot;
*/
struct WalIterator {
int iPrior; /* Last result returned from the iterator */
int nSegment; /* Size of the aSegment[] array */
int nSegment; /* Number of entries in aSegment[] */
struct WalSegment {
int iNext; /* Next slot in aIndex[] not yet returned */
ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
u32 *aPgno; /* Array of page numbers. */
int nEntry; /* Max size of aPgno[] and aIndex[] arrays */
int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
int iZero; /* Frame number associated with aPgno[0] */
} aSegment[1]; /* One for every 32KB page in the WAL */
} aSegment[1]; /* One for every 32KB page in the wal-index */
};
/*
@ -514,9 +521,14 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
/* Request a pointer to the required page from the VFS */
if( pWal->apWiData[iPage]==0 ){
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
);
if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
}else{
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
);
}
}
*ppPage = pWal->apWiData[iPage];
@ -599,6 +611,12 @@ static void walChecksumBytes(
aOut[1] = s2;
}
static void walShmBarrier(Wal *pWal){
if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
sqlite3OsShmBarrier(pWal->pDbFd);
}
}
/*
** Write the header information in pWal->hdr into the wal-index.
**
@ -613,7 +631,7 @@ static void walIndexWriteHdr(Wal *pWal){
pWal->hdr.iVersion = WALINDEX_MAX_VERSION;
walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr));
sqlite3OsShmBarrier(pWal->pDbFd);
walShmBarrier(pWal);
memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr));
}
@ -1185,7 +1203,15 @@ recovery_error:
** Close an open wal-index.
*/
static void walIndexClose(Wal *pWal, int isDelete){
sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
int i;
for(i=0; i<pWal->nWiData; i++){
sqlite3_free((void *)pWal->apWiData[i]);
pWal->apWiData[i] = 0;
}
}else{
sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
}
}
/*
@ -1207,6 +1233,7 @@ int sqlite3WalOpen(
sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */
sqlite3_file *pDbFd, /* The open database file */
const char *zWalName, /* Name of the WAL file */
int bNoShm, /* True to run in heap-memory mode */
Wal **ppWal /* OUT: Allocated Wal handle */
){
int rc; /* Return Code */
@ -1240,6 +1267,7 @@ int sqlite3WalOpen(
pRet->pDbFd = pDbFd;
pRet->readLock = -1;
pRet->zWalName = zWalName;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
/* Open file handle on the write-ahead log file. */
flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
@ -1301,9 +1329,29 @@ static int walIteratorNext(
/*
** This function merges two sorted lists into a single sorted list.
**
** aLeft[] and aRight[] are arrays of indices. The sort key is
** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following
** is guaranteed for all J<K:
**
** aContent[aLeft[J]] < aContent[aLeft[K]]
** aContent[aRight[J]] < aContent[aRight[K]]
**
** This routine overwrites aRight[] with a new (probably longer) sequence
** of indices such that the aRight[] contains every index that appears in
** either aLeft[] or the old aRight[] and such that the second condition
** above is still met.
**
** The aContent[aLeft[X]] values will be unique for all X. And the
** aContent[aRight[X]] values will be unique too. But there might be
** one or more combinations of X and Y such that
**
** aLeft[X]!=aRight[Y] && aContent[aLeft[X]] == aContent[aRight[Y]]
**
** When that happens, omit the aLeft[X] and use the aRight[Y] index.
*/
static void walMerge(
u32 *aContent, /* Pages in wal */
const u32 *aContent, /* Pages in wal - keys for the sort */
ht_slot *aLeft, /* IN: Left hand input list */
int nLeft, /* IN: Elements in array *paLeft */
ht_slot **paRight, /* IN/OUT: Right hand input list */
@ -1343,10 +1391,24 @@ static void walMerge(
}
/*
** Sort the elements in list aList, removing any duplicates.
** Sort the elements in list aList using aContent[] as the sort key.
** Remove elements with duplicate keys, preferring to keep the
** larger aList[] values.
**
** The aList[] entries are indices into aContent[]. The values in
** aList[] are to be sorted so that for all J<K:
**
** aContent[aList[J]] < aContent[aList[K]]
**
** For any X and Y such that
**
** aContent[aList[X]] == aContent[aList[Y]]
**
** Keep the larger of the two values aList[X] and aList[Y] and discard
** the smaller.
*/
static void walMergesort(
u32 *aContent, /* Pages in wal */
const u32 *aContent, /* Pages in wal */
ht_slot *aBuffer, /* Buffer of at least *pnList items to use */
ht_slot *aList, /* IN/OUT: List to sort */
int *pnList /* IN/OUT: Number of elements in aList[] */
@ -1411,6 +1473,7 @@ static void walIteratorFree(WalIterator *p){
/*
** Construct a WalInterator object that can be used to loop over all
** pages in the WAL in ascending order. The caller must hold the checkpoint
** lock.
**
** On success, make *pp point to the newly allocated WalInterator object
** return SQLITE_OK. Otherwise, return an error code. If this routine
@ -1545,7 +1608,8 @@ static int walCheckpoint(
szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
testcase( szPage<=32768 );
testcase( szPage>=65536 );
if( pWal->hdr.mxFrame==0 ) return SQLITE_OK;
pInfo = walCkptInfo(pWal);
if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
/* Allocate the iterator */
rc = walIteratorInit(pWal, &pIter);
@ -1567,7 +1631,6 @@ static int walCheckpoint(
*/
mxSafeFrame = pWal->hdr.mxFrame;
mxPage = pWal->hdr.nPage;
pInfo = walCkptInfo(pWal);
for(i=1; i<WAL_NREADER; i++){
u32 y = pInfo->aReadMark[i];
if( mxSafeFrame>=y ){
@ -1673,7 +1736,9 @@ int sqlite3WalClose(
*/
rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
if( rc==SQLITE_OK ){
pWal->exclusiveMode = 1;
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
}
rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf);
if( rc==SQLITE_OK ){
isDelete = 1;
@ -1729,7 +1794,7 @@ static int walIndexTryHdr(Wal *pWal, int *pChanged){
*/
aHdr = walIndexHdr(pWal);
memcpy(&h1, (void *)&aHdr[0], sizeof(h1));
sqlite3OsShmBarrier(pWal->pDbFd);
walShmBarrier(pWal);
memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
@ -1930,7 +1995,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** and can be safely ignored.
*/
rc = walLockShared(pWal, WAL_READ_LOCK(0));
sqlite3OsShmBarrier(pWal->pDbFd);
walShmBarrier(pWal);
if( rc==SQLITE_OK ){
if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
/* It is not safe to allow the reader to continue here if frames
@ -2024,7 +2089,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** log-wrap (either of which would require an exclusive lock on
** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
*/
sqlite3OsShmBarrier(pWal->pDbFd);
walShmBarrier(pWal);
if( pInfo->aReadMark[mxI]!=mxReadMark
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
){
@ -2366,7 +2431,7 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
**
** SQLITE_OK is returned if no error is encountered (regardless of whether
** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned
** if some error
** if an error occurs.
*/
static int walRestartLog(Wal *pWal){
int rc = SQLITE_OK;
@ -2399,6 +2464,8 @@ static int walRestartLog(Wal *pWal){
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
assert( pInfo->aReadMark[0]==0 );
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
}else if( rc!=SQLITE_BUSY ){
return rc;
}
}
walUnlockShared(pWal, WAL_READ_LOCK(0));
@ -2478,7 +2545,7 @@ int sqlite3WalFrames(
return rc;
}
}
assert( pWal->szPage==szPage );
assert( (int)pWal->szPage==szPage );
/* Write the log file. */
for(p=pList; p; p=p->pDirty){
@ -2665,13 +2732,14 @@ int sqlite3WalCallback(Wal *pWal){
** on the main database file before invoking this operation.
**
** If op is negative, then do a dry-run of the op==1 case but do
** not actually change anything. The pager uses this to see if it
** not actually change anything. The pager uses this to see if it
** should acquire the database exclusive lock prior to invoking
** the op==1 case.
*/
int sqlite3WalExclusiveMode(Wal *pWal, int op){
int rc;
assert( pWal->writeLock==0 );
assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 );
/* pWal->readLock is usually set, but might be -1 if there was a
** prior error while attempting to acquire are read-lock. This cannot
@ -2705,4 +2773,13 @@ int sqlite3WalExclusiveMode(Wal *pWal, int op){
return rc;
}
/*
** Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false.
*/
int sqlite3WalHeapMemory(Wal *pWal){
return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
}
#endif /* #ifndef SQLITE_OMIT_WAL */

View File

@ -35,6 +35,7 @@
# define sqlite3WalCheckpoint(u,v,w,x) 0
# define sqlite3WalCallback(z) 0
# define sqlite3WalExclusiveMode(y,z) 0
# define sqlite3WalHeapMemory(z) 0
#else
#define WAL_SAVEPOINT_NDATA 4
@ -45,7 +46,7 @@
typedef struct Wal Wal;
/* Open and close a connection to a write-ahead log. */
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, Wal**);
int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**);
int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *);
/* Used by readers to open (lock) and close (unlock) a snapshot. A
@ -102,5 +103,11 @@ int sqlite3WalCallback(Wal *pWal);
*/
int sqlite3WalExclusiveMode(Wal *pWal, int op);
/* Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false.
*/
int sqlite3WalHeapMemory(Wal *pWal);
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */

View File

@ -192,7 +192,6 @@ struct WhereMaskSet {
struct WhereCost {
WherePlan plan; /* The lookup strategy */
double rCost; /* Overall cost of pursuing this search strategy */
double nRow; /* Estimated number of output rows */
Bitmask used; /* Bitmask of cursors used by this plan */
};
@ -235,10 +234,11 @@ struct WhereCost {
#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
#define WHERE_NOT_FULLSCAN 0x000f3000 /* Does not do a full table scan */
#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
#define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */
#define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */
#define WHERE_REVERSE 0x02000000 /* Scan in reverse order */
@ -669,11 +669,12 @@ static int isLikeOrGlob(
}
if( op==TK_VARIABLE ){
Vdbe *pReprepare = pParse->pReprepare;
pVal = sqlite3VdbeGetValue(pReprepare, pRight->iColumn, SQLITE_AFF_NONE);
int iCol = pRight->iColumn;
pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
z = (char *)sqlite3_value_text(pVal);
}
sqlite3VdbeSetVarmask(pParse->pVdbe, pRight->iColumn);
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
}else if( op==TK_STRING ){
z = pRight->u.zToken;
@ -691,7 +692,7 @@ static int isLikeOrGlob(
*ppPrefix = pPrefix;
if( op==TK_VARIABLE ){
Vdbe *v = pParse->pVdbe;
sqlite3VdbeSetVarmask(v, pRight->iColumn);
sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */
if( *pisComplete && pRight->u.zToken[1] ){
/* If the rhs of the LIKE expression is a variable, and the current
** value of the variable means there is no need to invoke the LIKE
@ -1555,7 +1556,8 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
** Required because bestIndex() is called by bestOrClauseIndex()
*/
static void bestIndex(
Parse*, WhereClause*, struct SrcList_item*, Bitmask, ExprList*, WhereCost*);
Parse*, WhereClause*, struct SrcList_item*,
Bitmask, Bitmask, ExprList*, WhereCost*);
/*
** This routine attempts to find an scanning strategy that can be used
@ -1568,7 +1570,8 @@ static void bestOrClauseIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors that are not available */
Bitmask notReady, /* Mask of cursors not available for indexing */
Bitmask notValid, /* Cursors not available for any purpose */
ExprList *pOrderBy, /* The ORDER BY clause */
WhereCost *pCost /* Lowest cost query plan */
){
@ -1578,8 +1581,9 @@ static void bestOrClauseIndex(
WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
WhereTerm *pTerm; /* A single term of the WHERE clause */
/* No OR-clause optimization allowed if the NOT INDEXED clause is used */
if( pSrc->notIndexed ){
/* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses
** are used */
if( pSrc->notIndexed || pSrc->pIndex!=0 ){
return;
}
@ -1604,7 +1608,7 @@ static void bestOrClauseIndex(
));
if( pOrTerm->eOperator==WO_AND ){
WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc;
bestIndex(pParse, pAndWC, pSrc, notReady, 0, &sTermCost);
bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost);
}else if( pOrTerm->leftCursor==iCur ){
WhereClause tempWC;
tempWC.pParse = pWC->pParse;
@ -1612,12 +1616,12 @@ static void bestOrClauseIndex(
tempWC.op = TK_AND;
tempWC.a = pOrTerm;
tempWC.nTerm = 1;
bestIndex(pParse, &tempWC, pSrc, notReady, 0, &sTermCost);
bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost);
}else{
continue;
}
rTotal += sTermCost.rCost;
nRow += sTermCost.nRow;
nRow += sTermCost.plan.nRow;
used |= sTermCost.used;
if( rTotal>=pCost->rCost ) break;
}
@ -1636,8 +1640,8 @@ static void bestOrClauseIndex(
WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
if( rTotal<pCost->rCost ){
pCost->rCost = rTotal;
pCost->nRow = nRow;
pCost->used = used;
pCost->plan.nRow = nRow;
pCost->plan.wsFlags = flags;
pCost->plan.u.pTerm = pTerm;
}
@ -1705,7 +1709,7 @@ static void bestAutomaticIndex(
assert( pParse->nQueryLoop >= (double)1 );
pTable = pSrc->pTab;
nTableRow = pTable->pIndex ? pTable->pIndex->aiRowEst[0] : 1000000;
nTableRow = pTable->nRowEst;
logN = estLog(nTableRow);
costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1);
if( costTempIdx>=pCost->rCost ){
@ -1721,7 +1725,7 @@ static void bestAutomaticIndex(
WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n",
pCost->rCost, costTempIdx));
pCost->rCost = costTempIdx;
pCost->nRow = logN + 1;
pCost->plan.nRow = logN + 1;
pCost->plan.wsFlags = WHERE_TEMP_INDEX;
pCost->used = pTerm->prereqRight;
break;
@ -2059,7 +2063,8 @@ static void bestVirtualIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors that are not available */
Bitmask notReady, /* Mask of cursors not available for index */
Bitmask notValid, /* Cursors not valid for any purpose */
ExprList *pOrderBy, /* The order by clause */
WhereCost *pCost, /* Lowest cost query plan */
sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */
@ -2189,7 +2194,7 @@ static void bestVirtualIndex(
/* Try to find a more efficient access pattern by using multiple indexes
** to optimize an OR expression within the WHERE clause.
*/
bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost);
bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@ -2310,12 +2315,11 @@ static int valueFromExpr(
u8 aff,
sqlite3_value **pp
){
/* The evalConstExpr() function will have already converted any TK_VARIABLE
** expression involved in an comparison into a TK_REGISTER. */
assert( pExpr->op!=TK_VARIABLE );
if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){
if( pExpr->op==TK_VARIABLE
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
){
int iVar = pExpr->iColumn;
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */
*pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
return SQLITE_OK;
}
@ -2470,7 +2474,8 @@ static void bestBtreeIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors that are not available */
Bitmask notReady, /* Mask of cursors not available for indexing */
Bitmask notValid, /* Cursors not available for any purpose */
ExprList *pOrderBy, /* The ORDER BY clause */
WhereCost *pCost /* Lowest cost query plan */
){
@ -2512,23 +2517,14 @@ static void bestBtreeIndex(
sPk.nColumn = 1;
sPk.aiColumn = &aiColumnPk;
sPk.aiRowEst = aiRowEstPk;
aiRowEstPk[1] = 1;
sPk.onError = OE_Replace;
sPk.pTable = pSrc->pTab;
aiRowEstPk[0] = pSrc->pTab->nRowEst;
aiRowEstPk[1] = 1;
pFirst = pSrc->pTab->pIndex;
if( pSrc->notIndexed==0 ){
sPk.pNext = pFirst;
}
/* The aiRowEstPk[0] is an estimate of the total number of rows in the
** table. Get this information from the ANALYZE information if it is
** available. If not available, assume the table 1 million rows in size.
*/
if( pFirst ){
assert( pFirst->aiRowEst!=0 ); /* Allocated together with pFirst */
aiRowEstPk[0] = pFirst->aiRowEst[0];
}else{
aiRowEstPk[0] = 1000000;
}
pProbe = &sPk;
wsFlagMask = ~(
WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE
@ -2741,16 +2737,16 @@ static void bestBtreeIndex(
** with this step if we already know this index will not be chosen.
** Also, never reduce the output row count below 2 using this step.
**
** Do not reduce the output row count if pSrc is the only table that
** is notReady; if notReady is a power of two. This will be the case
** when the main sqlite3WhereBegin() loop is scanning for a table with
** and "optimal" index, and on such a scan the output row count
** reduction is not valid because it does not update the "pCost->used"
** bitmap. The notReady bitmap will also be a power of two when we
** are scanning for the last table in a 64-way join. We are willing
** to bypass this optimization in that corner case.
** It is critical that the notValid mask be used here instead of
** the notReady mask. When computing an "optimal" index, the notReady
** mask will only have one bit set - the bit for the current table.
** The notValid mask, on the other hand, always has all bits set for
** tables that are not in outer loops. If notReady is used here instead
** of notValid, then a optimal index that depends on inner joins loops
** might be selected even when there exists an optimal index that has
** no such dependency.
*/
if( nRow>2 && cost<=pCost->rCost && (notReady & (notReady-1))!=0 ){
if( nRow>2 && cost<=pCost->rCost ){
int k; /* Loop counter */
int nSkipEq = nEq; /* Number of == constraints to skip */
int nSkipRange = nBound; /* Number of < constraints to skip */
@ -2759,7 +2755,7 @@ static void bestBtreeIndex(
thisTab = getMask(pWC->pMaskSet, iCur);
for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){
if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
if( (pTerm->prereqAll & notReady)!=thisTab ) continue;
if( (pTerm->prereqAll & notValid)!=thisTab ) continue;
if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
if( nSkipEq ){
/* Ignore the first nEq equality matches since the index
@ -2801,11 +2797,11 @@ static void bestBtreeIndex(
** index and its cost in the pCost structure.
*/
if( (!pIdx || wsFlags)
&& (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->nRow))
&& (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->plan.nRow))
){
pCost->rCost = cost;
pCost->nRow = nRow;
pCost->used = used;
pCost->plan.nRow = nRow;
pCost->plan.wsFlags = (wsFlags&wsFlagMask);
pCost->plan.nEq = nEq;
pCost->plan.u.pIdx = pIdx;
@ -2841,7 +2837,7 @@ static void bestBtreeIndex(
pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk")
));
bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost);
bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost);
pCost->plan.wsFlags |= eqTermMask;
}
@ -2856,14 +2852,15 @@ static void bestIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
struct SrcList_item *pSrc, /* The FROM clause term to search */
Bitmask notReady, /* Mask of cursors that are not available */
Bitmask notReady, /* Mask of cursors not available for indexing */
Bitmask notValid, /* Cursors not available for any purpose */
ExprList *pOrderBy, /* The ORDER BY clause */
WhereCost *pCost /* Lowest cost query plan */
){
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pSrc->pTab) ){
sqlite3_index_info *p = 0;
bestVirtualIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost, &p);
bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p);
if( p->needToFreeIdxStr ){
sqlite3_free(p->idxStr);
}
@ -2871,7 +2868,7 @@ static void bestIndex(
}else
#endif
{
bestBtreeIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost);
bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
}
}
@ -3133,6 +3130,161 @@ static int codeAllEqualityTerms(
return regBase;
}
#ifndef SQLITE_OMIT_EXPLAIN
/*
** This routine is a helper for explainIndexRange() below
**
** pStr holds the text of an expression that we are building up one term
** at a time. This routine adds a new term to the end of the expression.
** Terms are separated by AND so add the "AND" text for second and subsequent
** terms only.
*/
static void explainAppendTerm(
StrAccum *pStr, /* The text expression being built */
int iTerm, /* Index of this term. First is zero */
const char *zColumn, /* Name of the column */
const char *zOp /* Name of the operator */
){
if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
sqlite3StrAccumAppend(pStr, zColumn, -1);
sqlite3StrAccumAppend(pStr, zOp, 1);
sqlite3StrAccumAppend(pStr, "?", 1);
}
/*
** Argument pLevel describes a strategy for scanning table pTab. This
** function returns a pointer to a string buffer containing a description
** of the subset of table rows scanned by the strategy in the form of an
** SQL expression. Or, if all rows are scanned, NULL is returned.
**
** For example, if the query:
**
** SELECT * FROM t1 WHERE a=1 AND b>2;
**
** is run and there is an index on (a, b), then this function returns a
** string similar to:
**
** "a=? AND b>?"
**
** The returned pointer points to memory obtained from sqlite3DbMalloc().
** It is the responsibility of the caller to free the buffer when it is
** no longer required.
*/
static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){
WherePlan *pPlan = &pLevel->plan;
Index *pIndex = pPlan->u.pIdx;
int nEq = pPlan->nEq;
int i, j;
Column *aCol = pTab->aCol;
int *aiColumn = pIndex->aiColumn;
StrAccum txt;
if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
return 0;
}
sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH);
txt.db = db;
sqlite3StrAccumAppend(&txt, " (", 2);
for(i=0; i<nEq; i++){
explainAppendTerm(&txt, i, aCol[aiColumn[i]].zName, "=");
}
j = i;
if( pPlan->wsFlags&WHERE_BTM_LIMIT ){
explainAppendTerm(&txt, i++, aCol[aiColumn[j]].zName, ">");
}
if( pPlan->wsFlags&WHERE_TOP_LIMIT ){
explainAppendTerm(&txt, i, aCol[aiColumn[j]].zName, "<");
}
sqlite3StrAccumAppend(&txt, ")", 1);
return sqlite3StrAccumFinish(&txt);
}
/*
** 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.
*/
static void explainOneScan(
Parse *pParse, /* Parse context */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
int iLevel, /* Value for "level" column of output */
int iFrom, /* Value for "from" column of output */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
if( pParse->explain==2 ){
u32 flags = pLevel->plan.wsFlags;
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
char *zMsg; /* Text to add to EQP output */
sqlite3_int64 nRow; /* Expected number of rows visited by scan */
int iId = pParse->iSelectId; /* Select id (left-most output column) */
int isSearch; /* True for a SEARCH. False for SCAN. */
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return;
isSearch = (pLevel->plan.nEq>0)
|| (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN");
if( pItem->pSelect ){
zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId);
}else{
zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName);
}
if( pItem->zAlias ){
zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
}
if( (flags & WHERE_INDEXED)!=0 ){
char *zWhere = explainIndexRange(db, pLevel, pItem->pTab);
zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg,
((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""),
((flags & WHERE_IDX_ONLY)?"COVERING ":""),
((flags & WHERE_TEMP_INDEX)?"":" "),
((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName),
zWhere
);
sqlite3DbFree(db, zWhere);
}else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
if( flags&WHERE_ROWID_EQ ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg);
}else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg);
}else if( flags&WHERE_BTM_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg);
}else if( flags&WHERE_TOP_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg);
}
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
pVtabIdx->idxNum, pVtabIdx->idxStr);
}
#endif
if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){
testcase( wctrlFlags & WHERE_ORDERBY_MIN );
nRow = 1;
}else{
nRow = (sqlite3_int64)pLevel->plan.nRow;
}
zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow);
sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
}
}
#else
# define explainOneScan(u,v,w,x,y,z)
#endif /* SQLITE_OMIT_EXPLAIN */
/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
@ -3540,7 +3692,7 @@ static Bitmask codeOneLoopStart(
r1 = sqlite3GetTempReg(pParse);
testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT );
testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT );
if( pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT) ){
if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1);
sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont);
}
@ -3674,6 +3826,9 @@ static Bitmask codeOneLoopStart(
WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
if( pSubWInfo ){
explainOneScan(
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
);
if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
int r;
@ -4069,6 +4224,7 @@ WhereInfo *sqlite3WhereBegin(
memset(&bestPlan, 0, sizeof(bestPlan));
bestPlan.rCost = SQLITE_BIG_DBL;
WHERETRACE(("*** Begin search for loop %d ***\n", i));
/* Loop through the remaining entries in the FROM clause to find the
** next nested loop. The loop tests all FROM clause entries
@ -4087,9 +4243,15 @@ WhereInfo *sqlite3WhereBegin(
** other FROM clause terms that are notReady. If no notReady terms are
** used then the "optimal" query plan works.
**
** Note that the WhereCost.nRow parameter for an optimal scan might
** not be as small as it would be if the table really were the innermost
** join. The nRow value can be reduced by WHERE clause constraints
** that do not use indices. But this nRow reduction only happens if the
** table really is the innermost join.
**
** The second loop iteration is only performed if no optimal scan
** strategies were found by the first loop. This 2nd iteration is used to
** search for the lowest cost scan overall.
** strategies were found by the first iteration. This second iteration
** is used to search for the lowest cost scan overall.
**
** Previous versions of SQLite performed only the second iteration -
** the next outermost loop was always that with the lowest overall
@ -4102,14 +4264,14 @@ WhereInfo *sqlite3WhereBegin(
**
** The best strategy is to iterate through table t1 first. However it
** is not possible to determine this with a simple greedy algorithm.
** However, since the cost of a linear scan through table t2 is the same
** Since the cost of a linear scan through table t2 is the same
** as the cost of a linear scan through table t1, a simple greedy
** algorithm may choose to use t2 for the outer loop, which is a much
** costlier approach.
*/
nUnconstrained = 0;
notIndexed = 0;
for(isOptimal=(iFrom<nTabList-1); isOptimal>=0; isOptimal--){
for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){
Bitmask mask; /* Mask of tables not yet ready */
for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
int doNotReorder; /* True if this table should not be reordered */
@ -4127,15 +4289,19 @@ WhereInfo *sqlite3WhereBegin(
pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0);
if( pTabItem->pIndex==0 ) nUnconstrained++;
WHERETRACE(("=== trying table %d with isOptimal=%d ===\n",
j, isOptimal));
assert( pTabItem->pTab );
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTabItem->pTab) ){
sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo;
bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp);
bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
&sCost, pp);
}else
#endif
{
bestBtreeIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost);
bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
&sCost);
}
assert( isOptimal || (sCost.used&notReady)==0 );
@ -4175,10 +4341,12 @@ WhereInfo *sqlite3WhereBegin(
&& (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */
|| NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
&& (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */
|| (sCost.rCost<=bestPlan.rCost && sCost.nRow<bestPlan.nRow))
|| (sCost.rCost<=bestPlan.rCost
&& sCost.plan.nRow<bestPlan.plan.nRow))
){
WHERETRACE(("... best so far with cost=%g and nRow=%g\n",
sCost.rCost, sCost.nRow));
WHERETRACE(("=== table %d is best so far"
" with cost=%g and nRow=%g\n",
j, sCost.rCost, sCost.plan.nRow));
bestPlan = sCost;
bestJ = j;
}
@ -4187,8 +4355,9 @@ WhereInfo *sqlite3WhereBegin(
}
assert( bestJ>=0 );
assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ,
pLevel-pWInfo->a));
WHERETRACE(("*** Optimizer selects table %d for loop %d"
" with cost=%g and nRow=%g\n",
bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow));
if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){
*ppOrderBy = 0;
}
@ -4203,7 +4372,9 @@ WhereInfo *sqlite3WhereBegin(
}
notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor);
pLevel->iFrom = (u8)bestJ;
if( bestPlan.nRow>=(double)1 ) pParse->nQueryLoop *= bestPlan.nRow;
if( bestPlan.plan.nRow>=(double)1 ){
pParse->nQueryLoop *= bestPlan.plan.nRow;
}
/* Check that if the table scanned by this loop iteration had an
** INDEXED BY clause attached to it, that the named index is being
@ -4251,44 +4422,15 @@ WhereInfo *sqlite3WhereBegin(
*/
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
notReady = ~(Bitmask)0;
pWInfo->nRowOut = (double)1;
for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
#ifndef SQLITE_OMIT_EXPLAIN
if( pParse->explain==2 ){
char *zMsg;
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
zMsg = sqlite3MPrintf(db, "TABLE %s", pItem->zName);
if( pItem->zAlias ){
zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
}
if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){
zMsg = sqlite3MAppendf(db, zMsg, "%s WITH AUTOMATIC INDEX", zMsg);
}else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
zMsg = sqlite3MAppendf(db, zMsg, "%s WITH INDEX %s",
zMsg, pLevel->plan.u.pIdx->zName);
}else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
zMsg = sqlite3MAppendf(db, zMsg, "%s VIA MULTI-INDEX UNION", zMsg);
}else if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s USING PRIMARY KEY", zMsg);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
pVtabIdx->idxNum, pVtabIdx->idxStr);
}
#endif
if( pLevel->plan.wsFlags & WHERE_ORDERBY ){
zMsg = sqlite3MAppendf(db, zMsg, "%s ORDER BY", zMsg);
}
sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC);
}
#endif /* SQLITE_OMIT_EXPLAIN */
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
pLevel->iTabCur = pTabItem->iCursor;
pWInfo->nRowOut *= pLevel->plan.nRow;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
/* Do nothing */
@ -4344,8 +4486,10 @@ WhereInfo *sqlite3WhereBegin(
*/
notReady = ~(Bitmask)0;
for(i=0; i<nTabList; i++){
pLevel = &pWInfo->a[i];
explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags);
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
pWInfo->iContinue = pWInfo->a[i].addrCont;
pWInfo->iContinue = pLevel->addrCont;
}
#ifdef SQLITE_TEST /* For testing and debugging use only */

View File

@ -16,6 +16,7 @@ source $testdir/permutations.test
run_test_suite full
run_test_suite no_optimization
run_test_suite memsubsys1
run_test_suite memsubsys2
run_test_suite singlethread

View File

@ -27,11 +27,6 @@ ifcapable {!pragma} return
#
do_not_use_codec
# These tests do not work if there is a codec.
#
#if {[catch {sqlite3 -has_codec} r] || $r} return
#
# The file format change affects the way row-records stored in tables (but
# not indices) are interpreted. Before version 3.1.3, a row-record for a
# table with N columns was guaranteed to contain exactly N fields. As

View File

@ -28,7 +28,7 @@ ifcapable !altertable {
# Determine if there is a codec available on this test.
#
if {[catch {sqlite3 -has_codec} r] || $r} {
if {[catch {sqlite3 -has-codec} r] || $r} {
set has_codec 1
} else {
set has_codec 0
@ -55,6 +55,7 @@ proc get_file_format {{fname test.db}} {
do_test alter3-1.1 {
execsql {
PRAGMA legacy_file_format=ON;
CREATE TABLE abc(a, b, c);
SELECT sql FROM sqlite_master;
}
@ -198,6 +199,7 @@ do_test alter3-4.1 {
file delete -force test.db
set ::DB [sqlite3 db test.db]
execsql {
PRAGMA legacy_file_format=ON;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 100);
INSERT INTO t1 VALUES(2, 300);

Some files were not shown because too many files have changed in this diff Show More