Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b361ff381 |
@@ -589,10 +589,6 @@ sqlite3$(TEXE): $(TOP)/src/shell.c sqlite3.c
|
||||
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
|
||||
|
||||
scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.o
|
||||
$(LTLINK) -o $@ -I. -DSCRUB_STANDALONE \
|
||||
$(TOP)/ext/misc/scrub.c sqlite3.o $(TLIBS)
|
||||
|
||||
srcck1$(BEXE): $(TOP)/tool/srcck1.c
|
||||
$(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c
|
||||
|
||||
|
||||
+5
-11
@@ -198,12 +198,6 @@ DEBUG = 0
|
||||
OPTIMIZATIONS = 2
|
||||
!ENDIF
|
||||
|
||||
# Set this to non-0 to enable support for the session extension.
|
||||
#
|
||||
!IFNDEF SESSION
|
||||
SESSION = 0
|
||||
!ENDIF
|
||||
|
||||
# Set the source code file to be used by executables and libraries when
|
||||
# they need the amalgamation.
|
||||
#
|
||||
@@ -272,9 +266,12 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
!ENDIF
|
||||
|
||||
# Should the session extension be enabled? If so, add compilation options
|
||||
# to enable it.
|
||||
# Enable support for the session extension if the SESSION=1 argument is
|
||||
# present on the nmake command-line
|
||||
#
|
||||
!IFNDEF SESSION
|
||||
SESSION = 0
|
||||
!ENDIF
|
||||
!IF $(SESSION)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
|
||||
@@ -1455,9 +1452,6 @@ $(SQLITE3EXE): $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_S
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
srcck1.exe: $(TOP)\tool\srcck1.c
|
||||
$(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c
|
||||
|
||||
|
||||
@@ -183,12 +183,6 @@ DEBUG = 0
|
||||
OPTIMIZATIONS = 2
|
||||
!ENDIF
|
||||
|
||||
# Set this to non-0 to enable support for the session extension.
|
||||
#
|
||||
!IFNDEF SESSION
|
||||
SESSION = 0
|
||||
!ENDIF
|
||||
|
||||
# Set the source code file to be used by executables and libraries when
|
||||
# they need the amalgamation.
|
||||
#
|
||||
@@ -257,9 +251,12 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
!ENDIF
|
||||
|
||||
# Should the session extension be enabled? If so, add compilation options
|
||||
# to enable it.
|
||||
# Enable support for the session extension if the SESSION=1 argument is
|
||||
# present on the nmake command-line
|
||||
#
|
||||
!IFNDEF SESSION
|
||||
SESSION = 0
|
||||
!ENDIF
|
||||
!IF $(SESSION)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
|
||||
|
||||
@@ -41,15 +41,14 @@ AC_ARG_ENABLE(editline, [AS_HELP_STRING(
|
||||
AC_ARG_ENABLE(readline, [AS_HELP_STRING(
|
||||
[--enable-readline],
|
||||
[use readline])],
|
||||
[], [enable_readline=yes])
|
||||
[], [enable_readline=no])
|
||||
if test x"$enable_editline" != xno ; then
|
||||
sLIBS=$LIBS
|
||||
LIBS=""
|
||||
AC_SEARCH_LIBS([readline],[edit],[enable_readline=no],[enable_editline=no])
|
||||
READLINE_LIBS=$LIBS
|
||||
if test x"$LIBS" != "x"; then
|
||||
AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline)
|
||||
enable_readline=no
|
||||
AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline)
|
||||
else
|
||||
unset ac_cv_search_readline
|
||||
fi
|
||||
@@ -104,7 +103,7 @@ AC_SUBST(DYNAMIC_EXTENSION_FLAGS)
|
||||
AC_ARG_ENABLE(fts5, [AS_HELP_STRING(
|
||||
[--enable-fts5], [include fts5 support [default=no]])],
|
||||
[], [enable_fts5=no])
|
||||
if test x"$enable_fts5" = "xyes"; then
|
||||
if test x"$enable_fts5" == "xyes"; then
|
||||
AC_SEARCH_LIBS(log, m)
|
||||
FTS5_FLAGS=-DSQLITE_ENABLE_FTS5
|
||||
fi
|
||||
@@ -117,7 +116,7 @@ AC_SUBST(FTS5_FLAGS)
|
||||
AC_ARG_ENABLE(json1, [AS_HELP_STRING(
|
||||
[--enable-json1], [include json1 support [default=no]])],
|
||||
[], [enable_json1=no])
|
||||
if test x"$enable_json1" = "xyes"; then
|
||||
if test x"$enable_json1" == "xyes"; then
|
||||
JSON1_FLAGS=-DSQLITE_ENABLE_JSON1
|
||||
fi
|
||||
AC_SUBST(JSON1_FLAGS)
|
||||
@@ -129,7 +128,7 @@ AC_SUBST(JSON1_FLAGS)
|
||||
AC_ARG_ENABLE(session, [AS_HELP_STRING(
|
||||
[--enable-session], [enable the session extension [default=no]])],
|
||||
[], [enable_session=no])
|
||||
if test x"$enable_session" = "xyes"; then
|
||||
if test x"$enable_session" == "xyes"; then
|
||||
SESSION_FLAGS="-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
||||
fi
|
||||
AC_SUBST(SESSION_FLAGS)
|
||||
@@ -142,7 +141,7 @@ AC_ARG_ENABLE(static-shell, [AS_HELP_STRING(
|
||||
[--enable-static-shell],
|
||||
[statically link libsqlite3 into shell tool [default=yes]])],
|
||||
[], [enable_static_shell=yes])
|
||||
if test x"$enable_static_shell" = "xyes"; then
|
||||
if test x"$enable_static_shell" == "xyes"; then
|
||||
EXTRA_SHELL_OBJ=sqlite3-sqlite3.$OBJEXT
|
||||
else
|
||||
EXTRA_SHELL_OBJ=libsqlite3.la
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.14.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.13.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.14.0'
|
||||
PACKAGE_STRING='sqlite 3.14.0'
|
||||
PACKAGE_VERSION='3.13.0'
|
||||
PACKAGE_STRING='sqlite 3.13.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1461,7 +1461,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.14.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.13.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1526,7 +1526,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.14.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.13.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1648,7 +1648,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.14.0
|
||||
sqlite configure 3.13.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2067,7 +2067,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.14.0, which was
|
||||
It was created by sqlite $as_me 3.13.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -10757,20 +10757,6 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# Recent versions of Xcode on Macs hid the tclConfig.sh file
|
||||
# in a strange place.
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# then check for a private Tcl installation
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
@@ -12109,7 +12095,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.14.0, which was
|
||||
This file was extended by sqlite $as_me 3.13.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12175,7 +12161,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.14.0
|
||||
sqlite config.status 3.13.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -334,20 +334,6 @@ if test "${use_tcl}" = "yes" ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Recent versions of Xcode on Macs hid the tclConfig.sh file
|
||||
# in a strange place.
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# then check for a private Tcl installation
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
|
||||
+5
-7
@@ -143,13 +143,11 @@ struct Fts5PhraseIter {
|
||||
** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
|
||||
**
|
||||
** with $p set to a phrase equivalent to the phrase iPhrase of the
|
||||
** current query is executed. Any column filter that applies to
|
||||
** phrase iPhrase of the current query is included in $p. For each
|
||||
** row visited, the callback function passed as the fourth argument
|
||||
** is invoked. The context and API objects passed to the callback
|
||||
** function may be used to access the properties of each matched row.
|
||||
** Invoking Api.xUserData() returns a copy of the pointer passed as
|
||||
** the third argument to pUserData.
|
||||
** current query is executed. For each row visited, the callback function
|
||||
** passed as the fourth argument is invoked. The context and API objects
|
||||
** passed to the callback function may be used to access the properties of
|
||||
** each matched row. Invoking Api.xUserData() returns a copy of the pointer
|
||||
** passed as the third argument to pUserData.
|
||||
**
|
||||
** If the callback function returns any value other than SQLITE_OK, the
|
||||
** query is abandoned and the xQueryPhrase function returns immediately.
|
||||
|
||||
@@ -1655,17 +1655,6 @@ int sqlite3Fts5ExprClonePhrase(
|
||||
pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
|
||||
sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
|
||||
if( pColsetOrig ){
|
||||
int nByte = sizeof(Fts5Colset) + pColsetOrig->nCol * sizeof(int);
|
||||
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
|
||||
if( pColset ){
|
||||
memcpy(pColset, pColsetOrig, nByte);
|
||||
}
|
||||
pNew->pRoot->pNear->pColset = pColset;
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
|
||||
int tflags = 0;
|
||||
|
||||
@@ -246,37 +246,5 @@ foreach {tn lRow res} {
|
||||
} $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the built-in bm25() demo.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 1
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 2
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 3
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 4
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 5
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 6
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 7
|
||||
INSERT INTO t1 VALUES('a', NULL); -- 8
|
||||
INSERT INTO t1 VALUES(NULL, 'a a b'); -- 9
|
||||
INSERT INTO t1 VALUES(NULL, 'b b a'); -- 10
|
||||
}
|
||||
|
||||
do_execsql_test 9.2 {
|
||||
SELECT rowid FROM t1('a AND b') ORDER BY rank;
|
||||
} {
|
||||
10 9
|
||||
}
|
||||
|
||||
do_execsql_test 9.3 {
|
||||
SELECT rowid FROM t1('b:a AND b:b') ORDER BY rank;
|
||||
} {
|
||||
9 10
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -1,594 +0,0 @@
|
||||
/*
|
||||
** 2016-05-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file implements a utility function (and a utility program) that
|
||||
** makes a copy of an SQLite database while simultaneously zeroing out all
|
||||
** deleted content.
|
||||
**
|
||||
** Normally (when PRAGMA secure_delete=OFF, which is the default) when SQLite
|
||||
** deletes content, it does not overwrite the deleted content but rather marks
|
||||
** the region of the file that held that content as being reusable. This can
|
||||
** cause deleted content to recoverable from the database file. This stale
|
||||
** content is removed by the VACUUM command, but VACUUM can be expensive for
|
||||
** large databases. When in PRAGMA secure_delete=ON mode, the deleted content
|
||||
** is zeroed, but secure_delete=ON has overhead as well.
|
||||
**
|
||||
** This utility attempts to make a copy of a complete SQLite database where
|
||||
** all of the deleted content is zeroed out in the copy, and it attempts to
|
||||
** do so while being faster than running VACUUM.
|
||||
**
|
||||
** Usage:
|
||||
**
|
||||
** int sqlite3_scrub_backup(
|
||||
** const char *zSourceFile, // Source database filename
|
||||
** const char *zDestFile, // Destination database filename
|
||||
** char **pzErrMsg // Write error message here
|
||||
** );
|
||||
**
|
||||
** Simply call the API above specifying the filename of the source database
|
||||
** and the name of the backup copy. The source database must already exist
|
||||
** and can be in active use. (A read lock is held during the backup.) The
|
||||
** destination file should not previously exist. If the pzErrMsg parameter
|
||||
** is non-NULL and if an error occurs, then an error message might be written
|
||||
** into memory obtained from sqlite3_malloc() and *pzErrMsg made to point to
|
||||
** that error message. But if the error is an OOM, the error might not be
|
||||
** reported. The routine always returns non-zero if there is an error.
|
||||
**
|
||||
** If compiled with -DSCRUB_STANDALONE then a main() procedure is added and
|
||||
** this file becomes a standalone program that can be run as follows:
|
||||
**
|
||||
** ./sqlite3scrub SOURCE DEST
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct ScrubState ScrubState;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
|
||||
|
||||
/* State information for a scrub-and-backup operation */
|
||||
struct ScrubState {
|
||||
const char *zSrcFile; /* Name of the source file */
|
||||
const char *zDestFile; /* Name of the destination file */
|
||||
int rcErr; /* Error code */
|
||||
char *zErr; /* Error message text */
|
||||
sqlite3 *dbSrc; /* Source database connection */
|
||||
sqlite3_file *pSrc; /* Source file handle */
|
||||
sqlite3 *dbDest; /* Destination database connection */
|
||||
sqlite3_file *pDest; /* Destination file handle */
|
||||
u32 szPage; /* Page size */
|
||||
u32 szUsable; /* Usable bytes on each page */
|
||||
u32 nPage; /* Number of pages */
|
||||
u8 *page1; /* Content of page 1 */
|
||||
};
|
||||
|
||||
/* Store an error message */
|
||||
static void scrubBackupErr(ScrubState *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
sqlite3_free(p->zErr);
|
||||
va_start(ap, zFormat);
|
||||
p->zErr = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
if( p->rcErr==0 ) p->rcErr = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Allocate memory to hold a single page of content */
|
||||
static u8 *scrubBackupAllocPage(ScrubState *p){
|
||||
u8 *pPage;
|
||||
if( p->rcErr ) return 0;
|
||||
pPage = sqlite3_malloc( p->szPage );
|
||||
if( pPage==0 ) p->rcErr = SQLITE_NOMEM;
|
||||
return pPage;
|
||||
}
|
||||
|
||||
/* Read a page from the source database into memory. Use the memory
|
||||
** provided by pBuf if not NULL or allocate a new page if pBuf==NULL.
|
||||
*/
|
||||
static u8 *scrubBackupRead(ScrubState *p, int pgno, u8 *pBuf){
|
||||
int rc;
|
||||
sqlite3_int64 iOff;
|
||||
u8 *pOut = pBuf;
|
||||
if( p->rcErr ) return 0;
|
||||
if( pOut==0 ){
|
||||
pOut = scrubBackupAllocPage(p);
|
||||
if( pOut==0 ) return 0;
|
||||
}
|
||||
iOff = (pgno-1)*(sqlite3_int64)p->szPage;
|
||||
rc = p->pSrc->pMethods->xRead(p->pSrc, pOut, p->szPage, iOff);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( pBuf==0 ) sqlite3_free(pOut);
|
||||
pOut = 0;
|
||||
scrubBackupErr(p, "read failed for page %d", pgno);
|
||||
p->rcErr = SQLITE_IOERR;
|
||||
}
|
||||
return pOut;
|
||||
}
|
||||
|
||||
/* Write a page to the destination database */
|
||||
static void scrubBackupWrite(ScrubState *p, int pgno, const u8 *pData){
|
||||
int rc;
|
||||
sqlite3_int64 iOff;
|
||||
if( p->rcErr ) return;
|
||||
iOff = (pgno-1)*(sqlite3_int64)p->szPage;
|
||||
rc = p->pDest->pMethods->xWrite(p->pDest, pData, p->szPage, iOff);
|
||||
if( rc!=SQLITE_OK ){
|
||||
scrubBackupErr(p, "write failed for page %d", pgno);
|
||||
p->rcErr = SQLITE_IOERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare a statement against the "db" database. */
|
||||
static sqlite3_stmt *scrubBackupPrepare(
|
||||
ScrubState *p, /* Backup context */
|
||||
sqlite3 *db, /* Database to prepare against */
|
||||
const char *zSql /* SQL statement */
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
if( p->rcErr ) return 0;
|
||||
p->rcErr = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p, "SQL error \"%s\" on \"%s\"",
|
||||
sqlite3_errmsg(db), zSql);
|
||||
sqlite3_finalize(pStmt);
|
||||
return 0;
|
||||
}
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
|
||||
/* Open the source database file */
|
||||
static void scrubBackupOpenSrc(ScrubState *p){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
/* Open the source database file */
|
||||
p->rcErr = sqlite3_open_v2(p->zSrcFile, &p->dbSrc,
|
||||
SQLITE_OPEN_READWRITE |
|
||||
SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p, "cannot open source database: %s",
|
||||
sqlite3_errmsg(p->dbSrc));
|
||||
return;
|
||||
}
|
||||
p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_master; BEGIN;",
|
||||
0, 0, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p,
|
||||
"cannot start a read transaction on the source database: %s",
|
||||
sqlite3_errmsg(p->dbSrc));
|
||||
return;
|
||||
}
|
||||
rc = sqlite3_wal_checkpoint_v2(p->dbSrc, "main", SQLITE_CHECKPOINT_FULL,
|
||||
0, 0);
|
||||
if( rc ){
|
||||
scrubBackupErr(p, "cannot checkpoint the source database");
|
||||
return;
|
||||
}
|
||||
pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_size");
|
||||
if( pStmt==0 ) return;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
p->szPage = sqlite3_column_int(pStmt, 0);
|
||||
}else{
|
||||
scrubBackupErr(p, "unable to determine the page size");
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( p->rcErr ) return;
|
||||
pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_count");
|
||||
if( pStmt==0 ) return;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
p->nPage = sqlite3_column_int(pStmt, 0);
|
||||
}else{
|
||||
scrubBackupErr(p, "unable to determine the size of the source database");
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_file_control(p->dbSrc, "main", SQLITE_FCNTL_FILE_POINTER, &p->pSrc);
|
||||
if( p->pSrc==0 || p->pSrc->pMethods==0 ){
|
||||
scrubBackupErr(p, "cannot get the source file handle");
|
||||
p->rcErr = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and open the destination file */
|
||||
static void scrubBackupOpenDest(ScrubState *p){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
char *zSql;
|
||||
if( p->rcErr ) return;
|
||||
p->rcErr = sqlite3_open_v2(p->zDestFile, &p->dbDest,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
|
||||
SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p, "cannot open destination database: %s",
|
||||
sqlite3_errmsg(p->dbDest));
|
||||
return;
|
||||
}
|
||||
zSql = sqlite3_mprintf("PRAGMA page_size(%u);", p->szPage);
|
||||
if( zSql==0 ){
|
||||
p->rcErr = SQLITE_NOMEM;
|
||||
return;
|
||||
}
|
||||
p->rcErr = sqlite3_exec(p->dbDest, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p,
|
||||
"cannot set the page size on the destination database: %s",
|
||||
sqlite3_errmsg(p->dbDest));
|
||||
return;
|
||||
}
|
||||
sqlite3_exec(p->dbDest, "PRAGMA journal_mode=OFF;", 0, 0, 0);
|
||||
p->rcErr = sqlite3_exec(p->dbDest, "BEGIN EXCLUSIVE;", 0, 0, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p,
|
||||
"cannot start a write transaction on the destination database: %s",
|
||||
sqlite3_errmsg(p->dbDest));
|
||||
return;
|
||||
}
|
||||
pStmt = scrubBackupPrepare(p, p->dbDest, "PRAGMA page_count;");
|
||||
if( pStmt==0 ) return;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
scrubBackupErr(p, "cannot measure the size of the destination");
|
||||
}else if( sqlite3_column_int(pStmt, 0)>1 ){
|
||||
scrubBackupErr(p, "destination database is not empty - holds %d pages",
|
||||
sqlite3_column_int(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_file_control(p->dbDest, "main", SQLITE_FCNTL_FILE_POINTER, &p->pDest);
|
||||
if( p->pDest==0 || p->pDest->pMethods==0 ){
|
||||
scrubBackupErr(p, "cannot get the destination file handle");
|
||||
p->rcErr = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a 32-bit big-endian integer */
|
||||
static u32 scrubBackupInt32(const u8 *a){
|
||||
u32 v = a[3];
|
||||
v += ((u32)a[2])<<8;
|
||||
v += ((u32)a[1])<<16;
|
||||
v += ((u32)a[0])<<24;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read a 16-bit big-endian integer */
|
||||
static u32 scrubBackupInt16(const u8 *a){
|
||||
return (a[0]<<8) + a[1];
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a varint. Put the value in *pVal and return the number of bytes.
|
||||
*/
|
||||
static int scrubBackupVarint(const u8 *z, sqlite3_int64 *pVal){
|
||||
sqlite3_int64 v = 0;
|
||||
int i;
|
||||
for(i=0; i<8; i++){
|
||||
v = (v<<7) + (z[i]&0x7f);
|
||||
if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
|
||||
}
|
||||
v = (v<<8) + (z[i]&0xff);
|
||||
*pVal = v;
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes in a varint.
|
||||
*/
|
||||
static int scrubBackupVarintSize(const u8 *z){
|
||||
int i;
|
||||
for(i=0; i<8; i++){
|
||||
if( (z[i]&0x80)==0 ){ return i+1; }
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy the freelist trunk page given, and all its descendents,
|
||||
** zeroing out as much as possible in the process.
|
||||
*/
|
||||
static void scrubBackupFreelist(ScrubState *p, int pgno, u32 nFree){
|
||||
u8 *a, *aBuf;
|
||||
u32 n, mx;
|
||||
|
||||
if( p->rcErr ) return;
|
||||
aBuf = scrubBackupAllocPage(p);
|
||||
if( aBuf==0 ) return;
|
||||
|
||||
while( pgno && nFree){
|
||||
a = scrubBackupRead(p, pgno, aBuf);
|
||||
if( a==0 ) break;
|
||||
n = scrubBackupInt32(&a[4]);
|
||||
mx = p->szUsable/4 - 2;
|
||||
if( n<mx ){
|
||||
memset(&a[n*4+8], 0, 4*(mx-n));
|
||||
}
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
pgno = scrubBackupInt32(a);
|
||||
#if 0
|
||||
/* There is really no point in copying the freelist leaf pages.
|
||||
** Simply leave them uninitialized in the destination database. The
|
||||
** OS filesystem should zero those pages for us automatically.
|
||||
*/
|
||||
for(i=0; i<n && nFree; i++){
|
||||
u32 iLeaf = scrubBackupInt32(&a[i*4+8]);
|
||||
if( aZero==0 ){
|
||||
aZero = scrubBackupAllocPage(p);
|
||||
if( aZero==0 ){ pgno = 0; break; }
|
||||
memset(aZero, 0, p->szPage);
|
||||
}
|
||||
scrubBackupWrite(p, iLeaf, aZero);
|
||||
nFree--;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
sqlite3_free(aBuf);
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy an overflow chain from source to destination. Zero out any
|
||||
** unused tail at the end of the overflow chain.
|
||||
*/
|
||||
static void scrubBackupOverflow(ScrubState *p, int pgno, u32 nByte){
|
||||
u8 *a, *aBuf;
|
||||
|
||||
aBuf = scrubBackupAllocPage(p);
|
||||
if( aBuf==0 ) return;
|
||||
while( nByte>0 && pgno!=0 ){
|
||||
a = scrubBackupRead(p, pgno, aBuf);
|
||||
if( a==0 ) break;
|
||||
if( nByte >= (p->szUsable)-4 ){
|
||||
nByte -= (p->szUsable) - 4;
|
||||
}else{
|
||||
u32 x = (p->szUsable - 4) - nByte;
|
||||
u32 i = p->szUsable - x;
|
||||
memset(&a[i], 0, x);
|
||||
nByte = 0;
|
||||
}
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
pgno = scrubBackupInt32(a);
|
||||
}
|
||||
sqlite3_free(aBuf);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Copy B-Tree page pgno, and all of its children, from source to destination.
|
||||
** Zero out deleted content during the copy.
|
||||
*/
|
||||
static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){
|
||||
u8 *a;
|
||||
u32 i, n, pc;
|
||||
u32 nCell;
|
||||
u32 nPrefix;
|
||||
u32 szHdr;
|
||||
u32 iChild;
|
||||
u8 *aTop;
|
||||
u8 *aCell;
|
||||
u32 x, y;
|
||||
int ln = 0;
|
||||
|
||||
|
||||
if( p->rcErr ) return;
|
||||
if( iDepth>50 ){
|
||||
scrubBackupErr(p, "corrupt: b-tree too deep at page %d", pgno);
|
||||
return;
|
||||
}
|
||||
if( pgno==1 ){
|
||||
a = p->page1;
|
||||
}else{
|
||||
a = scrubBackupRead(p, pgno, 0);
|
||||
if( a==0 ) return;
|
||||
}
|
||||
nPrefix = pgno==1 ? 100 : 0;
|
||||
aTop = &a[nPrefix];
|
||||
szHdr = 8 + 4*(aTop[0]==0x02 || aTop[0]==0x05);
|
||||
aCell = aTop + szHdr;
|
||||
nCell = scrubBackupInt16(&aTop[3]);
|
||||
|
||||
/* Zero out the gap between the cell index and the start of the
|
||||
** cell content area */
|
||||
x = scrubBackupInt16(&aTop[5]); /* First byte of cell content area */
|
||||
if( x>p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
||||
y = szHdr + nPrefix + nCell*2;
|
||||
if( y>x ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( y<x ) memset(a+y, 0, x-y); /* Zero the gap */
|
||||
|
||||
/* Zero out all the free blocks */
|
||||
pc = scrubBackupInt16(&aTop[1]);
|
||||
if( pc>0 && pc<x ){ ln=__LINE__; goto btree_corrupt; }
|
||||
while( pc ){
|
||||
if( pc>(p->szUsable)-4 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
n = scrubBackupInt16(&a[pc+2]);
|
||||
if( pc+n>(p->szUsable) ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( n>4 ) memset(&a[pc+4], 0, n-4);
|
||||
x = scrubBackupInt16(&a[pc]);
|
||||
if( x<pc+4 && x>0 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
pc = x;
|
||||
}
|
||||
|
||||
/* Write this one page */
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
|
||||
/* Walk the tree and process child pages */
|
||||
for(i=0; i<nCell; i++){
|
||||
u32 X, M, K, nLocal;
|
||||
sqlite3_int64 P;
|
||||
pc = scrubBackupInt16(&aCell[i*2]);
|
||||
if( pc <= szHdr ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( pc > p->szUsable-3 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( aTop[0]==0x05 || aTop[0]==0x02 ){
|
||||
if( pc+4 > p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
||||
iChild = scrubBackupInt32(&a[pc]);
|
||||
pc += 4;
|
||||
scrubBackupBtree(p, iChild, iDepth+1);
|
||||
if( aTop[0]==0x05 ) continue;
|
||||
}
|
||||
pc += scrubBackupVarint(&a[pc], &P);
|
||||
if( pc >= p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( aTop[0]==0x0d ){
|
||||
X = p->szUsable - 35;
|
||||
}else{
|
||||
X = ((p->szUsable - 12)*64/255) - 23;
|
||||
}
|
||||
if( P<=X ){
|
||||
/* All content is local. No overflow */
|
||||
continue;
|
||||
}
|
||||
M = ((p->szUsable - 12)*32/255)-23;
|
||||
K = M + ((P-M)%(p->szUsable-4));
|
||||
if( aTop[0]==0x0d ){
|
||||
pc += scrubBackupVarintSize(&a[pc]);
|
||||
if( pc > (p->szUsable-4) ){ ln=__LINE__; goto btree_corrupt; }
|
||||
}
|
||||
nLocal = K<=X ? K : M;
|
||||
if( pc+nLocal > p->szUsable-4 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
iChild = scrubBackupInt32(&a[pc+nLocal]);
|
||||
scrubBackupOverflow(p, iChild, P-nLocal);
|
||||
}
|
||||
|
||||
/* Walk the right-most tree */
|
||||
if( aTop[0]==0x05 || aTop[0]==0x02 ){
|
||||
iChild = scrubBackupInt32(&aTop[8]);
|
||||
scrubBackupBtree(p, iChild, iDepth+1);
|
||||
}
|
||||
|
||||
/* All done */
|
||||
if( pgno>1 ) sqlite3_free(a);
|
||||
return;
|
||||
|
||||
btree_corrupt:
|
||||
scrubBackupErr(p, "corruption on page %d of source database (errid=%d)",
|
||||
pgno, ln);
|
||||
if( pgno>1 ) sqlite3_free(a);
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy all ptrmap pages from source to destination.
|
||||
** This routine is only called if the source database is in autovacuum
|
||||
** or incremental vacuum mode.
|
||||
*/
|
||||
static void scrubBackupPtrmap(ScrubState *p){
|
||||
u32 pgno = 2;
|
||||
u32 J = p->szUsable/5;
|
||||
u32 iLock = (1073742335/p->szPage)+1;
|
||||
u8 *a, *pBuf;
|
||||
if( p->rcErr ) return;
|
||||
pBuf = scrubBackupAllocPage(p);
|
||||
if( pBuf==0 ) return;
|
||||
while( pgno<=p->nPage ){
|
||||
a = scrubBackupRead(p, pgno, pBuf);
|
||||
if( a==0 ) break;
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
pgno += J+1;
|
||||
if( pgno==iLock ) pgno++;
|
||||
}
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
|
||||
int sqlite3_scrub_backup(
|
||||
const char *zSrcFile, /* Source file */
|
||||
const char *zDestFile, /* Destination file */
|
||||
char **pzErr /* Write error here if non-NULL */
|
||||
){
|
||||
ScrubState s;
|
||||
u32 n, i;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.zSrcFile = zSrcFile;
|
||||
s.zDestFile = zDestFile;
|
||||
|
||||
/* Open both source and destination databases */
|
||||
scrubBackupOpenSrc(&s);
|
||||
scrubBackupOpenDest(&s);
|
||||
|
||||
/* Read in page 1 */
|
||||
s.page1 = scrubBackupRead(&s, 1, 0);
|
||||
if( s.page1==0 ) goto scrub_abort;
|
||||
s.szUsable = s.szPage - s.page1[20];
|
||||
|
||||
/* Copy the freelist */
|
||||
n = scrubBackupInt32(&s.page1[36]);
|
||||
i = scrubBackupInt32(&s.page1[32]);
|
||||
if( n ) scrubBackupFreelist(&s, i, n);
|
||||
|
||||
/* Copy ptrmap pages */
|
||||
n = scrubBackupInt32(&s.page1[52]);
|
||||
if( n ) scrubBackupPtrmap(&s);
|
||||
|
||||
/* Copy all of the btrees */
|
||||
scrubBackupBtree(&s, 1, 0);
|
||||
pStmt = scrubBackupPrepare(&s, s.dbSrc,
|
||||
"SELECT rootpage FROM sqlite_master WHERE coalesce(rootpage,0)>0");
|
||||
if( pStmt==0 ) goto scrub_abort;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
i = (u32)sqlite3_column_int(pStmt, 0);
|
||||
scrubBackupBtree(&s, i, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
scrub_abort:
|
||||
/* Close the destination database without closing the transaction. If we
|
||||
** commit, page zero will be overwritten. */
|
||||
sqlite3_close(s.dbDest);
|
||||
|
||||
/* But do close out the read-transaction on the source database */
|
||||
sqlite3_exec(s.dbSrc, "COMMIT;", 0, 0, 0);
|
||||
sqlite3_close(s.dbSrc);
|
||||
sqlite3_free(s.page1);
|
||||
if( pzErr ){
|
||||
*pzErr = s.zErr;
|
||||
}else{
|
||||
sqlite3_free(s.zErr);
|
||||
}
|
||||
return s.rcErr;
|
||||
}
|
||||
|
||||
#ifdef SCRUB_STANDALONE
|
||||
/* Error and warning log */
|
||||
static void errorLogCallback(void *pNotUsed, int iErr, const char *zMsg){
|
||||
const char *zType;
|
||||
switch( iErr&0xff ){
|
||||
case SQLITE_WARNING: zType = "WARNING"; break;
|
||||
case SQLITE_NOTICE: zType = "NOTICE"; break;
|
||||
default: zType = "ERROR"; break;
|
||||
}
|
||||
fprintf(stderr, "%s: %s\n", zType, zMsg);
|
||||
}
|
||||
|
||||
/* The main() routine when this utility is run as a stand-alone program */
|
||||
int main(int argc, char **argv){
|
||||
char *zErr = 0;
|
||||
int rc;
|
||||
if( argc!=3 ){
|
||||
fprintf(stderr,"Usage: %s SOURCE DESTINATION\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, 0);
|
||||
rc = sqlite3_scrub_backup(argv[1], argv[2], &zErr);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
fprintf(stderr, "%s: out of memory\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
if( zErr ){
|
||||
fprintf(stderr, "%s: %s\n", argv[0], zErr);
|
||||
sqlite3_free(zErr);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
+3
-55
@@ -2800,53 +2800,6 @@ static RtreeValue rtreeValueUp(sqlite3_value *v){
|
||||
}
|
||||
#endif /* !defined(SQLITE_RTREE_INT_ONLY) */
|
||||
|
||||
/*
|
||||
** A constraint has failed while inserting a row into an rtree table.
|
||||
** Assuming no OOM error occurs, this function sets the error message
|
||||
** (at pRtree->base.zErrMsg) to an appropriate value and returns
|
||||
** SQLITE_CONSTRAINT.
|
||||
**
|
||||
** Parameter iCol is the index of the leftmost column involved in the
|
||||
** constraint failure. If it is 0, then the constraint that failed is
|
||||
** the unique constraint on the id column. Otherwise, it is the rtree
|
||||
** (c1<=c2) constraint on columns iCol and iCol+1 that has failed.
|
||||
**
|
||||
** If an OOM occurs, SQLITE_NOMEM is returned instead of SQLITE_CONSTRAINT.
|
||||
*/
|
||||
static int rtreeConstraintError(Rtree *pRtree, int iCol){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
assert( iCol==0 || iCol%2 );
|
||||
zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", pRtree->zDb, pRtree->zName);
|
||||
if( zSql ){
|
||||
rc = sqlite3_prepare_v2(pRtree->db, zSql, -1, &pStmt, 0);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iCol==0 ){
|
||||
const char *zCol = sqlite3_column_name(pStmt, 0);
|
||||
pRtree->base.zErrMsg = sqlite3_mprintf(
|
||||
"UNIQUE constraint failed: %s.%s", pRtree->zName, zCol
|
||||
);
|
||||
}else{
|
||||
const char *zCol1 = sqlite3_column_name(pStmt, iCol);
|
||||
const char *zCol2 = sqlite3_column_name(pStmt, iCol+1);
|
||||
pRtree->base.zErrMsg = sqlite3_mprintf(
|
||||
"rtree constraint failed: %s.(%s<=%s)", pRtree->zName, zCol1, zCol2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(pStmt);
|
||||
return (rc==SQLITE_OK ? SQLITE_CONSTRAINT : rc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The xUpdate method for rtree module virtual tables.
|
||||
@@ -2897,7 +2850,7 @@ static int rtreeUpdate(
|
||||
cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]);
|
||||
cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]);
|
||||
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
|
||||
rc = rtreeConstraintError(pRtree, ii+1);
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
goto constraint;
|
||||
}
|
||||
}
|
||||
@@ -2908,7 +2861,7 @@ static int rtreeUpdate(
|
||||
cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
|
||||
cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
|
||||
if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
|
||||
rc = rtreeConstraintError(pRtree, ii+1);
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
goto constraint;
|
||||
}
|
||||
}
|
||||
@@ -2929,7 +2882,7 @@ static int rtreeUpdate(
|
||||
if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
|
||||
rc = rtreeDeleteRowid(pRtree, cell.iRowid);
|
||||
}else{
|
||||
rc = rtreeConstraintError(pRtree, 0);
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
goto constraint;
|
||||
}
|
||||
}
|
||||
@@ -3012,11 +2965,6 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
|
||||
int rc;
|
||||
i64 nRow = 0;
|
||||
|
||||
if( sqlite3_table_column_metadata(db,pRtree->zDb,"sqlite_stat1",
|
||||
0,0,0,0,0,0)==SQLITE_ERROR ){
|
||||
pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
|
||||
+9
-11
@@ -194,13 +194,13 @@ do_test rtree-2.1.3 {
|
||||
|
||||
do_test rtree-2.2.1 {
|
||||
catchsql { INSERT INTO t1 VALUES(2, 1, 3, 2, 4) }
|
||||
} {1 {UNIQUE constraint failed: t1.ii}}
|
||||
} {1 {constraint failed}}
|
||||
do_test rtree-2.2.2 {
|
||||
catchsql { INSERT INTO t1 VALUES(4, 1, 3, 4, 2) }
|
||||
} {1 {rtree constraint failed: t1.(y1<=y2)}}
|
||||
} {1 {constraint failed}}
|
||||
do_test rtree-2.2.3 {
|
||||
catchsql { INSERT INTO t1 VALUES(4, 3, 1, 2, 4) }
|
||||
} {1 {rtree constraint failed: t1.(x1<=x2)}}
|
||||
} {1 {constraint failed}}
|
||||
do_test rtree-2.2.4 {
|
||||
execsql { SELECT ii FROM t1 ORDER BY ii }
|
||||
} {1 2 3}
|
||||
@@ -236,7 +236,7 @@ do_test rtree-3.1.3 {
|
||||
# Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)):
|
||||
do_test rtree-3.2.1 {
|
||||
catchsql { INSERT INTO t1 VALUES(7, 2, 6, 4, 3) }
|
||||
} {1 {rtree constraint failed: t1.(y1<=y2)}}
|
||||
} {1 {constraint failed}}
|
||||
do_test rtree-3.2.2 {
|
||||
catchsql { INSERT INTO t1 VALUES(8, 2, 6, 3, 3) }
|
||||
} {0 {}}
|
||||
@@ -490,11 +490,11 @@ foreach {tn sql_template testdata} {
|
||||
}
|
||||
|
||||
4 "INSERT %CONF% INTO t1 VALUES(2, 7, 6, 7, 7)" {
|
||||
ROLLBACK 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
|
||||
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
REPLACE 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
|
||||
}
|
||||
|
||||
} {
|
||||
@@ -510,9 +510,7 @@ foreach {tn sql_template testdata} {
|
||||
}
|
||||
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {UNIQUE constraint failed: t1.idx}}
|
||||
set res(2) {1 {rtree constraint failed: t1.(x1<=x2)}}
|
||||
|
||||
set res(1) {1 {constraint failed}}
|
||||
do_catchsql_test $testname.1 $sql $res($error)
|
||||
do_test $testname.2 [list sql_uses_stmt db $sql] $uses
|
||||
do_execsql_test $testname.3 { SELECT * FROM t1 ORDER BY idx } $data
|
||||
|
||||
+1
-30
@@ -47,8 +47,7 @@ ifcapable !rtree {
|
||||
#
|
||||
# rtree3-8: Test OOM while registering the r-tree module with sqlite.
|
||||
#
|
||||
# rtree3-11: OOM following a constraint failure
|
||||
#
|
||||
|
||||
do_faultsim_test rtree3-1 -faults oom* -prep {
|
||||
faultsim_delete_and_reopen
|
||||
} -body {
|
||||
@@ -235,32 +234,4 @@ do_faultsim_test rtree3-10 -faults oom-* -prep {
|
||||
faultsim_test_result {0 2}
|
||||
}
|
||||
|
||||
|
||||
do_test rtree3-11.prep {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
|
||||
INSERT INTO rt VALUES(1, 2, 3, 4, 5);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test rtree3-10.1 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM rt }
|
||||
} -body {
|
||||
execsql { INSERT INTO rt VALUES(1, 2, 3, 4, 5) }
|
||||
} -test {
|
||||
faultsim_test_result {1 {UNIQUE constraint failed: rt.ii}} \
|
||||
{1 {constraint failed}}
|
||||
}
|
||||
do_faultsim_test rtree3-10.2 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM rt }
|
||||
} -body {
|
||||
execsql { INSERT INTO rt VALUES(2, 2, 3, 5, 4) }
|
||||
} -test {
|
||||
faultsim_test_result {1 {rtree constraint failed: rt.(y1<=y2)}} \
|
||||
{1 {constraint failed}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -350,3 +350,7 @@ do_eqp_execsql_test 7.4 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
# 2016-05-32
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Verify that no invalid SQL is run during initialization
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
test_sqlite3_log [list lappend ::log]
|
||||
set ::log [list]
|
||||
sqlite3 db test.db
|
||||
|
||||
|
||||
set ::log {}
|
||||
do_execsql_test rtreeG-1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1);
|
||||
} {}
|
||||
do_test rtreeG-1.1log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
do_execsql_test rtreeG-1.2 {
|
||||
INSERT INTO t1 VALUES(1,10,15,5,23),(2,20,21,5,23),(3,10,15,20,30);
|
||||
SELECT id from t1 WHERE x0>8 AND x1<16 AND y0>2 AND y1<25;
|
||||
} {1}
|
||||
do_test rtreeG-1.2log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test rtreeG-1.3 {
|
||||
SELECT id from t1 WHERE x0>8 AND x1<16 AND y0>2 AND y1<25;
|
||||
} {1}
|
||||
do_test rtreeG-1.3log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
do_execsql_test rtreeG-1.4 {
|
||||
DROP TABLE t1;
|
||||
} {}
|
||||
do_test rtreeG-1.4log {
|
||||
set ::log
|
||||
} {}
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
test_sqlite3_log
|
||||
sqlite3_initialize
|
||||
sqlite3 db test.db
|
||||
|
||||
finish_test
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
#if !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION)
|
||||
#ifndef __SQLITESESSION_H_
|
||||
#define __SQLITESESSION_H_ 1
|
||||
|
||||
/*
|
||||
@@ -1274,4 +1274,4 @@ int sqlite3changegroup_output_strm(sqlite3_changegroup*,
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) */
|
||||
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
|
||||
@@ -388,7 +388,8 @@ TESTSRC2 = \
|
||||
$(TOP)/ext/fts3/fts3_write.c \
|
||||
$(TOP)/ext/async/sqlite3async.c \
|
||||
$(TOP)/ext/session/sqlite3session.c \
|
||||
$(TOP)/ext/session/test_session.c
|
||||
$(TOP)/ext/session/test_session.c \
|
||||
$(FTS5_SRC)
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
@@ -487,9 +488,6 @@ sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
|
||||
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
scrub$(EXE): $(TOP)/ext/misc/scrub.c sqlite3.o
|
||||
$(TCC) -I. -DSCRUB_STANDALONE -o scrub$(EXE) $(TOP)/ext/misc/scrub.c sqlite3.o $(THREADLIB)
|
||||
|
||||
srcck1$(EXE): $(TOP)/tool/srcck1.c
|
||||
$(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
C Clang\scan\sdefine\s_MSC_VER\sin\ssome\scircumstances;\stherefore,\scheck\sfor\sClang\sfirst.
|
||||
D 2016-05-28T00:13:21.036
|
||||
F Makefile.in f59e0763ff448719fc1bd25513882b0567286317
|
||||
C Add\sthe\sexperimental\sSQLITE_FCNTL_WIN32_GET_HANDLE\sfile\scontrol.
|
||||
D 2016-05-03T19:36:54.758
|
||||
F Makefile.in 9eda6e1c90d05c199c3ec8a7069b0682ad307657
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7
|
||||
F Makefile.msc a7f34be67d16284e4208370365a9728094861194
|
||||
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
|
||||
F VERSION cb29eb11e493dd85b3eeec4053c03949bf98478e
|
||||
F VERSION 5d234da9b5dae329fab75ff75884cfe0a9cb3fda
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
|
||||
F autoconf/Makefile.am 1a47d071e3d5435f8f7ebff7eb6703848bbd65d4
|
||||
F autoconf/Makefile.msc 0eca137c12542bd76c253a2d24380f29ade59b95
|
||||
F autoconf/Makefile.msc ac014aab0b8314d6baa30cbd1dd4ce983f306b5d
|
||||
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
|
||||
F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
|
||||
F autoconf/configure.ac 7754404e955900cfdeba08862cdfa802eca5b0af
|
||||
F autoconf/configure.ac e2299fe80eb256dafdc68cf250e37ea1a0a6ec71
|
||||
F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd
|
||||
F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
|
||||
F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
|
||||
@@ -30,8 +30,8 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure 25cf7eb52a88846e4c1978016a9db22763c54f41 x
|
||||
F configure.ac 480e1a17d5316b1751c8edcb15b8e6b8ab0a6cc3
|
||||
F configure df9f793ad47eaeb3648a9090fbc0b96955e88b5a x
|
||||
F configure.ac 02b972db825685b4b0cca0234b2aa97f71bd15a5
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html e2118945e5f07ed146b45c9cd2b2dd6eabb8ebf2
|
||||
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
|
||||
@@ -97,12 +97,12 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
|
||||
F ext/fts3/unicode/mkunicode.tcl 2debed3f582d77b3fdd0b8830880250021571fd8
|
||||
F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
|
||||
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
|
||||
F ext/fts5/fts5.h b770c5e0a8d2ee071ddffc7ab722dbf3474a8abe
|
||||
F ext/fts5/fts5.h ff9c2782e8ed890b0de2f697a8d63971939e70c7
|
||||
F ext/fts5/fts5Int.h 3677076aecbf645a7f2a019115c6a4ec3272dd78
|
||||
F ext/fts5/fts5_aux.c daa57fb45216491814520bbb587e97bf81ced458
|
||||
F ext/fts5/fts5_buffer.c 4c1502d4c956cd092c89ce4480867f9d8bf325cd
|
||||
F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857
|
||||
F ext/fts5/fts5_expr.c da2b33c2aac91e96641c0a7cf2bbaa36eb2667f7
|
||||
F ext/fts5/fts5_expr.c 5ca4bafe29aa3d27683c90e836192e4aefd20a3f
|
||||
F ext/fts5/fts5_hash.c 880998e596b60f078348d48732ca4ad9a90caad2
|
||||
F ext/fts5/fts5_index.c b429e23fabb57506f71e406997cc46b89190dc97
|
||||
F ext/fts5/fts5_main.c b4a0fc5bf17f2f1f056ee76cdd7d2af08b360f55
|
||||
@@ -131,7 +131,7 @@ F ext/fts5/test/fts5ak.test fb26389985407826f6076bb9f382c67d3db6b5d9
|
||||
F ext/fts5/test/fts5al.test 18c277f5986df0a3d9071dfd7128afeb16fe9d5d
|
||||
F ext/fts5/test/fts5alter.test 6022c61467a82aa11c70822ccad22b328dcf0d04
|
||||
F ext/fts5/test/fts5auto.test 401c20e89f1114d733b94809be1e6f893e16c09e
|
||||
F ext/fts5/test/fts5aux.test 5dd158a1e7869e27e9762a2a452b189c728d1be3
|
||||
F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f
|
||||
F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e
|
||||
F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb
|
||||
F ext/fts5/test/fts5bigtok.test 017a9397b14e7598883a6328ead4a6539b42d59a
|
||||
@@ -215,7 +215,6 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
|
||||
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
|
||||
F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4
|
||||
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
|
||||
F ext/misc/scrub.c ea0903701e3ac02b4466ce9cffd0325c7ddbfcf0
|
||||
F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35
|
||||
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be
|
||||
@@ -253,11 +252,11 @@ F ext/rbu/sqlite3rbu.c bf36625990c6865ecf08bd844d8097ed2d0a6958
|
||||
F ext/rbu/sqlite3rbu.h 2acd0a6344a6079de15c8dc9d84d3df83a665930
|
||||
F ext/rbu/test_rbu.c 9bbdf6bd8efd58fbc4f192698df50569598fbb9e
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c d26a815b0df1c412a6881dae8d7fd3c9c08cce68
|
||||
F ext/rtree/rtree.c 0b870ccb7b58b734a2a8e1e2755a7c0ded070920
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 42dadfc7b44a436cd74a1bebc0b9b689e4eaf7ec
|
||||
F ext/rtree/rtree1.test 96a80c08440c932cd72aac50660e7af2612d2cda
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
F ext/rtree/rtree3.test 2cafe8265d1ff28f206fce88d114f208349df482
|
||||
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
|
||||
F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0
|
||||
F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e
|
||||
F ext/rtree/rtree6.test 773a90db2dce6a8353dd0d5b64bca69b29761196
|
||||
@@ -266,11 +265,10 @@ F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a
|
||||
F ext/rtree/rtree9.test b5eb13849545dfd271a54ff16784cb00d8792aea
|
||||
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
|
||||
F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
|
||||
F ext/rtree/rtreeC.test c0a9c67f2efa98b6fae12acb8a28348d231a481d
|
||||
F ext/rtree/rtreeC.test 90aaaffe2fd4f0dcd12289cad5515f6d41f45ffd
|
||||
F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca
|
||||
F ext/rtree/rtreeE.test 45a147a64a76306172819562309681d8e90f94bb
|
||||
F ext/rtree/rtreeF.test 66deb9fd1611c7ca2e374adba63debdc2dbb12b4
|
||||
F ext/rtree/rtreeG.test 3b185719630795f38594f64cd7d1de86a33f91f1
|
||||
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
|
||||
F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
|
||||
F ext/rtree/sqlite3rtree.h 9c5777af3d2921c7b4ae4954e8e5697502289d28
|
||||
@@ -296,7 +294,7 @@ F ext/session/session_common.tcl a1293167d14774b5e728836720497f40fe4ea596
|
||||
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
||||
F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
|
||||
F ext/session/sqlite3session.c 37485891b4add26cf61495df193c419f36556a32
|
||||
F ext/session/sqlite3session.h 69bf73cfd71e58f2ae5d2aa935b2c1a541aee555
|
||||
F ext/session/sqlite3session.h 64e9e7f185725ef43b97f4a9a0c0df0669844f1d
|
||||
F ext/session/test_session.c 464f2c8bf502795d95969387eb8e93f68c513c15
|
||||
F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
@@ -304,7 +302,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk eab7db27ac2a32b1172a85aa850c0b9c3a00524c
|
||||
F main.mk a283660f75c3c4b75d8c9d12a40fa38a066eee9d
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@@ -320,20 +318,20 @@ F src/alter.c 1bb0709b3048e24217b80ec6bd78a3e99a47c01b
|
||||
F src/analyze.c 37fedc80ac966ce1745811746e68e4d8fa64c7fe
|
||||
F src/attach.c 771153bd1f4ab0b97a44a13dde2c7e5e1efeba22
|
||||
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c 6df65fdd569c901a418887a1a76f82ec35044556
|
||||
F src/backup.c f60f0aa55d25d853ffde53d0b0370a7bb7ee41ce
|
||||
F src/bitvec.c 3ee4c8b2c94ed3a7377256e18199e6ff5cf33f63
|
||||
F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73
|
||||
F src/btree.c 2128172fc1c420a6fa6878827fa595407795069a
|
||||
F src/btree.h 1342a9b2cc2089e3534d3ef00204786783f6aea6
|
||||
F src/btree.c 3ae6aea66cc4e13d30162ff0d0d43c7088e34abf
|
||||
F src/btree.h a5008b9afe56e8e54ade6c436a910f112defcca9
|
||||
F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5
|
||||
F src/build.c 785fa789319d93c6ae20efbd01d4da9ce8f8a793
|
||||
F src/build.c 42e8ee74dfa3a4b99bfb2c201f8a66ab50688d95
|
||||
F src/callback.c 2e76147783386374bf01b227f752c81ec872d730
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 999a828425b35b8092a8cde25690e71c20906344
|
||||
F src/ctime.c 60e135af364d777a9ab41c97e5e89cd224da6198
|
||||
F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39
|
||||
F src/dbstat.c c845548d4346e606e2f2b7d2e714ace2b8a7dd1b
|
||||
F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f
|
||||
F src/expr.c 81bd7d87985746313770211183e900ed1ad28381
|
||||
F src/delete.c 362f89f8e6783e672c4ab06b90fca0177f9a576d
|
||||
F src/expr.c 8796c0739b2ad091e6779253f62aad6e767e2be1
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 4c0bd09e602b8ae8d36d81e31e4872d0b53c87bb
|
||||
F src/func.c ef4c18c8a66143413ce41a58d582d2c14ddf78e1
|
||||
@@ -364,37 +362,37 @@ F src/os.h 8e976e59eb4ca1c0fca6d35ee803e38951cb0343
|
||||
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
|
||||
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
|
||||
F src/os_unix.c a9443cdab41d7f3cdf0df3a5aab62fd6e1c9b234
|
||||
F src/os_win.c 852fc2ff6084296348ed3739c548b2cf32df394e
|
||||
F src/os_win.c 75edd38c5efbb177cbfdce7128635a2bb53cf16c
|
||||
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
|
||||
F src/pager.c c368634b888b1c8740aea83b36bfd266f2443e60
|
||||
F src/pager.h 8ab6b6feeee4bc0439bfde7ee59ba99df98b9bc3
|
||||
F src/pager.c a8d30c49c231e9a20d05257613db922532588963
|
||||
F src/pager.h 329bdf078a4e0a3b35084534d58625d21fd03681
|
||||
F src/parse.y 10eb2f3fb62341291528c7984498054731f9d31e
|
||||
F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df
|
||||
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
|
||||
F src/pcache.c b3230ecfc7f797063fbe167f2845da363e8f07f8
|
||||
F src/pcache.h 6b865be765d1ebd06145219550b10921c7da7cc9
|
||||
F src/pcache1.c 7f51d2b541aab57596adf62db2c4bb025d34f04d
|
||||
F src/pragma.c d98039af2deb5a4990f6635462b3f61f325a6c75
|
||||
F src/pragma.c faf42922bb7ab2f6672cb550356c1967abae3c84
|
||||
F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
|
||||
F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e
|
||||
F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
|
||||
F src/printf.c 63e6fb12bbe702dd664dc3703776c090383a5a26
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c cca3aa77b95706df5d635a2141a4d1de60ae6598
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c a0c4abf54bc6bd3a9c77a36ef3d1676045706cb2
|
||||
F src/resolve.c b8f7174e5f8c33c44ded3a25a973d0bb89228c20
|
||||
F src/rowset.c 49eb91c588a2bab36647368e031dc5b66928149d
|
||||
F src/select.c fd4a7ce2937497181063cfedb92058ac89491a5d
|
||||
F src/shell.c 14ff7f660530a52b117d110ba3390b7b2eb719b6
|
||||
F src/sqlite.h.in 9984129d86243424b765fcb3f147c697bd20bb54
|
||||
F src/sqlite.h.in 07a295a39a41cfd136bf63bdcfe478caaee78042
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 98f72cbfe00169c39089115427d06ea05fe4b4a2
|
||||
F src/sqliteInt.h 09621b4b7dba808b24262c2480ea75b045001853
|
||||
F src/sqliteInt.h f4a53f3547dab80dc7db975fa1192d9bad1f38e8
|
||||
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||
F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba
|
||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||
F src/tclsqlite.c 9c4c4589d078de37813ded708d8838b338ffb060
|
||||
F src/test1.c c0e5b69f99e95a2c9f55fdb6e46b44f1a15292d8
|
||||
F src/test1.c 7862c314d742153639ecb6ec2daf29936cc066cd
|
||||
F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b
|
||||
F src/test3.c d2c9efd2985ff8f5502ffd3253156984778d77d8
|
||||
F src/test3.c 0df6f8dbb4cbaa7106397c70a271fa6a43659042
|
||||
F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e
|
||||
F src/test5.c 5a34feec76d9b3a86aab30fd4f6cc9c48cbab4c1
|
||||
F src/test6.c a684b7abd01352ab50cb79c0bf727e6b3f381a3d
|
||||
F src/test6.c 2c014d4977efd6107ec9eef3dfdec56ac516f824
|
||||
F src/test7.c 9c89a4f1ed6bb13af0ed805b8d782bd83fcd57e3
|
||||
F src/test8.c fa262391d3edea6490a71bfaa8fed477ccbbac75
|
||||
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
@@ -439,29 +437,29 @@ F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 3b29883b0ce4a6c6f643965b66b5ca6613178e59
|
||||
F src/treeview.c c56d6ddbed564efda746236b35bcbb8238daac4b
|
||||
F src/treeview.c e4b41a37530a191579d3c53142cc44ee2eb99373
|
||||
F src/trigger.c e14840ee0c3e549e758ec9bf3e4146e166002280
|
||||
F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd
|
||||
F src/update.c 3e67ab3c0814635f355fb1f8ab010a2b9e016e7d
|
||||
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
||||
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
|
||||
F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52
|
||||
F src/vdbe.c 45e4da739187028281e342f79fae0f4145055bec
|
||||
F src/vdbe.c 4bad04212736526845fda9ca674afafb10078b2b
|
||||
F src/vdbe.h 5591b5add447096e31288b5a0a78ec5d7b5c5170
|
||||
F src/vdbeInt.h ddb157974436d87652de7dc641f7191496d9a8cd
|
||||
F src/vdbeapi.c ba85b78fe08dc4a9ce747e62c89a2b4a4547e74c
|
||||
F src/vdbeaux.c 1d6b9a979d1036db7bc39990e9e683f19520bc5c
|
||||
F src/vdbeaux.c ace1875da40b7185e604586768d5ac90de7e4f7f
|
||||
F src/vdbeblob.c c9f2f494b911c6fa34efd9803f0a10807da80f77
|
||||
F src/vdbemem.c 5cfef60e60e19cab6275d1b975bf4c791d575beb
|
||||
F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c
|
||||
F src/vdbesort.c 0a8f98366ae794442e6d1ef71d9553226d885d19
|
||||
F src/vdbetrace.c f75c5455d8cf389ef86a8bfdfd3177e0e3692484
|
||||
F src/vtab.c ce0f2ebb589b459b32c640b33af64bfa5b29aaf8
|
||||
F src/vtab.c 23b6cdfa996152d43b390504ed4a942c8caf3a00
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 02eeecc265f6ffd0597378f5d8ae9070b62a406a
|
||||
F src/wal.c 4db22ed7e77bcf672b1a685d6ddeffba8d5be302
|
||||
F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
|
||||
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
|
||||
F src/where.c b9f5b0ddb14c3827e70b5379e659cf4cfd524c4d
|
||||
F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0
|
||||
F src/wherecode.c ba71a4e4bada29aa9842200e6299714bf18c812c
|
||||
F src/where.c 019e5b10dedcf54ef077ce23dd8fce38d614dcf4
|
||||
F src/whereInt.h 3b1fc240e322613ba4e9dc857ca9c7c3390acc74
|
||||
F src/wherecode.c 28951741be3974701186281ced0564e9586a0db3
|
||||
F src/whereexpr.c eacc0e60d029a082b4fc0cc42ea98544add1319e
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
@@ -666,7 +664,7 @@ F test/expr.test 79c3e7502d9e571553b85f0ecc8ff2ac7d0e4931
|
||||
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
|
||||
F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
|
||||
F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7
|
||||
F test/filectrl.test 7c13f96457435238da99aff7343ad6a3a4885787
|
||||
F test/filectrl.test c6547e6c3c4c3ef9707a0b3dc147de718135cacc
|
||||
F test/filefmt.test e4edbdc637ca9576ccf4337a3cce627d9df7a56c
|
||||
F test/fkey1.test 13e3d48236a2b9f5c5ebd232eef9b3ab682a8a2c
|
||||
F test/fkey2.test f3d27ecba480a348c328965d154214719bb158a9
|
||||
@@ -748,7 +746,7 @@ F test/fts3defer2.test c540f5f5c2840f70c68fd9b597df817ec7170468
|
||||
F test/fts3defer3.test dd53fc13223c6d8264a98244e9b19abd35ed71cd
|
||||
F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
|
||||
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
F test/fts3expr.test 9466627007804d855bf9df2a0cfb3dac23686fdc
|
||||
F test/fts3expr.test 3401d47b229c4504424caf362cc4ff704cad4162
|
||||
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
|
||||
F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8
|
||||
F test/fts3expr4.test c39a15d676b14fc439d9bf845aa7bddcf4a74dc3
|
||||
@@ -849,7 +847,7 @@ F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
|
||||
F test/instr.test 737bbf80685232033f3abedc6ae92f75860b5dd2
|
||||
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
|
||||
F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc
|
||||
F test/intpkey.test ac71107a49a06492b69b82aafaf225400598d3c8
|
||||
F test/intpkey.test 153b70e32d008f4f4b2dff8daf448a6110b62a9c
|
||||
F test/io.test f95bca1783b01ea7761671560d023360d2dfa4cc
|
||||
F test/ioerr.test 2a24bd6ed5a8b062e64bfe1f6cf94fb25e92210d
|
||||
F test/ioerr2.test 2593563599e2cc6b6b4fcf5878b177bdd5d8df26
|
||||
@@ -879,7 +877,6 @@ F test/like.test 81632c437a947bf1f7130b19537da6a1a844806a
|
||||
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
|
||||
F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4
|
||||
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
|
||||
F test/limit2.test 55c9f4d08c89311e00afd75045ee1a2aca205cb4
|
||||
F test/loadext.test 42a3b8166dfcadcb0e0c8710dc520d97c31a8b98
|
||||
F test/loadext2.test 0408380b57adca04004247179837a18e866a74f7
|
||||
F test/lock.test be4fe08118fb988fed741f429b7dd5d65e1c90db
|
||||
@@ -929,7 +926,7 @@ F test/misc1.test 6430dabfb4b4fa480633590118964201f94d3ccc
|
||||
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
|
||||
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
|
||||
F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
|
||||
F test/misc5.test fff0f75e834bc09a39f6079320dd8c37de956f4f
|
||||
F test/misc5.test f96428ea95b3820aafc6f1c50cf48a09e4597ee1
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
|
||||
F test/misc8.test 21ac9d35a5e110279ae9e1588b8914f54de1c60b
|
||||
@@ -980,7 +977,7 @@ F test/parser1.test 222b5cbf3e2e659fec1bf7d723488c8b9c94f1d0
|
||||
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
|
||||
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
|
||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||
F test/permutations.test 544a2f317fc5045bc512ae432f89eae303c0d640
|
||||
F test/permutations.test b6b3e165fdc1b8c82a820033646dbfc6a7a01746
|
||||
F test/pragma.test 1e94755164a3a3264cd39836de4bebcb7809e5f8
|
||||
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
|
||||
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
|
||||
@@ -1056,7 +1053,7 @@ F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
|
||||
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
|
||||
F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test c90b0415cea6eeefb86b4ab1651b06247922ca52
|
||||
F test/shell1.test 023657b3f8ba108dbd5ff2ebc8fceb3cf6d4ff9f
|
||||
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
|
||||
F test/shell3.test da513d522ef6f01cee8475dcf8332bff8982b3dd
|
||||
F test/shell4.test 69995ee1cc278eb149aa8746ce1f935f4eaf98b9
|
||||
@@ -1070,7 +1067,7 @@ F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
|
||||
F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
|
||||
F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
|
||||
F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b
|
||||
F test/snapshot.test a19465046168b4420b5afeed37c3214e42a49f4a
|
||||
F test/snapshot.test 3adc4ef09d407b501f899a6c329bdf45dc725c1b
|
||||
F test/snapshot_fault.test 062ff0438a074978d45e9f9a92e7ad459b74ee73
|
||||
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
|
||||
F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087
|
||||
@@ -1078,7 +1075,7 @@ F test/sort.test c2adc635c2564241fefec0b3a68391ef6868fd3b
|
||||
F test/sort2.test cc23b7c19d684657559e8a55b02f7fcee03851d0
|
||||
F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5
|
||||
F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c
|
||||
F test/sort5.test 30cc17768e0c06ecb048e08efec59c11811fd186
|
||||
F test/sort5.test e47ec7a490b9b36787755874175d8f413a3883d9
|
||||
F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66
|
||||
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
|
||||
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
|
||||
@@ -1114,11 +1111,9 @@ F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
|
||||
F test/tclsqlite.test e1306001a0ca92250b691ea6d3cecaca5b6342aa
|
||||
F test/tempdb.test bd92eba8f20e16a9136e434e20b280794de3cdb6
|
||||
F test/tempdb2.test 4fc92055f2a3f7626c0f2eabbb637dc021b311d5
|
||||
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
|
||||
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
|
||||
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
|
||||
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
|
||||
F test/tester.tcl 3ace46dee55f07e28a1e3ab03258b929fefcee5b
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
@@ -1356,7 +1351,6 @@ F test/walcksum.test bb234a1bb42248b3515d992b719708015c384278
|
||||
F test/walcrash.test 21038858cc552077b0522f50b0fa87e38139306a
|
||||
F test/walcrash2.test a0edab4e5390f03b99a790de89aad15d6ec70b36
|
||||
F test/walcrash3.test e426aa58122d20f2b9fbe9a507f9eb8cab85b8af
|
||||
F test/walcrash4.test fa7d92ab84fe3ff409e1cda88a622545e2bc2b66
|
||||
F test/walfault.test 1f8389f7709877e9b4cc679033d71d6fe529056b
|
||||
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
|
||||
F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c
|
||||
@@ -1422,9 +1416,8 @@ F tool/fuzzershell.c 94019b185caceffc9f7c7b678a6489e42bc2aefa
|
||||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/lemon.c 09a96bed19955697a5e20c49ad863ec2005815a2
|
||||
F tool/lempar.c f06b7e98a6b7efb404375b6f4a0c71c85faa1512
|
||||
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
|
||||
F tool/lemon.c f38a55106d79b7a4c063abb958517d6c47dc6ac7
|
||||
F tool/lempar.c 404ea3dc27dbeed343f0e61b1d36e97b9f5f0fb6
|
||||
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
|
||||
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
|
||||
F tool/mkautoconfamal.sh e855df211ecbcc7131dee817110ff386cfb112f7
|
||||
@@ -1442,7 +1435,6 @@ F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
|
||||
F tool/mkvsix.tcl 4abcaf3267171b2faadaf9b82a0dfbaa6e98f8b7
|
||||
F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091
|
||||
F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97
|
||||
F tool/opcodesum.tcl 740ed206ba8c5040018988129abbf3089a0ccf4a
|
||||
F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
|
||||
F tool/replace.tcl 7727c60a04299b65a92f5e1590896fea0f25b9e0
|
||||
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
|
||||
@@ -1454,8 +1446,7 @@ F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68
|
||||
F tool/showstat4.c bda40d6e395df7edb6e9ea630784d3d762c35b4b
|
||||
F tool/showwal.c ec79959834f7b21f1e0a2aa52bb7c056d2203977
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/spaceanal.tcl 85d90e6674d8298e3eaf82dbcef3abc2d5317f3e
|
||||
F tool/speed-check.sh 45d3bf861b009993ff401f0d00e34a4cc937fce4
|
||||
F tool/spaceanal.tcl 93c1fdc9733c525b17a2024c7df193daa002e037
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@@ -1495,7 +1486,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 7c2cd4b05f88a759bdf72d3a249db5984fd7105d
|
||||
R 49f2ff6c03dd42bcf8b96859a75667c2
|
||||
P bcaa650e8796984b275eceba0ed477f360295d01
|
||||
R 7e9bbb4137cb671c555aabb8a094cd9b
|
||||
T *branch * win32GetHandle
|
||||
T *sym-win32GetHandle *
|
||||
T -sym-trunk *
|
||||
U mistachkin
|
||||
Z 7fd0da57a78dfad092dddeef636c580c
|
||||
Z e75ba582b3d5fa180704c51e0604373b
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3f710bc3617691fa7432a24f1410d4651ca181b6
|
||||
b6ddabe1818c36007c476a31e2e35755630a5f14
|
||||
@@ -784,6 +784,8 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
rc = sqlite3_backup_finish(&b);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
|
||||
}else{
|
||||
sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
|
||||
}
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo)==0 );
|
||||
|
||||
+57
-59
@@ -1676,11 +1676,11 @@ static int decodeFlags(MemPage *pPage, int flagByte){
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pBt = pPage->pBt;
|
||||
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
|
||||
/* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
|
||||
** interior table b-tree page. */
|
||||
/* EVIDENCE-OF: R-03640-13415 A value of 5 means the page is an interior
|
||||
** table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
|
||||
/* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
|
||||
** leaf table b-tree page. */
|
||||
/* EVIDENCE-OF: R-20501-61796 A value of 13 means the page is a leaf
|
||||
** table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
|
||||
pPage->intKey = 1;
|
||||
if( pPage->leaf ){
|
||||
@@ -1694,11 +1694,11 @@ static int decodeFlags(MemPage *pPage, int flagByte){
|
||||
pPage->maxLocal = pBt->maxLeaf;
|
||||
pPage->minLocal = pBt->minLeaf;
|
||||
}else if( flagByte==PTF_ZERODATA ){
|
||||
/* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
|
||||
** interior index b-tree page. */
|
||||
/* EVIDENCE-OF: R-27225-53936 A value of 2 means the page is an interior
|
||||
** index b-tree page. */
|
||||
assert( (PTF_ZERODATA)==2 );
|
||||
/* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
|
||||
** leaf index b-tree page. */
|
||||
/* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf
|
||||
** index b-tree page. */
|
||||
assert( (PTF_ZERODATA|PTF_LEAF)==10 );
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
@@ -6072,7 +6072,9 @@ static int clearCell(
|
||||
static int fillInCell(
|
||||
MemPage *pPage, /* The page that contains the cell */
|
||||
unsigned char *pCell, /* Complete text of the cell */
|
||||
const BtreePayload *pX, /* Payload with which to construct the cell */
|
||||
const void *pKey, i64 nKey, /* The key */
|
||||
const void *pData,int nData, /* The data */
|
||||
int nZero, /* Extra zero bytes to append to pData */
|
||||
int *pnSize /* Write cell size here */
|
||||
){
|
||||
int nPayload;
|
||||
@@ -6096,23 +6098,26 @@ static int fillInCell(
|
||||
|
||||
/* Fill in the header. */
|
||||
nHeader = pPage->childPtrSize;
|
||||
if( pPage->intKey ){
|
||||
nPayload = pX->nData + pX->nZero;
|
||||
pSrc = pX->pData;
|
||||
nSrc = pX->nData;
|
||||
assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */
|
||||
nPayload = nData + nZero;
|
||||
if( pPage->intKeyLeaf ){
|
||||
nHeader += putVarint32(&pCell[nHeader], nPayload);
|
||||
nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey);
|
||||
}else{
|
||||
assert( pX->nData==0 );
|
||||
assert( pX->nZero==0 );
|
||||
assert( pX->nKey<=0x7fffffff && pX->pKey!=0 );
|
||||
nSrc = nPayload = (int)pX->nKey;
|
||||
pSrc = pX->pKey;
|
||||
nHeader += putVarint32(&pCell[nHeader], nPayload);
|
||||
assert( nData==0 );
|
||||
assert( nZero==0 );
|
||||
}
|
||||
nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey);
|
||||
|
||||
/* Fill in the payload */
|
||||
/* Fill in the payload size */
|
||||
if( pPage->intKey ){
|
||||
pSrc = pData;
|
||||
nSrc = nData;
|
||||
nData = 0;
|
||||
}else{
|
||||
assert( nKey<=0x7fffffff && pKey!=0 );
|
||||
nPayload = (int)nKey;
|
||||
pSrc = pKey;
|
||||
nSrc = (int)nKey;
|
||||
}
|
||||
if( nPayload<=pPage->maxLocal ){
|
||||
n = nHeader + nPayload;
|
||||
testcase( n==3 );
|
||||
@@ -6150,7 +6155,7 @@ static int fillInCell(
|
||||
CellInfo info;
|
||||
pPage->xParseCell(pPage, pCell, &info);
|
||||
assert( nHeader==(int)(info.pPayload - pCell) );
|
||||
assert( info.nKey==pX->nKey );
|
||||
assert( info.nKey==nKey );
|
||||
assert( *pnSize == info.nSize );
|
||||
assert( spaceLeft == info.nLocal );
|
||||
}
|
||||
@@ -6235,6 +6240,10 @@ static int fillInCell(
|
||||
pSrc += n;
|
||||
nSrc -= n;
|
||||
spaceLeft -= n;
|
||||
if( nSrc==0 ){
|
||||
nSrc = nData;
|
||||
pSrc = pData;
|
||||
}
|
||||
}
|
||||
releasePage(pToRelease);
|
||||
return SQLITE_OK;
|
||||
@@ -6301,8 +6310,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
** in pTemp or the original pCell) and also record its index.
|
||||
** Allocating a new entry in pPage->aCell[] implies that
|
||||
** pPage->nOverflow is incremented.
|
||||
**
|
||||
** *pRC must be SQLITE_OK when this routine is called.
|
||||
*/
|
||||
static void insertCell(
|
||||
MemPage *pPage, /* Page into which we are copying */
|
||||
@@ -6318,7 +6325,8 @@ static void insertCell(
|
||||
u8 *data; /* The content of the whole page */
|
||||
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
|
||||
|
||||
assert( *pRC==SQLITE_OK );
|
||||
if( *pRC ) return;
|
||||
|
||||
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
|
||||
assert( MX_CELL(pPage->pBt)<=10921 );
|
||||
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
|
||||
@@ -6824,10 +6832,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
||||
while( ((*(pOut++) = *(pCell++))&0x80) && pCell<pStop );
|
||||
|
||||
/* Insert the new divider cell into pParent. */
|
||||
if( rc==SQLITE_OK ){
|
||||
insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
|
||||
0, pPage->pgno, &rc);
|
||||
}
|
||||
insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
|
||||
0, pPage->pgno, &rc);
|
||||
|
||||
/* Set the right-child pointer of pParent to point to the new page. */
|
||||
put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew);
|
||||
@@ -7919,19 +7925,13 @@ static int balance(BtCursor *pCur){
|
||||
|
||||
|
||||
/*
|
||||
** Insert a new record into the BTree. The content of the new record
|
||||
** is described by the pX object. The pCur cursor is used only to
|
||||
** define what table the record should be inserted into, and is left
|
||||
** pointing at a random location.
|
||||
** Insert a new record into the BTree. The key is given by (pKey,nKey)
|
||||
** and the data is given by (pData,nData). The cursor is used only to
|
||||
** define what table the record should be inserted into. The cursor
|
||||
** is left pointing at a random location.
|
||||
**
|
||||
** For a table btree (used for rowid tables), only the pX.nKey value of
|
||||
** the key is used. The pX.pKey value must be NULL. The pX.nKey is the
|
||||
** rowid or INTEGER PRIMARY KEY of the row. The pX.nData,pData,nZero fields
|
||||
** hold the content of the row.
|
||||
**
|
||||
** For an index btree (used for indexes and WITHOUT ROWID tables), the
|
||||
** key is an arbitrary byte sequence stored in pX.pKey,nKey. The
|
||||
** pX.pData,nData,nZero fields must be zero.
|
||||
** For an INTKEY table, only the nKey value of the key is used. pKey is
|
||||
** ignored. For a ZERODATA table, the pData and nData are both ignored.
|
||||
**
|
||||
** If the seekResult parameter is non-zero, then a successful call to
|
||||
** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already
|
||||
@@ -7948,7 +7948,9 @@ static int balance(BtCursor *pCur){
|
||||
*/
|
||||
int sqlite3BtreeInsert(
|
||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||
const BtreePayload *pX, /* Content of the row to be inserted */
|
||||
const void *pKey, i64 nKey, /* The key of the new record */
|
||||
const void *pData, int nData, /* The data of the new record */
|
||||
int nZero, /* Number of extra 0 bytes to append to data */
|
||||
int appendBias, /* True if this is likely an append */
|
||||
int seekResult /* Result of prior MovetoUnpacked() call */
|
||||
){
|
||||
@@ -7978,7 +7980,7 @@ int sqlite3BtreeInsert(
|
||||
** keys with no associated data. If the cursor was opened expecting an
|
||||
** intkey table, the caller should be inserting integer keys with a
|
||||
** blob of associated data. */
|
||||
assert( (pX->pKey==0)==(pCur->pKeyInfo==0) );
|
||||
assert( (pKey==0)==(pCur->pKeyInfo==0) );
|
||||
|
||||
/* Save the positions of any other cursors open on this table.
|
||||
**
|
||||
@@ -7997,38 +7999,38 @@ int sqlite3BtreeInsert(
|
||||
}
|
||||
|
||||
if( pCur->pKeyInfo==0 ){
|
||||
assert( pX->pKey==0 );
|
||||
assert( pKey==0 );
|
||||
/* If this is an insert into a table b-tree, invalidate any incrblob
|
||||
** cursors open on the row being replaced */
|
||||
invalidateIncrblobCursors(p, pX->nKey, 0);
|
||||
invalidateIncrblobCursors(p, nKey, 0);
|
||||
|
||||
/* If the cursor is currently on the last row and we are appending a
|
||||
** new row onto the end, set the "loc" to avoid an unnecessary
|
||||
** btreeMoveto() call */
|
||||
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey>0
|
||||
&& pCur->info.nKey==pX->nKey-1 ){
|
||||
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0
|
||||
&& pCur->info.nKey==nKey-1 ){
|
||||
loc = -1;
|
||||
}else if( loc==0 ){
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, appendBias, &loc);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, nKey, appendBias, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}else if( loc==0 ){
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc);
|
||||
rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
|
||||
|
||||
pPage = pCur->apPage[pCur->iPage];
|
||||
assert( pPage->intKey || pX->nKey>=0 );
|
||||
assert( pPage->intKey || nKey>=0 );
|
||||
assert( pPage->leaf || !pPage->intKey );
|
||||
|
||||
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
|
||||
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
|
||||
pCur->pgnoRoot, nKey, nData, pPage->pgno,
|
||||
loc==0 ? "overwrite" : "new entry"));
|
||||
assert( pPage->isInit );
|
||||
newCell = pBt->pTmpSpace;
|
||||
assert( newCell!=0 );
|
||||
rc = fillInCell(pPage, newCell, pX, &szNew);
|
||||
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
|
||||
if( rc ) goto end_insert;
|
||||
assert( szNew==pPage->xCellSize(pPage, newCell) );
|
||||
assert( szNew <= MX_CELL_SIZE(pBt) );
|
||||
@@ -8054,7 +8056,6 @@ int sqlite3BtreeInsert(
|
||||
assert( pPage->leaf );
|
||||
}
|
||||
insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
|
||||
assert( pPage->nOverflow==0 || rc==SQLITE_OK );
|
||||
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
|
||||
|
||||
/* If no error has occurred and pPage has an overflow cell, call balance()
|
||||
@@ -8078,8 +8079,7 @@ int sqlite3BtreeInsert(
|
||||
** row without seeking the cursor. This can be a big performance boost.
|
||||
*/
|
||||
pCur->info.nSize = 0;
|
||||
if( pPage->nOverflow ){
|
||||
assert( rc==SQLITE_OK );
|
||||
if( rc==SQLITE_OK && pPage->nOverflow ){
|
||||
pCur->curFlags &= ~(BTCF_ValidNKey);
|
||||
rc = balance(pCur);
|
||||
|
||||
@@ -8215,9 +8215,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
pTmp = pBt->pTmpSpace;
|
||||
assert( pTmp!=0 );
|
||||
rc = sqlite3PagerWrite(pLeaf->pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
|
||||
}
|
||||
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
|
||||
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
|
||||
+3
-29
@@ -39,7 +39,6 @@
|
||||
typedef struct Btree Btree;
|
||||
typedef struct BtCursor BtCursor;
|
||||
typedef struct BtShared BtShared;
|
||||
typedef struct BtreePayload BtreePayload;
|
||||
|
||||
|
||||
int sqlite3BtreeOpen(
|
||||
@@ -251,34 +250,9 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
|
||||
#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
|
||||
|
||||
/* An instance of the BtreePayload object describes the content of a single
|
||||
** entry in either an index or table btree.
|
||||
**
|
||||
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
|
||||
** an arbitrary key and no data. These btrees have pKey,nKey set to their
|
||||
** key and pData,nData,nZero set to zero.
|
||||
**
|
||||
** Table btrees (used for rowid tables) contain an integer rowid used as
|
||||
** the key and passed in the nKey field. The pKey field is zero.
|
||||
** pData,nData hold the content of the new entry. nZero extra zero bytes
|
||||
** are appended to the end of the content when constructing the entry.
|
||||
**
|
||||
** This object is used to pass information into sqlite3BtreeInsert(). The
|
||||
** same information used to be passed as five separate parameters. But placing
|
||||
** the information into this object helps to keep the interface more
|
||||
** organized and understandable, and it also helps the resulting code to
|
||||
** run a little faster by using fewer registers for parameter passing.
|
||||
*/
|
||||
struct BtreePayload {
|
||||
const void *pKey; /* Key content for indexes. NULL for tables */
|
||||
sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */
|
||||
const void *pData; /* Data for tables. NULL for indexes */
|
||||
int nData; /* Size of pData. 0 if none. */
|
||||
int nZero; /* Extra zero data appended after pData,nData */
|
||||
};
|
||||
|
||||
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
|
||||
int bias, int seekResult);
|
||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||
const void *pData, int nData,
|
||||
int nZero, int bias, int seekResult);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||
|
||||
+9
-10
@@ -592,10 +592,16 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
|
||||
** db parameter can be used with db->pnBytesFreed to measure the memory
|
||||
** used by the Table object.
|
||||
*/
|
||||
static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
|
||||
void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
Index *pIndex, *pNext;
|
||||
TESTONLY( int nLookaside; ) /* Used to verify lookaside not used for schema */
|
||||
|
||||
assert( !pTable || pTable->nRef>0 );
|
||||
|
||||
/* Do not delete the table until the reference count reaches zero. */
|
||||
if( !pTable ) return;
|
||||
if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
|
||||
|
||||
/* Record the number of outstanding lookaside allocations in schema Tables
|
||||
** prior to doing any free() operations. Since schema Tables do not use
|
||||
** lookaside, this number should not change. */
|
||||
@@ -635,13 +641,6 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
|
||||
/* Verify that no lookaside memory was used by schema tables */
|
||||
assert( nLookaside==0 || nLookaside==db->lookaside.nOut );
|
||||
}
|
||||
void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
/* Do not delete the table until the reference count reaches zero. */
|
||||
if( !pTable ) return;
|
||||
if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
|
||||
deleteTable(db, pTable);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Unlink the given table from the hash tables and the delete the
|
||||
@@ -2222,7 +2221,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
pTable->nCol = 0;
|
||||
nErr++;
|
||||
}
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
if( pSelTab ) sqlite3DeleteTable(db, pSelTab);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
db->lookaside.bDisable--;
|
||||
} else {
|
||||
@@ -3365,7 +3364,7 @@ Index *sqlite3CreateIndex(
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
||||
sqlite3VdbeAddOp0(v, OP_Expire);
|
||||
sqlite3VdbeAddOp1(v, OP_Expire, 0);
|
||||
}
|
||||
|
||||
sqlite3VdbeJumpHere(v, pIndex->tnum);
|
||||
|
||||
@@ -45,13 +45,6 @@ static const char * const azCompileOpt[] = {
|
||||
#if SQLITE_CHECK_PAGES
|
||||
"CHECK_PAGES",
|
||||
#endif
|
||||
#if defined(__clang__) && defined(__clang_version__)
|
||||
"COMPILER=clang-" __clang_version__,
|
||||
#elif defined(_MSC_VER)
|
||||
"COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER),
|
||||
#elif defined(__GNUC__) && defined(__VERSION__)
|
||||
"COMPILER=gcc-" __VERSION__,
|
||||
#endif
|
||||
#if SQLITE_COVERAGE_TEST
|
||||
"COVERAGE_TEST",
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -239,7 +239,7 @@ void sqlite3DeleteFrom(
|
||||
int addrBypass = 0; /* Address of jump over the delete logic */
|
||||
int addrLoop = 0; /* Top of the delete loop */
|
||||
int addrEphOpen = 0; /* Instruction to open the Ephemeral table */
|
||||
int bComplex; /* True if there are triggers or FKs or
|
||||
int bComplex; /* True if there are triggers or FKs or or
|
||||
** subqueries in the WHERE clause */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
@@ -373,8 +373,8 @@ void sqlite3DeleteFrom(
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
{
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE;
|
||||
if( sNC.ncFlags & NC_VarSelect ) bComplex = 1;
|
||||
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
|
||||
if( pWhere && ExprHasProperty(pWhere, EP_Subquery) ) bComplex = 1;
|
||||
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
|
||||
if( HasRowid(pTab) ){
|
||||
/* For a rowid table, initialize the RowSet to an empty set */
|
||||
|
||||
+2
-1
@@ -2222,7 +2222,8 @@ static void sqlite3ExprCodeIN(
|
||||
if( eType==IN_INDEX_ROWID ){
|
||||
/* In this case, the RHS is the ROWID of table b-tree
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, r1);
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
/* In this case, the RHS is an index b-tree.
|
||||
|
||||
@@ -3434,6 +3434,12 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_WIN32_GET_HANDLE: {
|
||||
LPHANDLE phFile = (LPHANDLE)pArg;
|
||||
*phFile = pFile->h;
|
||||
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#ifdef SQLITE_TEST
|
||||
case SQLITE_FCNTL_WIN32_SET_HANDLE: {
|
||||
LPHANDLE phFile = (LPHANDLE)pArg;
|
||||
|
||||
+58
-37
@@ -1876,24 +1876,20 @@ static int pager_error(Pager *pPager, int rc){
|
||||
static int pager_truncate(Pager *pPager, Pgno nPage);
|
||||
|
||||
/*
|
||||
** The write transaction open on pPager is being committed (bCommit==1)
|
||||
** or rolled back (bCommit==0).
|
||||
** The write transaction open on the pager passed as the only argument is
|
||||
** being committed. This function returns true if all dirty pages should
|
||||
** be flushed to disk, or false otherwise. Pages should be flushed to disk
|
||||
** unless one of the following is true:
|
||||
**
|
||||
** Return TRUE if and only if all dirty pages should be flushed to disk.
|
||||
** * The db is an in-memory database.
|
||||
**
|
||||
** Rules:
|
||||
** * The db is a temporary database and the db file has not been opened.
|
||||
**
|
||||
** * For non-TEMP databases, always sync to disk. This is necessary
|
||||
** for transactions to be durable.
|
||||
**
|
||||
** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing
|
||||
** file has been created already (via a spill on pagerStress()) and
|
||||
** when the number of dirty pages in memory exceeds 25% of the total
|
||||
** cache size.
|
||||
** * The db is a temporary database and the cache contains less than
|
||||
** C/4 dirty pages, where C is the configured cache-size.
|
||||
*/
|
||||
static int pagerFlushOnCommit(Pager *pPager, int bCommit){
|
||||
static int pagerFlushOnCommit(Pager *pPager){
|
||||
if( pPager->tempFile==0 ) return 1;
|
||||
if( !bCommit ) return 0;
|
||||
if( !isOpen(pPager->fd) ) return 0;
|
||||
return (sqlite3PCachePercentDirty(pPager->pPCache)>=25);
|
||||
}
|
||||
@@ -2001,7 +1997,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|
||||
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
|
||||
){
|
||||
rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile);
|
||||
rc = zeroJournalHdr(pPager, hasMaster);
|
||||
pPager->journalOff = 0;
|
||||
}else{
|
||||
/* This branch may be executed with Pager.journalMode==MEMORY if
|
||||
@@ -2036,14 +2032,12 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
|
||||
sqlite3BitvecDestroy(pPager->pInJournal);
|
||||
pPager->pInJournal = 0;
|
||||
pPager->nRec = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pagerFlushOnCommit(pPager, bCommit) ){
|
||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||
}else{
|
||||
sqlite3PcacheClearWritable(pPager->pPCache);
|
||||
}
|
||||
sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
|
||||
if( MEMDB || pagerFlushOnCommit(pPager) ){
|
||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||
}else{
|
||||
sqlite3PcacheClearWritable(pPager->pPCache);
|
||||
}
|
||||
sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
|
||||
|
||||
if( pagerUseWal(pPager) ){
|
||||
/* Drop the WAL write-lock, if any. Also, if the connection was in
|
||||
@@ -2377,6 +2371,7 @@ static int pager_playback_one_page(
|
||||
assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
|
||||
pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
pPg->flags &= ~PGHDR_NEED_READ;
|
||||
sqlite3PcacheMakeDirty(pPg);
|
||||
}
|
||||
if( pPg ){
|
||||
@@ -2390,10 +2385,33 @@ static int pager_playback_one_page(
|
||||
pData = pPg->pData;
|
||||
memcpy(pData, (u8*)aData, pPager->pageSize);
|
||||
pPager->xReiniter(pPg);
|
||||
/* It used to be that sqlite3PcacheMakeClean(pPg) was called here. But
|
||||
** that call was dangerous and had no detectable benefit since the cache
|
||||
** is normally cleaned by sqlite3PcacheCleanAll() after rollback and so
|
||||
** has been removed. */
|
||||
if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){
|
||||
/* If the contents of this page were just restored from the main
|
||||
** journal file, then its content must be as they were when the
|
||||
** transaction was first opened. In this case we can mark the page
|
||||
** as clean, since there will be no need to write it out to the
|
||||
** database.
|
||||
**
|
||||
** There is one exception to this rule. If the page is being rolled
|
||||
** back as part of a savepoint (or statement) rollback from an
|
||||
** unsynced portion of the main journal file, then it is not safe
|
||||
** to mark the page as clean. This is because marking the page as
|
||||
** clean will clear the PGHDR_NEED_SYNC flag. Since the page is
|
||||
** already in the journal file (recorded in Pager.pInJournal) and
|
||||
** the PGHDR_NEED_SYNC flag is cleared, if the page is written to
|
||||
** again within this transaction, it will be marked as dirty but
|
||||
** the PGHDR_NEED_SYNC flag will not be set. It could then potentially
|
||||
** be written out into the database file before its journal file
|
||||
** segment is synced. If a crash occurs during or following this,
|
||||
** database corruption may ensue.
|
||||
**
|
||||
** Update: Another exception is for temp files that are not
|
||||
** in-memory databases. In this case the page may have been dirty
|
||||
** at the start of the transaction.
|
||||
*/
|
||||
assert( !pagerUseWal(pPager) );
|
||||
if( pPager->tempFile==0 ) sqlite3PcacheMakeClean(pPg);
|
||||
}
|
||||
pager_set_pagehash(pPg);
|
||||
|
||||
/* If this was page 1, then restore the value of Pager.dbFileVers.
|
||||
@@ -5990,21 +6008,14 @@ int sqlite3PagerIswriteable(DbPage *pPg){
|
||||
**
|
||||
** Tests show that this optimization can quadruple the speed of large
|
||||
** DELETE operations.
|
||||
**
|
||||
** This optimization cannot be used with a temp-file, as the page may
|
||||
** have been dirty at the start of the transaction. In that case, if
|
||||
** memory pressure forces page pPg out of the cache, the data does need
|
||||
** to be written out to disk so that it may be read back in if the
|
||||
** current transaction is rolled back.
|
||||
*/
|
||||
void sqlite3PagerDontWrite(PgHdr *pPg){
|
||||
Pager *pPager = pPg->pPager;
|
||||
if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
|
||||
if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
|
||||
PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
|
||||
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
|
||||
pPg->flags |= PGHDR_DONT_WRITE;
|
||||
pPg->flags &= ~PGHDR_WRITEABLE;
|
||||
testcase( pPg->flags & PGHDR_NEED_SYNC );
|
||||
pager_set_pagehash(pPg);
|
||||
}
|
||||
}
|
||||
@@ -6200,7 +6211,7 @@ int sqlite3PagerCommitPhaseOne(
|
||||
if( NEVER(pPager->errCode) ) return pPager->errCode;
|
||||
|
||||
/* Provide the ability to easily simulate an I/O error during testing */
|
||||
if( sqlite3FaultSim(400) ) return SQLITE_IOERR;
|
||||
if( (rc = sqlite3FaultSim(400))!=SQLITE_OK ) return rc;
|
||||
|
||||
PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
|
||||
pPager->zFilename, zMaster, pPager->dbSize));
|
||||
@@ -6210,7 +6221,7 @@ int sqlite3PagerCommitPhaseOne(
|
||||
|
||||
assert( MEMDB==0 || pPager->tempFile );
|
||||
assert( isOpen(pPager->fd) || pPager->tempFile );
|
||||
if( 0==pagerFlushOnCommit(pPager, 1) ){
|
||||
if( 0==pagerFlushOnCommit(pPager) ){
|
||||
/* If this is an in-memory db, or no pages have been written to, or this
|
||||
** function has already been called, it is mostly a no-op. However, any
|
||||
** backup in progress needs to be restarted. */
|
||||
@@ -6835,7 +6846,6 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
/* In order to be able to rollback, an in-memory database must journal
|
||||
** the page we are moving from.
|
||||
*/
|
||||
assert( pPager->tempFile || !MEMDB );
|
||||
if( pPager->tempFile ){
|
||||
rc = sqlite3PagerWrite(pPg);
|
||||
if( rc ) return rc;
|
||||
@@ -6910,7 +6920,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
||||
** to exist, in case the transaction needs to roll back. Use pPgOld
|
||||
** as the original page since it has already been allocated.
|
||||
*/
|
||||
if( pPager->tempFile && pPgOld ){
|
||||
if( pPager->tempFile ){
|
||||
assert( pPgOld );
|
||||
sqlite3PcacheMove(pPgOld, origPgno);
|
||||
sqlite3PagerUnrefNotNull(pPgOld);
|
||||
}
|
||||
@@ -7157,6 +7168,16 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
|
||||
return &pPager->pBackup;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VACUUM
|
||||
/*
|
||||
** Unless this is an in-memory or temporary database, clear the pager cache.
|
||||
*/
|
||||
void sqlite3PagerClearCache(Pager *pPager){
|
||||
assert( MEMDB==0 || pPager->tempFile );
|
||||
if( pPager->tempFile==0 ) pager_reset(pPager);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/*
|
||||
** This function is called when the user invokes "PRAGMA wal_checkpoint",
|
||||
|
||||
@@ -203,6 +203,7 @@ const char *sqlite3PagerJournalname(Pager*);
|
||||
void *sqlite3PagerTempSpace(Pager*);
|
||||
int sqlite3PagerIsMemdb(Pager*);
|
||||
void sqlite3PagerCacheStat(Pager *, int, int, int *);
|
||||
void sqlite3PagerClearCache(Pager *);
|
||||
int sqlite3SectorSize(sqlite3_file *);
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
|
||||
+25
-185
@@ -14,29 +14,7 @@
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** A complete page cache is an instance of this structure. Every
|
||||
** entry in the cache holds a single page of the database file. The
|
||||
** btree layer only operates on the cached copy of the database pages.
|
||||
**
|
||||
** A page cache entry is "clean" if it exactly matches what is currently
|
||||
** on disk. A page is "dirty" if it has been modified and needs to be
|
||||
** persisted to disk.
|
||||
**
|
||||
** pDirty, pDirtyTail, pSynced:
|
||||
** All dirty pages are linked into the doubly linked list using
|
||||
** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order
|
||||
** such that p was added to the list more recently than p->pDirtyNext.
|
||||
** PCache.pDirty points to the first (newest) element in the list and
|
||||
** pDirtyTail to the last (oldest).
|
||||
**
|
||||
** The PCache.pSynced variable is used to optimize searching for a dirty
|
||||
** page to eject from the cache mid-transaction. It is better to eject
|
||||
** a page that does not require a journal sync than one that does.
|
||||
** Therefore, pSynced is maintained to that it *almost* always points
|
||||
** to either the oldest page in the pDirty/pDirtyTail list that has a
|
||||
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
|
||||
** (so that the right page to eject can be found by following pDirtyPrev
|
||||
** pointers).
|
||||
** A complete page cache is an instance of this structure.
|
||||
*/
|
||||
struct PCache {
|
||||
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
|
||||
@@ -53,95 +31,6 @@ struct PCache {
|
||||
sqlite3_pcache *pCache; /* Pluggable cache module */
|
||||
};
|
||||
|
||||
/********************************** Test and Debug Logic **********************/
|
||||
/*
|
||||
** Debug tracing macros. Enable by by changing the "0" to "1" and
|
||||
** recompiling.
|
||||
**
|
||||
** When sqlite3PcacheTrace is 1, single line trace messages are issued.
|
||||
** When sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries
|
||||
** is displayed for many operations, resulting in a lot of output.
|
||||
*/
|
||||
#if defined(SQLITE_DEBUG) && 0
|
||||
int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
|
||||
int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
|
||||
# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
|
||||
void pcacheDump(PCache *pCache){
|
||||
int N;
|
||||
int i, j;
|
||||
sqlite3_pcache_page *pLower;
|
||||
PgHdr *pPg;
|
||||
unsigned char *a;
|
||||
|
||||
if( sqlite3PcacheTrace<2 ) return;
|
||||
if( pCache->pCache==0 ) return;
|
||||
N = sqlite3PcachePagecount(pCache);
|
||||
if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
|
||||
for(i=1; i<=N; i++){
|
||||
pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
|
||||
if( pLower==0 ) continue;
|
||||
pPg = (PgHdr*)pLower->pExtra;
|
||||
printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
|
||||
a = (unsigned char *)pLower->pBuf;
|
||||
for(j=0; j<12; j++) printf("%02x", a[j]);
|
||||
printf("\n");
|
||||
if( pPg->pPage==0 ){
|
||||
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define pcacheTrace(X)
|
||||
# define pcacheDump(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Check invariants on a PgHdr entry. Return true if everything is OK.
|
||||
** Return false if any invariant is violated.
|
||||
**
|
||||
** This routine is for use inside of assert() statements only. For
|
||||
** example:
|
||||
**
|
||||
** assert( sqlite3PcachePageSanity(pPg) );
|
||||
*/
|
||||
#if SQLITE_DEBUG
|
||||
int sqlite3PcachePageSanity(PgHdr *pPg){
|
||||
PCache *pCache;
|
||||
assert( pPg!=0 );
|
||||
assert( pPg->pgno>0 ); /* Page number is 1 or more */
|
||||
pCache = pPg->pCache;
|
||||
assert( pCache!=0 ); /* Every page has an associated PCache */
|
||||
if( pPg->flags & PGHDR_CLEAN ){
|
||||
assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
|
||||
assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
|
||||
assert( pCache->pDirtyTail!=pPg );
|
||||
}
|
||||
/* WRITEABLE pages must also be DIRTY */
|
||||
if( pPg->flags & PGHDR_WRITEABLE ){
|
||||
assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */
|
||||
}
|
||||
/* NEED_SYNC can be set independently of WRITEABLE. This can happen,
|
||||
** for example, when using the sqlite3PagerDontWrite() optimization:
|
||||
** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK.
|
||||
** (2) Page X moved to freelist, WRITEABLE is cleared
|
||||
** (3) Page X reused, WRITEABLE is set again
|
||||
** If NEED_SYNC had been cleared in step 2, then it would not be reset
|
||||
** in step 3, and page might be written into the database without first
|
||||
** syncing the rollback journal, which might cause corruption on a power
|
||||
** loss.
|
||||
**
|
||||
** Another example is when the database page size is smaller than the
|
||||
** disk sector size. When any page of a sector is journalled, all pages
|
||||
** in that sector are marked NEED_SYNC even if they are still CLEAN, just
|
||||
** in case they are later modified, since all pages in the same sector
|
||||
** must be journalled and synced before any of those pages can be safely
|
||||
** written.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
|
||||
/********************************** Linked List Management ********************/
|
||||
|
||||
/* Allowed values for second argument to pcacheManageDirtyList() */
|
||||
@@ -158,16 +47,17 @@ int sqlite3PcachePageSanity(PgHdr *pPg){
|
||||
static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
PCache *p = pPage->pCache;
|
||||
|
||||
pcacheTrace(("%p.DIRTYLIST.%s %d\n", p,
|
||||
addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT",
|
||||
pPage->pgno));
|
||||
if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
|
||||
assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
|
||||
assert( pPage->pDirtyPrev || pPage==p->pDirty );
|
||||
|
||||
/* Update the PCache1.pSynced variable if necessary. */
|
||||
if( p->pSynced==pPage ){
|
||||
p->pSynced = pPage->pDirtyPrev;
|
||||
PgHdr *pSynced = pPage->pDirtyPrev;
|
||||
while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
|
||||
pSynced = pSynced->pDirtyPrev;
|
||||
}
|
||||
p->pSynced = pSynced;
|
||||
}
|
||||
|
||||
if( pPage->pDirtyNext ){
|
||||
@@ -179,15 +69,10 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
if( pPage->pDirtyPrev ){
|
||||
pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
|
||||
}else{
|
||||
/* If there are now no dirty pages in the cache, set eCreate to 2.
|
||||
** This is an optimization that allows sqlite3PcacheFetch() to skip
|
||||
** searching for a dirty page to eject from the cache when it might
|
||||
** otherwise have to. */
|
||||
assert( pPage==p->pDirty );
|
||||
p->pDirty = pPage->pDirtyNext;
|
||||
assert( p->bPurgeable || p->eCreate==2 );
|
||||
if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
assert( p->bPurgeable==0 || p->eCreate==1 );
|
||||
if( p->pDirty==0 && p->bPurgeable ){
|
||||
assert( p->eCreate==1 );
|
||||
p->eCreate = 2;
|
||||
}
|
||||
}
|
||||
@@ -209,19 +94,10 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
}
|
||||
}
|
||||
p->pDirty = pPage;
|
||||
|
||||
/* If pSynced is NULL and this page has a clear NEED_SYNC flag, set
|
||||
** pSynced to point to it. Checking the NEED_SYNC flag is an
|
||||
** optimization, as if pSynced points to a page with the NEED_SYNC
|
||||
** flag set sqlite3PcacheFetchStress() searches through all newer
|
||||
** entries of the dirty-list for a page with NEED_SYNC clear anyway. */
|
||||
if( !p->pSynced
|
||||
&& 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/
|
||||
){
|
||||
if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
|
||||
p->pSynced = pPage;
|
||||
}
|
||||
}
|
||||
pcacheDump(p);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -230,9 +106,7 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
||||
*/
|
||||
static void pcacheUnpin(PgHdr *p){
|
||||
if( p->pCache->bPurgeable ){
|
||||
pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno));
|
||||
sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0);
|
||||
pcacheDump(p->pCache);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +176,6 @@ int sqlite3PcacheOpen(
|
||||
p->pStress = pStress;
|
||||
p->szCache = 100;
|
||||
p->szSpill = 1;
|
||||
pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable));
|
||||
return sqlite3PcacheSetPageSize(p, szPage);
|
||||
}
|
||||
|
||||
@@ -325,7 +198,6 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
|
||||
}
|
||||
pCache->pCache = pNew;
|
||||
pCache->szPage = szPage;
|
||||
pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage));
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -360,13 +232,11 @@ sqlite3_pcache_page *sqlite3PcacheFetch(
|
||||
int createFlag /* If true, create page if it does not exist already */
|
||||
){
|
||||
int eCreate;
|
||||
sqlite3_pcache_page *pRes;
|
||||
|
||||
assert( pCache!=0 );
|
||||
assert( pCache->pCache!=0 );
|
||||
assert( createFlag==3 || createFlag==0 );
|
||||
assert( pgno>0 );
|
||||
assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) );
|
||||
|
||||
/* eCreate defines what to do if the page does not exist.
|
||||
** 0 Do not allocate a new page. (createFlag==0)
|
||||
@@ -379,10 +249,7 @@ sqlite3_pcache_page *sqlite3PcacheFetch(
|
||||
assert( eCreate==0 || eCreate==1 || eCreate==2 );
|
||||
assert( createFlag==0 || pCache->eCreate==eCreate );
|
||||
assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
|
||||
pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
|
||||
createFlag?" create":"",pRes));
|
||||
return pRes;
|
||||
return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -409,11 +276,7 @@ int sqlite3PcacheFetchStress(
|
||||
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
|
||||
** cleared), but if that is not possible settle for any other
|
||||
** unreferenced dirty page.
|
||||
**
|
||||
** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC
|
||||
** flag is currently referenced, then the following may leave pSynced
|
||||
** set incorrectly (pointing to other than the LRU page with NEED_SYNC
|
||||
** cleared). This is Ok, as pSynced is just an optimization. */
|
||||
*/
|
||||
for(pPg=pCache->pSynced;
|
||||
pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
|
||||
pPg=pPg->pDirtyPrev
|
||||
@@ -431,9 +294,7 @@ int sqlite3PcacheFetchStress(
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
numberOfCachePages(pCache));
|
||||
#endif
|
||||
pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
|
||||
rc = pCache->xStress(pCache->pStress, pPg);
|
||||
pcacheDump(pCache);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
return rc;
|
||||
}
|
||||
@@ -493,7 +354,6 @@ PgHdr *sqlite3PcacheFetchFinish(
|
||||
}
|
||||
pCache->nRefSum++;
|
||||
pPgHdr->nRef++;
|
||||
assert( sqlite3PcachePageSanity(pPgHdr) );
|
||||
return pPgHdr;
|
||||
}
|
||||
|
||||
@@ -507,11 +367,8 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
|
||||
if( (--p->nRef)==0 ){
|
||||
if( p->flags&PGHDR_CLEAN ){
|
||||
pcacheUnpin(p);
|
||||
}else if( p->pDirtyPrev!=0 ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
/* Move the page to the head of the dirty list. If p->pDirtyPrev==0,
|
||||
** then page p is already at the head of the dirty list and the
|
||||
** following call would be a no-op. Hence the OPTIMIZATION-IF-FALSE
|
||||
** tag above. */
|
||||
}else if( p->pDirtyPrev!=0 ){
|
||||
/* Move the page to the head of the dirty list. */
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
||||
}
|
||||
}
|
||||
@@ -522,7 +379,6 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheRef(PgHdr *p){
|
||||
assert(p->nRef>0);
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
p->nRef++;
|
||||
p->pCache->nRefSum++;
|
||||
}
|
||||
@@ -534,7 +390,6 @@ void sqlite3PcacheRef(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheDrop(PgHdr *p){
|
||||
assert( p->nRef==1 );
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( p->flags&PGHDR_DIRTY ){
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
||||
}
|
||||
@@ -548,16 +403,13 @@ void sqlite3PcacheDrop(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
assert( p->nRef>0 );
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){
|
||||
p->flags &= ~PGHDR_DONT_WRITE;
|
||||
if( p->flags & PGHDR_CLEAN ){
|
||||
p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN);
|
||||
pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
|
||||
assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
|
||||
}
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,14 +418,11 @@ void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
** make it so.
|
||||
*/
|
||||
void sqlite3PcacheMakeClean(PgHdr *p){
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( ALWAYS((p->flags & PGHDR_DIRTY)!=0) ){
|
||||
if( (p->flags & PGHDR_DIRTY) ){
|
||||
assert( (p->flags & PGHDR_CLEAN)==0 );
|
||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
||||
p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
||||
p->flags |= PGHDR_CLEAN;
|
||||
pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
if( p->nRef==0 ){
|
||||
pcacheUnpin(p);
|
||||
}
|
||||
@@ -585,7 +434,6 @@ void sqlite3PcacheMakeClean(PgHdr *p){
|
||||
*/
|
||||
void sqlite3PcacheCleanAll(PCache *pCache){
|
||||
PgHdr *p;
|
||||
pcacheTrace(("%p.CLEAN-ALL\n",pCache));
|
||||
while( (p = pCache->pDirty)!=0 ){
|
||||
sqlite3PcacheMakeClean(p);
|
||||
}
|
||||
@@ -596,7 +444,6 @@ void sqlite3PcacheCleanAll(PCache *pCache){
|
||||
*/
|
||||
void sqlite3PcacheClearWritable(PCache *pCache){
|
||||
PgHdr *p;
|
||||
pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache));
|
||||
for(p=pCache->pDirty; p; p=p->pDirtyNext){
|
||||
p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
||||
}
|
||||
@@ -621,8 +468,6 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
|
||||
PCache *pCache = p->pCache;
|
||||
assert( p->nRef>0 );
|
||||
assert( newPgno>0 );
|
||||
assert( sqlite3PcachePageSanity(p) );
|
||||
pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
|
||||
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
|
||||
p->pgno = newPgno;
|
||||
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
|
||||
@@ -643,7 +488,6 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
||||
if( pCache->pCache ){
|
||||
PgHdr *p;
|
||||
PgHdr *pNext;
|
||||
pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno));
|
||||
for(p=pCache->pDirty; p; p=pNext){
|
||||
pNext = p->pDirtyNext;
|
||||
/* This routine never gets call with a positive pgno except right
|
||||
@@ -674,7 +518,6 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
||||
*/
|
||||
void sqlite3PcacheClose(PCache *pCache){
|
||||
assert( pCache->pCache!=0 );
|
||||
pcacheTrace(("%p.CLOSE\n",pCache));
|
||||
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
|
||||
}
|
||||
|
||||
@@ -687,31 +530,29 @@ void sqlite3PcacheClear(PCache *pCache){
|
||||
|
||||
/*
|
||||
** Merge two lists of pages connected by pDirty and in pgno order.
|
||||
** Do not bother fixing the pDirtyPrev pointers.
|
||||
** Do not both fixing the pDirtyPrev pointers.
|
||||
*/
|
||||
static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
|
||||
PgHdr result, *pTail;
|
||||
pTail = &result;
|
||||
assert( pA!=0 && pB!=0 );
|
||||
for(;;){
|
||||
while( pA && pB ){
|
||||
if( pA->pgno<pB->pgno ){
|
||||
pTail->pDirty = pA;
|
||||
pTail = pA;
|
||||
pA = pA->pDirty;
|
||||
if( pA==0 ){
|
||||
pTail->pDirty = pB;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
pTail->pDirty = pB;
|
||||
pTail = pB;
|
||||
pB = pB->pDirty;
|
||||
if( pB==0 ){
|
||||
pTail->pDirty = pA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pA ){
|
||||
pTail->pDirty = pA;
|
||||
}else if( pB ){
|
||||
pTail->pDirty = pB;
|
||||
}else{
|
||||
pTail->pDirty = 0;
|
||||
}
|
||||
return result.pDirty;
|
||||
}
|
||||
|
||||
@@ -752,8 +593,7 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
|
||||
}
|
||||
p = a[0];
|
||||
for(i=1; i<N_SORT_BUCKET; i++){
|
||||
if( a[i]==0 ) continue;
|
||||
p = p ? pcacheMergeDirtyList(p, a[i]) : a[i];
|
||||
p = pcacheMergeDirtyList(p, a[i]);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
+5
-9
@@ -26,7 +26,7 @@ struct PgHdr {
|
||||
sqlite3_pcache_page *pPage; /* Pcache object page handle */
|
||||
void *pData; /* Page data */
|
||||
void *pExtra; /* Extra content */
|
||||
PgHdr *pDirty; /* Transient list of dirty sorted by pgno */
|
||||
PgHdr *pDirty; /* Transient list of dirty pages */
|
||||
Pager *pPager; /* The pager this page is part of */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
#ifdef SQLITE_CHECK_PAGES
|
||||
@@ -51,10 +51,11 @@ struct PgHdr {
|
||||
#define PGHDR_WRITEABLE 0x004 /* Journaled and ready to modify */
|
||||
#define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before
|
||||
** writing this page to the database */
|
||||
#define PGHDR_DONT_WRITE 0x010 /* Do not write content to disk */
|
||||
#define PGHDR_MMAP 0x020 /* This is an mmap page object */
|
||||
#define PGHDR_NEED_READ 0x010 /* Content is unread */
|
||||
#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */
|
||||
#define PGHDR_MMAP 0x040 /* This is an mmap page object */
|
||||
|
||||
#define PGHDR_WAL_APPEND 0x040 /* Appended to wal file */
|
||||
#define PGHDR_WAL_APPEND 0x080 /* Appended to wal file */
|
||||
|
||||
/* Initialize and shutdown the page cache subsystem */
|
||||
int sqlite3PcacheInitialize(void);
|
||||
@@ -137,11 +138,6 @@ int sqlite3PcachePagecount(PCache*);
|
||||
void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
/* Check invariants on a PgHdr object */
|
||||
int sqlite3PcachePageSanity(PgHdr*);
|
||||
#endif
|
||||
|
||||
/* Set and get the suggested cache-size for the specified pager-cache.
|
||||
**
|
||||
** If no global maximum is configured, then the system attempts to limit
|
||||
|
||||
+4
-2
@@ -1030,7 +1030,7 @@ void sqlite3Pragma(
|
||||
** compiler (eg. count_changes). So add an opcode to expire all
|
||||
** compiled SQL statements after modifying a pragma value.
|
||||
*/
|
||||
sqlite3VdbeAddOp0(v, OP_Expire);
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
|
||||
setAllPagerFlags(db);
|
||||
}
|
||||
break;
|
||||
@@ -1334,10 +1334,12 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
|
||||
sqlite3ColumnDefault(v, pTab, iKey, regRow);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow,
|
||||
sqlite3VdbeCurrentAddr(v)+3); VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, i, 0, regRow); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); VdbeCoverage(v);
|
||||
sqlite3VdbeGoto(v, addrOk);
|
||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
||||
}else{
|
||||
|
||||
+18
-18
@@ -15,26 +15,26 @@
|
||||
** Conversion types fall into various categories as defined by the
|
||||
** following enumeration.
|
||||
*/
|
||||
#define etRADIX 0 /* Integer types. %d, %x, %o, and so forth */
|
||||
#define etFLOAT 1 /* Floating point. %f */
|
||||
#define etEXP 2 /* Exponentional notation. %e and %E */
|
||||
#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */
|
||||
#define etSIZE 4 /* Return number of characters processed so far. %n */
|
||||
#define etSTRING 5 /* Strings. %s */
|
||||
#define etDYNSTRING 6 /* Dynamically allocated strings. %z */
|
||||
#define etPERCENT 7 /* Percent symbol. %% */
|
||||
#define etCHARX 8 /* Characters. %c */
|
||||
#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
|
||||
#define etFLOAT 2 /* Floating point. %f */
|
||||
#define etEXP 3 /* Exponentional notation. %e and %E */
|
||||
#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
|
||||
#define etSIZE 5 /* Return number of characters processed so far. %n */
|
||||
#define etSTRING 6 /* Strings. %s */
|
||||
#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
|
||||
#define etPERCENT 8 /* Percent symbol. %% */
|
||||
#define etCHARX 9 /* Characters. %c */
|
||||
/* The rest are extensions, not normally found in printf() */
|
||||
#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */
|
||||
#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
|
||||
#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */
|
||||
#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '',
|
||||
NULL pointers replaced by SQL NULL. %Q */
|
||||
#define etTOKEN 11 /* a pointer to a Token structure */
|
||||
#define etSRCLIST 12 /* a pointer to a SrcList */
|
||||
#define etPOINTER 13 /* The %p conversion */
|
||||
#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
|
||||
#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
|
||||
#define etTOKEN 12 /* a pointer to a Token structure */
|
||||
#define etSRCLIST 13 /* a pointer to a SrcList */
|
||||
#define etPOINTER 14 /* The %p conversion */
|
||||
#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */
|
||||
#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
|
||||
|
||||
#define etINVALID 16 /* Any unrecognized conversion type */
|
||||
#define etINVALID 0 /* Any unrecognized conversion type */
|
||||
|
||||
|
||||
/*
|
||||
@@ -189,7 +189,7 @@ void sqlite3VXPrintf(
|
||||
etByte flag_long; /* True if "l" flag is present */
|
||||
etByte flag_longlong; /* True if the "ll" flag is present */
|
||||
etByte done; /* Loop termination flag */
|
||||
etByte xtype = etINVALID; /* Conversion paradigm */
|
||||
etByte xtype = 0; /* Conversion paradigm */
|
||||
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
|
||||
u8 useIntern; /* Ok to use internal conversions (ex: %T) */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
|
||||
@@ -763,7 +763,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
assert( pNC->nRef>=nRef );
|
||||
if( nRef!=pNC->nRef ){
|
||||
ExprSetProperty(pExpr, EP_VarSelect);
|
||||
pNC->ncFlags |= NC_VarSelect;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+19
-18
@@ -242,26 +242,28 @@ static struct RowSetEntry *rowSetEntryMerge(
|
||||
struct RowSetEntry *pTail;
|
||||
|
||||
pTail = &head;
|
||||
assert( pA!=0 && pB!=0 );
|
||||
for(;;){
|
||||
while( pA && pB ){
|
||||
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
|
||||
assert( pB->pRight==0 || pB->v<=pB->pRight->v );
|
||||
if( pA->v<=pB->v ){
|
||||
if( pA->v<pB->v ) pTail = pTail->pRight = pA;
|
||||
if( pA->v<pB->v ){
|
||||
pTail->pRight = pA;
|
||||
pA = pA->pRight;
|
||||
if( pA==0 ){
|
||||
pTail->pRight = pB;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
pTail = pTail->pRight = pB;
|
||||
pTail = pTail->pRight;
|
||||
}else if( pB->v<pA->v ){
|
||||
pTail->pRight = pB;
|
||||
pB = pB->pRight;
|
||||
if( pB==0 ){
|
||||
pTail->pRight = pA;
|
||||
break;
|
||||
}
|
||||
pTail = pTail->pRight;
|
||||
}else{
|
||||
pA = pA->pRight;
|
||||
}
|
||||
}
|
||||
if( pA ){
|
||||
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
|
||||
pTail->pRight = pA;
|
||||
}else{
|
||||
assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v );
|
||||
pTail->pRight = pB;
|
||||
}
|
||||
return head.pRight;
|
||||
}
|
||||
|
||||
@@ -284,10 +286,9 @@ static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){
|
||||
aBucket[i] = pIn;
|
||||
pIn = pNext;
|
||||
}
|
||||
pIn = aBucket[0];
|
||||
for(i=1; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
|
||||
if( aBucket[i]==0 ) continue;
|
||||
pIn = pIn ? rowSetEntryMerge(pIn, aBucket[i]) : aBucket[i];
|
||||
pIn = 0;
|
||||
for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
|
||||
pIn = rowSetEntryMerge(pIn, aBucket[i]);
|
||||
}
|
||||
return pIn;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ struct SortCtx {
|
||||
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
|
||||
int labelDone; /* Jump here when done, ex: LIMIT reached */
|
||||
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
|
||||
u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
|
||||
};
|
||||
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
|
||||
|
||||
@@ -590,30 +589,9 @@ static void pushOntoSorter(
|
||||
sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
|
||||
if( iLimit ){
|
||||
int addr;
|
||||
int r1 = 0;
|
||||
/* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit
|
||||
** register is initialized with value of LIMIT+OFFSET.) After the sorter
|
||||
** fills up, delete the least entry in the sorter after each insert.
|
||||
** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
|
||||
if( pSort->bOrderedInnerLoop ){
|
||||
r1 = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1);
|
||||
VdbeComment((v, "seq"));
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
|
||||
if( pSort->bOrderedInnerLoop ){
|
||||
/* If the inner loop is driven by an index such that values from
|
||||
** the same iteration of the inner loop are in sorted order, then
|
||||
** immediately jump to the next iteration of an inner loop if the
|
||||
** entry from the current iteration does not fit into the top
|
||||
** LIMIT+OFFSET entries of the sorter. */
|
||||
int iBrk = sqlite3VdbeCurrentAddr(v) + 2;
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, regBase+nExpr, iBrk, r1);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
}
|
||||
@@ -5198,7 +5176,6 @@ int sqlite3Select(
|
||||
}
|
||||
if( sSort.pOrderBy ){
|
||||
sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
|
||||
sSort.bOrderedInnerLoop = sqlite3WhereOrderedInnerLoop(pWInfo);
|
||||
if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
|
||||
sSort.pOrderBy = 0;
|
||||
}
|
||||
|
||||
@@ -966,6 +966,12 @@ struct sqlite3_io_methods {
|
||||
** on whether or not the file has been renamed, moved, or deleted since it
|
||||
** was first opened.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_GET_HANDLE]]
|
||||
** The [SQLITE_FCNTL_WIN32_GET_HANDLE] opcode can be used to obtain the
|
||||
** underlying native file handle associated with a file handle. This file
|
||||
** control interprets its argument as a pointer to a native file handle and
|
||||
** writes the resulting value there.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
|
||||
** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
|
||||
** opcode causes the xFileControl method to swap the file handle with the one
|
||||
@@ -1016,6 +1022,7 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_RBU 26
|
||||
#define SQLITE_FCNTL_VFS_POINTER 27
|
||||
#define SQLITE_FCNTL_JOURNAL_POINTER 28
|
||||
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
|
||||
+13
-17
@@ -411,7 +411,7 @@
|
||||
** be true and false so that the unreachable code they specify will
|
||||
** not be counted as untested code.
|
||||
*/
|
||||
#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
|
||||
#if defined(SQLITE_COVERAGE_TEST)
|
||||
# define ALWAYS(X) (1)
|
||||
# define NEVER(X) (0)
|
||||
#elif !defined(NDEBUG)
|
||||
@@ -2531,20 +2531,18 @@ struct SrcList {
|
||||
#define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */
|
||||
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
|
||||
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
|
||||
#define WHERE_ONEPASS_MULTIROW 0x0008 /* ONEPASS is ok with multiple rows */
|
||||
#define WHERE_DUPLICATES_OK 0x0010 /* Ok to return a row more than once */
|
||||
#define WHERE_OR_SUBCLAUSE 0x0020 /* Processing a sub-WHERE as part of
|
||||
** the OR optimization */
|
||||
#define WHERE_GROUPBY 0x0040 /* pOrderBy is really a GROUP BY */
|
||||
#define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */
|
||||
#define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */
|
||||
#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */
|
||||
#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */
|
||||
#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
|
||||
/* 0x1000 not currently used */
|
||||
/* 0x2000 not currently used */
|
||||
#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
|
||||
/* 0x8000 not currently used */
|
||||
#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
|
||||
#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */
|
||||
#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
|
||||
#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
|
||||
#define WHERE_NO_AUTOINDEX 0x0080 /* Disallow automatic indexes */
|
||||
#define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */
|
||||
#define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */
|
||||
#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
|
||||
#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
|
||||
#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
|
||||
#define WHERE_ONEPASS_MULTIROW 0x2000 /* ONEPASS is ok with multiple rows */
|
||||
#define WHERE_USE_LIMIT 0x4000 /* There is a constant LIMIT clause */
|
||||
|
||||
/* Allowed return values from sqlite3WhereIsDistinct()
|
||||
*/
|
||||
@@ -2599,7 +2597,6 @@ struct NameContext {
|
||||
#define NC_InAggFunc 0x0008 /* True if analyzing arguments to an agg func */
|
||||
#define NC_HasAgg 0x0010 /* One or more aggregate functions seen */
|
||||
#define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */
|
||||
#define NC_VarSelect 0x0040 /* A correlated subquery has been seen */
|
||||
#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
|
||||
|
||||
/*
|
||||
@@ -3637,7 +3634,6 @@ void sqlite3WhereEnd(WhereInfo*);
|
||||
LogEst sqlite3WhereOutputRowCount(WhereInfo*);
|
||||
int sqlite3WhereIsDistinct(WhereInfo*);
|
||||
int sqlite3WhereIsOrdered(WhereInfo*);
|
||||
int sqlite3WhereOrderedInnerLoop(WhereInfo*);
|
||||
int sqlite3WhereIsSorted(WhereInfo*);
|
||||
int sqlite3WhereContinueLabel(WhereInfo*);
|
||||
int sqlite3WhereBreakLabel(WhereInfo*);
|
||||
|
||||
+33
@@ -5482,6 +5482,38 @@ static int file_control_win32_av_retry(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_win32_get_handle DB
|
||||
**
|
||||
** This TCL command runs the sqlite3_file_control interface with
|
||||
** the SQLITE_FCNTL_WIN32_GET_HANDLE opcode.
|
||||
*/
|
||||
static int file_control_win32_get_handle(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
HANDLE hFile = NULL;
|
||||
char z[100];
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " DB", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_WIN32_GET_HANDLE,
|
||||
(void*)&hFile);
|
||||
sqlite3_snprintf(sizeof(z), z, "%d %p", rc, (void*)hFile);
|
||||
Tcl_AppendResult(interp, z, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_win32_set_handle DB HANDLE
|
||||
**
|
||||
@@ -7214,6 +7246,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
|
||||
#if SQLITE_OS_WIN
|
||||
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
|
||||
{ "file_control_win32_get_handle", file_control_win32_get_handle, 0 },
|
||||
{ "file_control_win32_set_handle", file_control_win32_set_handle, 0 },
|
||||
#endif
|
||||
{ "file_control_persist_wal", file_control_persist_wal, 0 },
|
||||
|
||||
+8
-8
@@ -618,27 +618,27 @@ static int btree_insert(
|
||||
){
|
||||
BtCursor *pCur;
|
||||
int rc;
|
||||
BtreePayload x;
|
||||
void *pKey = 0;
|
||||
int nKey = 0;
|
||||
void *pData = 0;
|
||||
int nData = 0;
|
||||
|
||||
if( objc!=4 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "?-intkey? CSR KEY VALUE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
memset(&x, 0, sizeof(x));
|
||||
if( objc==4 ){
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &rc) ) return TCL_ERROR;
|
||||
x.nKey = rc;
|
||||
x.pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &x.nData);
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &nKey) ) return TCL_ERROR;
|
||||
pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &nData);
|
||||
}else{
|
||||
x.pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &rc);
|
||||
x.nKey = rc;
|
||||
pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &nKey);
|
||||
}
|
||||
pCur = (BtCursor*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
|
||||
sqlite3_mutex_enter(pCur->pBtree->db->mutex);
|
||||
sqlite3BtreeEnter(pCur->pBtree);
|
||||
rc = sqlite3BtreeInsert(pCur, &x, 0, 0);
|
||||
rc = sqlite3BtreeInsert(pCur, pKey, nKey, pData, nData, 0, 0, 0);
|
||||
sqlite3BtreeLeave(pCur->pBtree);
|
||||
sqlite3_mutex_leave(pCur->pBtree->db->mutex);
|
||||
|
||||
|
||||
+1
-25
@@ -215,9 +215,7 @@ static int writeListSync(CrashFile *pFile, int isCrash){
|
||||
}
|
||||
|
||||
#ifdef TRACE_CRASHTEST
|
||||
if( pFile ){
|
||||
printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
|
||||
}
|
||||
printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
|
||||
#endif
|
||||
|
||||
ppPtr = &g.pWriteList;
|
||||
@@ -801,27 +799,6 @@ static int processDevSymArgs(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_crash_now
|
||||
**
|
||||
** Simulate a crash immediately. This function does not return
|
||||
** (writeListSync() calls exit(-1)).
|
||||
*/
|
||||
static int crashNowCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
writeListSync(0, 1);
|
||||
assert( 0 );
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite_crash_enable ENABLE
|
||||
**
|
||||
@@ -1057,7 +1034,6 @@ int Sqlitetest6_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
|
||||
|
||||
@@ -445,12 +445,6 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case TK_MATCH: {
|
||||
sqlite3TreeViewLine(pView, "MATCH {%d:%d}%s",
|
||||
pExpr->iTable, pExpr->iColumn, zFlgs);
|
||||
sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
|
||||
break;
|
||||
|
||||
+1
-2
@@ -352,8 +352,7 @@ void sqlite3Update(
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE, iIdxCur
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
|
||||
+42
-86
@@ -2403,7 +2403,6 @@ case OP_Column: {
|
||||
|
||||
/* If the cursor cache is stale, bring it up-to-date */
|
||||
rc = sqlite3VdbeCursorMoveto(&pC, &p2);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
|
||||
pDest = &aMem[pOp->p3];
|
||||
@@ -2417,7 +2416,8 @@ case OP_Column: {
|
||||
assert( pC->eCurType!=CURTYPE_SORTER );
|
||||
pCrsr = pC->uc.pCursor;
|
||||
|
||||
if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( pC->cacheStatus!=p->cacheCtr ){
|
||||
if( pC->nullRow ){
|
||||
if( pC->eCurType==CURTYPE_PSEUDO ){
|
||||
assert( pC->uc.pseudoTableReg>0 );
|
||||
@@ -2464,7 +2464,7 @@ case OP_Column: {
|
||||
aOffset[0] = offset;
|
||||
|
||||
|
||||
if( avail<offset ){ /*OPTIMIZATION-IF-FALSE*/
|
||||
if( avail<offset ){
|
||||
/* pC->aRow does not have to hold the entire row, but it does at least
|
||||
** need to cover the header of the record. If pC->aRow does not contain
|
||||
** the complete header, then set it to zero, forcing the header to be
|
||||
@@ -2485,15 +2485,14 @@ case OP_Column: {
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
}else if( offset>0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
/* The following goto is an optimization. It can be omitted and
|
||||
** everything will still work. But OP_Column is measurably faster
|
||||
** by skipping the subsequent conditional, which is always true.
|
||||
*/
|
||||
zData = pC->aRow;
|
||||
assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */
|
||||
goto op_column_read_header;
|
||||
}
|
||||
|
||||
/* The following goto is an optimization. It can be omitted and
|
||||
** everything will still work. But OP_Column is measurably faster
|
||||
** by skipping the subsequent conditional, which is always true.
|
||||
*/
|
||||
assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */
|
||||
goto op_column_read_header;
|
||||
}
|
||||
|
||||
/* Make sure at least the first p2+1 entries of the header have been
|
||||
@@ -2503,6 +2502,7 @@ case OP_Column: {
|
||||
/* If there is more header available for parsing in the record, try
|
||||
** to extract additional fields up through the p2+1-th field
|
||||
*/
|
||||
op_column_read_header:
|
||||
if( pC->iHdrOffset<aOffset[0] ){
|
||||
/* Make sure zData points to enough of the record to cover the header. */
|
||||
if( pC->aRow==0 ){
|
||||
@@ -2515,11 +2515,11 @@ case OP_Column: {
|
||||
}
|
||||
|
||||
/* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */
|
||||
op_column_read_header:
|
||||
i = pC->nHdrParsed;
|
||||
offset64 = aOffset[i];
|
||||
zHdr = zData + pC->iHdrOffset;
|
||||
zEndHdr = zData + aOffset[0];
|
||||
assert( i<=p2 && zHdr<zEndHdr );
|
||||
do{
|
||||
if( (t = zHdr[0])<0x80 ){
|
||||
zHdr++;
|
||||
@@ -2531,7 +2531,9 @@ case OP_Column: {
|
||||
pC->aType[i++] = t;
|
||||
aOffset[i] = (u32)(offset64 & 0xffffffff);
|
||||
}while( i<=p2 && zHdr<zEndHdr );
|
||||
|
||||
pC->nHdrParsed = i;
|
||||
pC->iHdrOffset = (u32)(zHdr - zData);
|
||||
|
||||
/* The record is corrupt if any of the following are true:
|
||||
** (1) the bytes of the header extend past the declared header size
|
||||
** (2) the entire header was used but not all data was used
|
||||
@@ -2544,10 +2546,8 @@ case OP_Column: {
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
|
||||
pC->nHdrParsed = i;
|
||||
pC->iHdrOffset = (u32)(zHdr - zData);
|
||||
if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
|
||||
|
||||
}else{
|
||||
t = 0;
|
||||
}
|
||||
@@ -2575,10 +2575,9 @@ case OP_Column: {
|
||||
assert( p2<pC->nHdrParsed );
|
||||
assert( rc==SQLITE_OK );
|
||||
assert( sqlite3VdbeCheckMemInvariants(pDest) );
|
||||
if( VdbeMemDynamic(pDest) ){
|
||||
sqlite3VdbeMemSetNull(pDest);
|
||||
}
|
||||
if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest);
|
||||
assert( t==pC->aType[p2] );
|
||||
pDest->enc = encoding;
|
||||
if( pC->szRow>=aOffset[p2+1] ){
|
||||
/* This is the common case where the desired content fits on the original
|
||||
** page - where the content is not on an overflow page */
|
||||
@@ -2592,7 +2591,6 @@ case OP_Column: {
|
||||
*/
|
||||
static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term };
|
||||
pDest->n = len = (t-12)/2;
|
||||
pDest->enc = encoding;
|
||||
if( pDest->szMalloc < len+2 ){
|
||||
pDest->flags = MEM_Null;
|
||||
if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem;
|
||||
@@ -2605,7 +2603,6 @@ case OP_Column: {
|
||||
pDest->flags = aFlag[t&1];
|
||||
}
|
||||
}else{
|
||||
pDest->enc = encoding;
|
||||
/* This branch happens only when content is on overflow pages */
|
||||
if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
|
||||
&& ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0))
|
||||
@@ -4023,30 +4020,6 @@ case OP_Found: { /* jump, in3 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SeekRowid P1 P2 P3 * *
|
||||
** Synopsis: intkey=r[P3]
|
||||
**
|
||||
** P1 is the index of a cursor open on an SQL table btree (with integer
|
||||
** keys). If register P3 does not contain an integer or if P1 does not
|
||||
** contain a record with rowid P3 then jump immediately to P2.
|
||||
** Or, if P2 is 0, raise an SQLITE_CORRUPT error. If P1 does contain
|
||||
** a record with rowid P3 then
|
||||
** leave the cursor pointing at that record and fall through to the next
|
||||
** instruction.
|
||||
**
|
||||
** The OP_NotExists opcode performs the same operation, but with OP_NotExists
|
||||
** the P3 register must be guaranteed to contain an integer value. With this
|
||||
** opcode, register P3 might not contain an integer.
|
||||
**
|
||||
** The OP_NotFound opcode performs the same operation on index btrees
|
||||
** (with arbitrary multi-value keys).
|
||||
**
|
||||
** This opcode leaves the cursor in a state where it cannot be advanced
|
||||
** in either direction. In other words, the Next and Prev opcodes will
|
||||
** not work following this opcode.
|
||||
**
|
||||
** See also: Found, NotFound, NoConflict, SeekRowid
|
||||
*/
|
||||
/* Opcode: NotExists P1 P2 P3 * *
|
||||
** Synopsis: intkey=r[P3]
|
||||
**
|
||||
@@ -4057,10 +4030,6 @@ case OP_Found: { /* jump, in3 */
|
||||
** leave the cursor pointing at that record and fall through to the next
|
||||
** instruction.
|
||||
**
|
||||
** The OP_SeekRowid opcode performs the same operation but also allows the
|
||||
** P3 register to contain a non-integer value, in which case the jump is
|
||||
** always taken. This opcode requires that P3 always contain an integer.
|
||||
**
|
||||
** The OP_NotFound opcode performs the same operation on index btrees
|
||||
** (with arbitrary multi-value keys).
|
||||
**
|
||||
@@ -4068,21 +4037,14 @@ case OP_Found: { /* jump, in3 */
|
||||
** in either direction. In other words, the Next and Prev opcodes will
|
||||
** not work following this opcode.
|
||||
**
|
||||
** See also: Found, NotFound, NoConflict, SeekRowid
|
||||
** See also: Found, NotFound, NoConflict
|
||||
*/
|
||||
case OP_SeekRowid: { /* jump, in3 */
|
||||
case OP_NotExists: { /* jump, in3 */
|
||||
VdbeCursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
int res;
|
||||
u64 iKey;
|
||||
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
if( (pIn3->flags & MEM_Int)==0 ){
|
||||
applyAffinity(pIn3, SQLITE_AFF_NUMERIC, encoding);
|
||||
if( (pIn3->flags & MEM_Int)==0 ) goto jump_to_p2;
|
||||
}
|
||||
/* Fall through into OP_NotExists */
|
||||
case OP_NotExists: /* jump, in3 */
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
assert( pIn3->flags & MEM_Int );
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
@@ -4285,12 +4247,10 @@ case OP_NewRowid: { /* out2 */
|
||||
** sqlite3_last_insert_rowid() function (otherwise it is unmodified).
|
||||
**
|
||||
** If the OPFLAG_USESEEKRESULT flag of P5 is set and if the result of
|
||||
** the last seek operation (OP_NotExists or OP_SeekRowid) was a success,
|
||||
** then this
|
||||
** the last seek operation (OP_NotExists) was a success, then this
|
||||
** operation will not attempt to find the appropriate row before doing
|
||||
** the insert but will instead overwrite the row that the cursor is
|
||||
** currently pointing to. Presumably, the prior OP_NotExists or
|
||||
** OP_SeekRowid opcode
|
||||
** currently pointing to. Presumably, the prior OP_NotExists opcode
|
||||
** has already positioned the cursor correctly. This is an optimization
|
||||
** that boosts performance by avoiding redundant seeks.
|
||||
**
|
||||
@@ -4322,12 +4282,13 @@ case OP_Insert:
|
||||
case OP_InsertInt: {
|
||||
Mem *pData; /* MEM cell holding data for the record to be inserted */
|
||||
Mem *pKey; /* MEM cell holding key for the record */
|
||||
i64 iKey; /* The integer ROWID or key for the record to be inserted */
|
||||
VdbeCursor *pC; /* Cursor to table into which insert is written */
|
||||
int nZero; /* Number of zero-bytes to append */
|
||||
int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */
|
||||
const char *zDb; /* database name - used by the update hook */
|
||||
Table *pTab; /* Table structure - used by update and pre-update hooks */
|
||||
int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
|
||||
BtreePayload x; /* Payload to be inserted */
|
||||
|
||||
op = 0;
|
||||
pData = &aMem[pOp->p2];
|
||||
@@ -4346,10 +4307,10 @@ case OP_InsertInt: {
|
||||
assert( pKey->flags & MEM_Int );
|
||||
assert( memIsValid(pKey) );
|
||||
REGISTER_TRACE(pOp->p3, pKey);
|
||||
x.nKey = pKey->u.i;
|
||||
iKey = pKey->u.i;
|
||||
}else{
|
||||
assert( pOp->opcode==OP_InsertInt );
|
||||
x.nKey = pOp->p3;
|
||||
iKey = pOp->p3;
|
||||
}
|
||||
|
||||
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
|
||||
@@ -4370,28 +4331,26 @@ case OP_InsertInt: {
|
||||
&& pOp->p4type==P4_TABLE
|
||||
&& !(pOp->p5 & OPFLAG_ISUPDATE)
|
||||
){
|
||||
sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey, pOp->p2);
|
||||
sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, iKey, pOp->p2);
|
||||
}
|
||||
#endif
|
||||
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = x.nKey;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = iKey;
|
||||
if( pData->flags & MEM_Null ){
|
||||
x.pData = 0;
|
||||
x.nData = 0;
|
||||
pData->z = 0;
|
||||
pData->n = 0;
|
||||
}else{
|
||||
assert( pData->flags & (MEM_Blob|MEM_Str) );
|
||||
x.pData = pData->z;
|
||||
x.nData = pData->n;
|
||||
}
|
||||
seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
|
||||
if( pData->flags & MEM_Zero ){
|
||||
x.nZero = pData->u.nZero;
|
||||
nZero = pData->u.nZero;
|
||||
}else{
|
||||
x.nZero = 0;
|
||||
nZero = 0;
|
||||
}
|
||||
x.pKey = 0;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, 0, iKey,
|
||||
pData->z, pData->n, nZero,
|
||||
(pOp->p5 & OPFLAG_APPEND)!=0, seekResult
|
||||
);
|
||||
pC->deferredMoveto = 0;
|
||||
@@ -4400,7 +4359,7 @@ case OP_InsertInt: {
|
||||
/* Invoke the update-hook if required. */
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( db->xUpdateCallback && op ){
|
||||
db->xUpdateCallback(db->pUpdateArg, op, zDb, pTab->zName, x.nKey);
|
||||
db->xUpdateCallback(db->pUpdateArg, op, zDb, pTab->zName, iKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4648,9 +4607,8 @@ case OP_RowData: {
|
||||
pCrsr = pC->uc.pCursor;
|
||||
|
||||
/* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or
|
||||
** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
|
||||
** that might invalidate the cursor.
|
||||
** If this where not the case, on of the following assert()s
|
||||
** OP_Rewind/Op_Next with no intervening instructions that might invalidate
|
||||
** the cursor. If this where not the case, on of the following assert()s
|
||||
** would fail. Should this ever change (because of changes in the code
|
||||
** generator) then the fix would be to insert a call to
|
||||
** sqlite3VdbeCursorMoveto().
|
||||
@@ -5016,7 +4974,8 @@ next_tail:
|
||||
case OP_SorterInsert: /* in2 */
|
||||
case OP_IdxInsert: { /* in2 */
|
||||
VdbeCursor *pC;
|
||||
BtreePayload x;
|
||||
int nKey;
|
||||
const char *zKey;
|
||||
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
@@ -5032,12 +4991,9 @@ case OP_IdxInsert: { /* in2 */
|
||||
if( pOp->opcode==OP_SorterInsert ){
|
||||
rc = sqlite3VdbeSorterWrite(pC, pIn2);
|
||||
}else{
|
||||
x.nKey = pIn2->n;
|
||||
x.pKey = pIn2->z;
|
||||
x.nData = 0;
|
||||
x.nZero = 0;
|
||||
x.pData = 0;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, pOp->p3,
|
||||
nKey = pIn2->n;
|
||||
zKey = pIn2->z;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, zKey, nKey, "", 0, 0, pOp->p3,
|
||||
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||
);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
|
||||
+6
-12
@@ -791,7 +791,7 @@ void sqlite3VdbeJumpHere(Vdbe *p, int addr){
|
||||
** the FuncDef is not ephermal, then do nothing.
|
||||
*/
|
||||
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
|
||||
if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
|
||||
if( ALWAYS(pDef) && (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
|
||||
sqlite3DbFree(db, pDef);
|
||||
}
|
||||
}
|
||||
@@ -801,20 +801,12 @@ static void vdbeFreeOpArray(sqlite3 *, Op *, int);
|
||||
/*
|
||||
** Delete a P4 value if necessary.
|
||||
*/
|
||||
static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){
|
||||
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){
|
||||
freeEphemeralFunction(db, p->pFunc);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
static void freeP4(sqlite3 *db, int p4type, void *p4){
|
||||
assert( db );
|
||||
switch( p4type ){
|
||||
case P4_FUNCCTX: {
|
||||
freeP4FuncCtx(db, (sqlite3_context*)p4);
|
||||
break;
|
||||
freeEphemeralFunction(db, ((sqlite3_context*)p4)->pFunc);
|
||||
/* Fall through into the next case */
|
||||
}
|
||||
case P4_REAL:
|
||||
case P4_INT64:
|
||||
@@ -845,7 +837,9 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
|
||||
if( db->pnBytesFreed==0 ){
|
||||
sqlite3ValueFree((sqlite3_value*)p4);
|
||||
}else{
|
||||
freeP4Mem(db, (Mem*)p4);
|
||||
Mem *p = (Mem*)p4;
|
||||
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+9
-16
@@ -1342,18 +1342,19 @@ static int vdbeSortAllocUnpacked(SortSubtask *pTask){
|
||||
|
||||
/*
|
||||
** Merge the two sorted lists p1 and p2 into a single list.
|
||||
** Set *ppOut to the head of the new list.
|
||||
*/
|
||||
static SorterRecord *vdbeSorterMerge(
|
||||
static void vdbeSorterMerge(
|
||||
SortSubtask *pTask, /* Calling thread context */
|
||||
SorterRecord *p1, /* First list to merge */
|
||||
SorterRecord *p2 /* Second list to merge */
|
||||
SorterRecord *p2, /* Second list to merge */
|
||||
SorterRecord **ppOut /* OUT: Head of merged list */
|
||||
){
|
||||
SorterRecord *pFinal = 0;
|
||||
SorterRecord **pp = &pFinal;
|
||||
int bCached = 0;
|
||||
|
||||
assert( p1!=0 && p2!=0 );
|
||||
for(;;){
|
||||
while( p1 && p2 ){
|
||||
int res;
|
||||
res = pTask->xCompare(
|
||||
pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal
|
||||
@@ -1363,22 +1364,15 @@ static SorterRecord *vdbeSorterMerge(
|
||||
*pp = p1;
|
||||
pp = &p1->u.pNext;
|
||||
p1 = p1->u.pNext;
|
||||
if( p1==0 ){
|
||||
*pp = p2;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
*pp = p2;
|
||||
pp = &p2->u.pNext;
|
||||
p2 = p2->u.pNext;
|
||||
bCached = 0;
|
||||
if( p2==0 ){
|
||||
*pp = p1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pFinal;
|
||||
*pp = p1 ? p1 : p2;
|
||||
*ppOut = pFinal;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1431,7 +1425,7 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
|
||||
|
||||
p->u.pNext = 0;
|
||||
for(i=0; aSlot[i]; i++){
|
||||
p = vdbeSorterMerge(pTask, p, aSlot[i]);
|
||||
vdbeSorterMerge(pTask, p, aSlot[i], &p);
|
||||
aSlot[i] = 0;
|
||||
}
|
||||
aSlot[i] = p;
|
||||
@@ -1440,8 +1434,7 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
|
||||
|
||||
p = 0;
|
||||
for(i=0; i<64; i++){
|
||||
if( aSlot[i]==0 ) continue;
|
||||
p = p ? vdbeSorterMerge(pTask, p, aSlot[i]) : aSlot[i];
|
||||
vdbeSorterMerge(pTask, p, aSlot[i], &p);
|
||||
}
|
||||
pList->pList = p;
|
||||
|
||||
|
||||
+1
-1
@@ -418,7 +418,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
|
||||
sqlite3VdbeAddOp0(v, OP_Expire);
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
|
||||
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
|
||||
|
||||
|
||||
@@ -3109,21 +3109,16 @@ int sqlite3WalFrames(
|
||||
** past the sector boundary is written after the sync.
|
||||
*/
|
||||
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
|
||||
int bSync = 1;
|
||||
if( pWal->padToSectorBoundary ){
|
||||
int sectorSize = sqlite3SectorSize(pWal->pWalFd);
|
||||
w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
|
||||
bSync = (w.iSyncPoint==iOffset);
|
||||
testcase( bSync );
|
||||
while( iOffset<w.iSyncPoint ){
|
||||
rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
|
||||
if( rc ) return rc;
|
||||
iOffset += szFrame;
|
||||
nExtra++;
|
||||
}
|
||||
}
|
||||
if( bSync ){
|
||||
assert( rc==SQLITE_OK );
|
||||
}else{
|
||||
rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
+37
-91
@@ -51,18 +51,6 @@ int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
|
||||
return pWInfo->nOBSat;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the innermost loop of the WHERE clause implementation
|
||||
** returns rows in ORDER BY order for complete run of the inner loop.
|
||||
**
|
||||
** Across multiple iterations of outer loops, the output rows need not be
|
||||
** sorted. As long as rows are sorted for just the innermost loop, this
|
||||
** routine can return TRUE.
|
||||
*/
|
||||
int sqlite3WhereOrderedInnerLoop(WhereInfo *pWInfo){
|
||||
return pWInfo->bOrderedInnerLoop;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the VDBE address or label to jump to in order to continue
|
||||
** immediately with the next row of a WHERE clause.
|
||||
@@ -273,10 +261,7 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
|
||||
**
|
||||
** The scanner will be searching the WHERE clause pWC. It will look
|
||||
** for terms of the form "X <op> <expr>" where X is column iColumn of table
|
||||
** iCur. Or if pIdx!=0 then X is column iColumn of index pIdx. pIdx
|
||||
** must be one of the indexes of table iCur.
|
||||
**
|
||||
** The <op> must be one of the operators described by opMask.
|
||||
** iCur. The <op> must be one of the operators described by opMask.
|
||||
**
|
||||
** If the search is for X and the WHERE clause contains terms of the
|
||||
** form X=Y then this routine might also return terms of the form
|
||||
@@ -324,12 +309,11 @@ static WhereTerm *whereScanInit(
|
||||
|
||||
/*
|
||||
** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
|
||||
** where X is a reference to the iColumn of table iCur or of index pIdx
|
||||
** if pIdx!=0 and <op> is one of the WO_xx operator codes specified by
|
||||
** the op parameter. Return a pointer to the term. Return 0 if not found.
|
||||
** where X is a reference to the iColumn of table iCur and <op> is one of
|
||||
** the WO_xx operator codes specified by the op parameter.
|
||||
** Return a pointer to the term. Return 0 if not found.
|
||||
**
|
||||
** If pIdx!=0 then it must be one of the indexes of table iCur.
|
||||
** Search for terms matching the iColumn-th column of pIdx
|
||||
** If pIdx!=0 then search for terms matching the iColumn-th column of pIdx
|
||||
** rather than the iColumn-th column of table iCur.
|
||||
**
|
||||
** The term returned might by Y=<expr> if there is another constraint in
|
||||
@@ -1648,41 +1632,19 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){
|
||||
sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm);
|
||||
}else{
|
||||
char zType[4];
|
||||
char zLeft[50];
|
||||
memcpy(zType, "...", 4);
|
||||
if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V';
|
||||
if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E';
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L';
|
||||
if( pTerm->eOperator & WO_SINGLE ){
|
||||
sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}",
|
||||
pTerm->leftCursor, pTerm->u.leftColumn);
|
||||
}else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){
|
||||
sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld",
|
||||
pTerm->u.pOrInfo->indexable);
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
|
||||
}
|
||||
sqlite3DebugPrintf(
|
||||
"TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x\n",
|
||||
iTerm, pTerm, zType, zLeft, pTerm->truthProb,
|
||||
"TERM-%-3d %p %s cursor=%-3d prob=%-3d op=0x%03x wtFlags=0x%04x\n",
|
||||
iTerm, pTerm, zType, pTerm->leftCursor, pTerm->truthProb,
|
||||
pTerm->eOperator, pTerm->wtFlags);
|
||||
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WHERETRACE_ENABLED
|
||||
/*
|
||||
** Show the complete content of a WhereClause
|
||||
*/
|
||||
void sqlite3WhereClausePrint(WhereClause *pWC){
|
||||
int i;
|
||||
for(i=0; i<pWC->nTerm; i++){
|
||||
whereTermPrint(&pWC->a[i], i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WHERETRACE_ENABLED
|
||||
/*
|
||||
** Print a WhereLoop object for debugging purposes
|
||||
@@ -2669,7 +2631,7 @@ static int whereLoopAddBtree(
|
||||
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
/* Automatic indexes */
|
||||
if( !pBuilder->pOrSet /* Not part of an OR optimization */
|
||||
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
|
||||
&& (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0
|
||||
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
|
||||
&& pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
|
||||
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
|
||||
@@ -3146,7 +3108,9 @@ static int whereLoopAddOr(
|
||||
WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n",
|
||||
(int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm));
|
||||
if( sqlite3WhereTrace & 0x400 ){
|
||||
sqlite3WhereClausePrint(sSubBuild.pWC);
|
||||
for(i=0; i<sSubBuild.pWC->nTerm; i++){
|
||||
whereTermPrint(&sSubBuild.pWC->a[i], i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
@@ -3282,7 +3246,7 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
WhereInfo *pWInfo, /* The WHERE clause */
|
||||
ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */
|
||||
WherePath *pPath, /* The WherePath to check */
|
||||
u16 wctrlFlags, /* WHERE_GROUPBY or _DISTINCTBY or _ORDERBY_LIMIT */
|
||||
u16 wctrlFlags, /* Might contain WHERE_GROUPBY or WHERE_DISTINCTBY */
|
||||
u16 nLoop, /* Number of entries in pPath->aLoop[] */
|
||||
WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */
|
||||
Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */
|
||||
@@ -3293,7 +3257,6 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */
|
||||
u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */
|
||||
u8 isMatch; /* iColumn matches a term of the ORDER BY clause */
|
||||
u16 eqOpMask; /* Allowed equality operators */
|
||||
u16 nKeyCol; /* Number of key columns in pIndex */
|
||||
u16 nColumn; /* Total number of ordered columns in the index */
|
||||
u16 nOrderBy; /* Number terms in the ORDER BY clause */
|
||||
@@ -3344,16 +3307,9 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
obDone = MASKBIT(nOrderBy)-1;
|
||||
orderDistinctMask = 0;
|
||||
ready = 0;
|
||||
eqOpMask = WO_EQ | WO_IS | WO_ISNULL;
|
||||
if( wctrlFlags & WHERE_ORDERBY_LIMIT ) eqOpMask |= WO_IN;
|
||||
for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
|
||||
if( iLoop>0 ) ready |= pLoop->maskSelf;
|
||||
if( iLoop<nLoop ){
|
||||
pLoop = pPath->aLoop[iLoop];
|
||||
if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue;
|
||||
}else{
|
||||
pLoop = pLast;
|
||||
}
|
||||
pLoop = iLoop<nLoop ? pPath->aLoop[iLoop] : pLast;
|
||||
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
|
||||
if( pLoop->u.vtab.isOrdered ) obSat = obDone;
|
||||
break;
|
||||
@@ -3371,7 +3327,7 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
if( pOBExpr->op!=TK_COLUMN ) continue;
|
||||
if( pOBExpr->iTable!=iCur ) continue;
|
||||
pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
|
||||
~ready, eqOpMask, 0);
|
||||
~ready, WO_EQ|WO_ISNULL|WO_IS, 0);
|
||||
if( pTerm==0 ) continue;
|
||||
if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){
|
||||
const char *z1, *z2;
|
||||
@@ -3411,12 +3367,10 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
for(j=0; j<nColumn; j++){
|
||||
u8 bOnce; /* True to run the ORDER BY search loop */
|
||||
|
||||
/* Skip over == and IS and ISNULL terms.
|
||||
** (Also skip IN terms when doing WHERE_ORDERBY_LIMIT processing)
|
||||
*/
|
||||
/* Skip over == and IS NULL terms */
|
||||
if( j<pLoop->u.btree.nEq
|
||||
&& pLoop->nSkip==0
|
||||
&& ((i = pLoop->aLTerm[j]->eOperator) & eqOpMask)!=0
|
||||
&& ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL|WO_IS))!=0
|
||||
){
|
||||
if( i & WO_ISNULL ){
|
||||
testcase( isOrderDistinct );
|
||||
@@ -3940,19 +3894,8 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
}
|
||||
}else{
|
||||
pWInfo->nOBSat = pFrom->isOrdered;
|
||||
if( pWInfo->nOBSat<0 ) pWInfo->nOBSat = 0;
|
||||
pWInfo->revMask = pFrom->revLoop;
|
||||
if( pWInfo->nOBSat<=0 ){
|
||||
pWInfo->nOBSat = 0;
|
||||
if( nLoop>0 ){
|
||||
Bitmask m;
|
||||
int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom,
|
||||
WHERE_ORDERBY_LIMIT, nLoop-1, pFrom->aLoop[nLoop-1], &m);
|
||||
if( rc==pWInfo->pOrderBy->nExpr ){
|
||||
pWInfo->bOrderedInnerLoop = 1;
|
||||
pWInfo->revMask = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
|
||||
&& pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0
|
||||
@@ -4000,7 +3943,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
|
||||
Index *pIdx;
|
||||
|
||||
pWInfo = pBuilder->pWInfo;
|
||||
if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0;
|
||||
if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0;
|
||||
assert( pWInfo->pTabList->nSrc>=1 );
|
||||
pItem = pWInfo->pTabList->a;
|
||||
pTab = pItem->pTab;
|
||||
@@ -4147,7 +4090,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
|
||||
** is called from an UPDATE or DELETE statement, then pOrderBy is NULL.
|
||||
**
|
||||
** The iIdxCur parameter is the cursor number of an index. If
|
||||
** WHERE_OR_SUBCLAUSE is set, iIdxCur is the cursor number of an index
|
||||
** WHERE_ONETABLE_ONLY is set, iIdxCur is the cursor number of an index
|
||||
** to use for OR clause processing. The WHERE clause should use this
|
||||
** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is
|
||||
** the first cursor in an array of cursors for all indices. iIdxCur should
|
||||
@@ -4161,7 +4104,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
|
||||
ExprList *pDistinctSet, /* Try not to output two rows that duplicate these */
|
||||
u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */
|
||||
int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number
|
||||
int iAuxArg /* If WHERE_ONETABLE_ONLY is set, index cursor number
|
||||
** If WHERE_USE_LIMIT, then the limit amount */
|
||||
){
|
||||
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
|
||||
@@ -4180,11 +4123,11 @@ WhereInfo *sqlite3WhereBegin(
|
||||
|
||||
assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
|
||||
(wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
|
||||
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
|
||||
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
|
||||
));
|
||||
|
||||
/* Only one of WHERE_OR_SUBCLAUSE or WHERE_USE_LIMIT */
|
||||
assert( (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
|
||||
/* Only one of WHERE_ONETABLE_ONLY or WHERE_USE_LIMIT */
|
||||
assert( (wctrlFlags & WHERE_ONETABLE_ONLY)==0
|
||||
|| (wctrlFlags & WHERE_USE_LIMIT)==0 );
|
||||
|
||||
/* Variable initialization */
|
||||
@@ -4212,11 +4155,11 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
|
||||
/* This function normally generates a nested loop for all tables in
|
||||
** pTabList. But if the WHERE_OR_SUBCLAUSE flag is set, then we should
|
||||
** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should
|
||||
** only generate code for the first table in pTabList and assume that
|
||||
** any cursors associated with subsequent tables are uninitialized.
|
||||
*/
|
||||
nTabList = (wctrlFlags & WHERE_OR_SUBCLAUSE) ? 1 : pTabList->nSrc;
|
||||
nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc;
|
||||
|
||||
/* Allocate and initialize the WhereInfo structure that will become the
|
||||
** return value. A single allocation is used to store the WhereInfo
|
||||
@@ -4292,7 +4235,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** Note that bitmasks are created for all pTabList->nSrc tables in
|
||||
** pTabList, not just the first nTabList tables. nTabList is normally
|
||||
** equal to pTabList->nSrc but might be shortened to 1 if the
|
||||
** WHERE_OR_SUBCLAUSE flag is set.
|
||||
** WHERE_ONETABLE_ONLY flag is set.
|
||||
*/
|
||||
for(ii=0; ii<pTabList->nSrc; ii++){
|
||||
createMask(pMaskSet, pTabList->a[ii].iCursor);
|
||||
@@ -4330,7 +4273,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3DebugPrintf(")\n");
|
||||
}
|
||||
if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
|
||||
sqlite3WhereClausePrint(sWLB.pWC);
|
||||
int i;
|
||||
for(i=0; i<sWLB.pWC->nTerm; i++){
|
||||
whereTermPrint(&sWLB.pWC->a[i], i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4472,7 +4418,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}else
|
||||
#endif
|
||||
if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
|
||||
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){
|
||||
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
|
||||
int op = OP_OpenRead;
|
||||
if( pWInfo->eOnePass!=ONEPASS_OFF ){
|
||||
op = OP_OpenWrite;
|
||||
@@ -4511,7 +4457,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
/* iAuxArg is always set if to a positive value if ONEPASS is possible */
|
||||
assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 );
|
||||
if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx)
|
||||
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0
|
||||
&& (wctrlFlags & WHERE_ONETABLE_ONLY)!=0
|
||||
){
|
||||
/* This is one term of an OR-optimization using the PRIMARY KEY of a
|
||||
** WITHOUT ROWID table. No need for a separate index */
|
||||
@@ -4527,9 +4473,9 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
op = OP_OpenWrite;
|
||||
pWInfo->aiCurOnePass[1] = iIndexCur;
|
||||
}else if( iAuxArg && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){
|
||||
}else if( iAuxArg && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){
|
||||
iIndexCur = iAuxArg;
|
||||
op = OP_ReopenIdx;
|
||||
if( wctrlFlags & WHERE_REOPEN_IDX ) op = OP_ReopenIdx;
|
||||
}else{
|
||||
iIndexCur = pParse->nTab++;
|
||||
}
|
||||
@@ -4591,7 +4537,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
|
||||
notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady);
|
||||
pWInfo->iContinue = pLevel->addrCont;
|
||||
if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0 ){
|
||||
if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){
|
||||
sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain);
|
||||
}
|
||||
}
|
||||
@@ -4714,12 +4660,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
|
||||
/* Close all of the cursors that were opened by sqlite3WhereBegin.
|
||||
** Except, do not close cursors that will be reused by the OR optimization
|
||||
** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors
|
||||
** (WHERE_OMIT_OPEN_CLOSE). And do not close the OP_OpenWrite cursors
|
||||
** created for the ONEPASS optimization.
|
||||
*/
|
||||
if( (pTab->tabFlags & TF_Ephemeral)==0
|
||||
&& pTab->pSelect==0
|
||||
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
|
||||
&& (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
|
||||
){
|
||||
int ws = pLoop->wsFlags;
|
||||
if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){
|
||||
|
||||
+1
-5
@@ -418,9 +418,8 @@ struct WhereInfo {
|
||||
u8 sorted; /* True if really sorted (not just grouped) */
|
||||
u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */
|
||||
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
|
||||
u8 eDistinct; /* One of the WHERE_DISTINCT_* values */
|
||||
u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
|
||||
u8 nLevel; /* Number of nested loop */
|
||||
u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */
|
||||
int iTop; /* The very beginning of the WHERE loop */
|
||||
int iContinue; /* Jump here to continue with next record */
|
||||
int iBreak; /* Jump here to break out of the loop */
|
||||
@@ -437,9 +436,6 @@ struct WhereInfo {
|
||||
** where.c:
|
||||
*/
|
||||
Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
|
||||
#ifdef WHERETRACE_ENABLED
|
||||
void sqlite3WhereClausePrint(WhereClause *pWC);
|
||||
#endif
|
||||
WhereTerm *sqlite3WhereFindTerm(
|
||||
WhereClause *pWC, /* The WHERE clause to be searched */
|
||||
int iCur, /* Cursor number of LHS */
|
||||
|
||||
+12
-7
@@ -126,7 +126,7 @@ int sqlite3WhereExplainOneScan(
|
||||
|
||||
pLoop = pLevel->pWLoop;
|
||||
flags = pLoop->wsFlags;
|
||||
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0;
|
||||
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0;
|
||||
|
||||
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|
||||
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|
||||
@@ -776,7 +776,7 @@ static void codeDeferredSeek(
|
||||
assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_Seek, iIdxCur, 0, iCur);
|
||||
if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
|
||||
if( (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)
|
||||
&& DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
|
||||
){
|
||||
int i;
|
||||
@@ -831,7 +831,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
|
||||
bRev = (pWInfo->revMask>>iLevel)&1;
|
||||
omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
|
||||
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0;
|
||||
&& (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0;
|
||||
VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
|
||||
|
||||
/* Create labels for the "break" and "continue" instructions
|
||||
@@ -971,7 +971,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
|
||||
if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
|
||||
addrNxt = pLevel->addrNxt;
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
|
||||
sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
|
||||
VdbeCoverage(v);
|
||||
sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
@@ -1323,7 +1324,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( omitTable ){
|
||||
/* pIdx is a covering index. No need to access the main table. */
|
||||
}else if( HasRowid(pIdx->pTable) ){
|
||||
if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE)!=0 ){
|
||||
if( pWInfo->eOnePass!=ONEPASS_OFF ){
|
||||
iRowidReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
@@ -1516,7 +1517,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
** eliminating duplicates from other WHERE clauses, the action for each
|
||||
** sub-WHERE clause is to to invoke the main loop body as a subroutine.
|
||||
*/
|
||||
wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE);
|
||||
wctrlFlags = WHERE_OMIT_OPEN_CLOSE
|
||||
| WHERE_FORCE_TABLE
|
||||
| WHERE_ONETABLE_ONLY
|
||||
| WHERE_NO_AUTOINDEX;
|
||||
for(ii=0; ii<pOrWc->nTerm; ii++){
|
||||
WhereTerm *pOrTerm = &pOrWc->a[ii];
|
||||
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
|
||||
@@ -1624,6 +1628,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
){
|
||||
assert( pSubWInfo->a[0].iIdxCur==iCovCur );
|
||||
pCov = pSubLoop->u.btree.pIndex;
|
||||
wctrlFlags |= WHERE_REOPEN_IDX;
|
||||
}else{
|
||||
pCov = 0;
|
||||
}
|
||||
@@ -1685,7 +1690,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
|
||||
if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
|
||||
testcase( pWInfo->untestedTerms==0
|
||||
&& (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 );
|
||||
&& (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
|
||||
pWInfo->untestedTerms = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -43,4 +43,30 @@ do_test filectrl-1.6 {
|
||||
} {/etilqs_/}
|
||||
db close
|
||||
forcedelete .test_control_lockproxy.db-conch test.proxy
|
||||
forcedelete test.db test2.db
|
||||
|
||||
if {$tcl_platform(platform)=="windows"} {
|
||||
do_test filectrl-2.1 {
|
||||
sqlite3 db test2.db
|
||||
set size [file size test2.db]
|
||||
set handle [file_control_win32_get_handle db]
|
||||
db close
|
||||
forcedelete test2.db
|
||||
list $size $handle [expr {$handle != 0}]
|
||||
} {/^0 \{0 \d+\} 1$/}
|
||||
|
||||
do_test filectrl-2.2 {
|
||||
sqlite3 db test2.db
|
||||
execsql {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 (x) VALUES(RANDOMBLOB(1048576));
|
||||
}
|
||||
set size [file size test2.db]
|
||||
set handle [file_control_win32_get_handle db]
|
||||
db close
|
||||
forcedelete test2.db
|
||||
list $size $handle [expr {$handle != 0}]
|
||||
} {/^1\d+ \{0 \d+\} 1$/}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -514,8 +514,4 @@ do_test fts3expr-9.1 {
|
||||
test_fts3expr "f (e NEAR/2 a)"
|
||||
} {AND {PHRASE 3 0 f} {NEAR/2 {PHRASE 3 0 e} {PHRASE 3 0 a}}}
|
||||
|
||||
do_test fts3expr-10.1 { test_fts3expr "abc *" } {PHRASE 3 0 abc}
|
||||
do_test fts3expr-10.2 { test_fts3expr "*" } {}
|
||||
do_test fts3expr-10.3 { test_fts3expr "abc*" } {PHRASE 3 0 abc+}
|
||||
|
||||
finish_test
|
||||
|
||||
+1
-27
@@ -614,32 +614,6 @@ do_execsql_test intpkey-16.1 {
|
||||
PRAGMA table_info=t16a;
|
||||
} {0 id INTEGER 0 {} 1 1 b TEXT 0 {} 0 2 c INT 0 {} 0}
|
||||
|
||||
# 2016-05-06 ticket https://www.sqlite.org/src/tktview/16c9801ceba4923939085
|
||||
# When the schema contains an index on the IPK and no other index
|
||||
# and a WHERE clause on a delete uses an OR where both sides referencing
|
||||
# the IPK, then it is possible that the OP_Delete will fail because there
|
||||
# deferred seek of the OP_Seek is not resolved prior to reaching the OP_Delete.
|
||||
#
|
||||
do_execsql_test intpkey-17.0 {
|
||||
CREATE TABLE t17(x INTEGER PRIMARY KEY, y TEXT);
|
||||
INSERT INTO t17(x,y) VALUES(123,'elephant'),(248,'giraffe');
|
||||
CREATE INDEX t17x ON t17(x);
|
||||
DELETE FROM t17 WHERE x=99 OR x<130;
|
||||
SELECT * FROM t17;
|
||||
} {248 giraffe}
|
||||
do_execsql_test intpkey-17.1 {
|
||||
DROP INDEX t17x;
|
||||
DELETE FROM t17;
|
||||
INSERT INTO t17(x,y) VALUES(123,'elephant'),(248,'giraffe');
|
||||
CREATE UNIQUE INDEX t17x ON t17(abs(x));
|
||||
DELETE FROM t17 WHERE abs(x) IS NULL OR abs(x)<130;
|
||||
SELECT * FROM t17;
|
||||
} {248 giraffe}
|
||||
do_execsql_test intpkey-17.2 {
|
||||
DELETE FROM t17;
|
||||
INSERT INTO t17(x,y) VALUES(123,'elephant'),(248,'giraffe');
|
||||
UPDATE t17 SET y='ostrich' WHERE abs(x)=248;
|
||||
SELECT * FROM t17 ORDER BY +x;
|
||||
} {123 elephant 248 ostrich}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
# 2016-05-20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the LIMIT in combination with ORDER BY
|
||||
# and in particular, the optimizations in the inner loop that cause an
|
||||
# early exit of the inner loop when the LIMIT is reached and the inner
|
||||
# loop is emitting rows in ORDER BY order.
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test limit2-100 {
|
||||
CREATE TABLE t1(a,b);
|
||||
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
|
||||
INSERT INTO t1(a,b) SELECT 1, (x*17)%1000 + 1000 FROM c;
|
||||
INSERT INTO t1(a,b) VALUES(2,2),(3,1006),(4,4),(5,9999);
|
||||
CREATE INDEX t1ab ON t1(a,b);
|
||||
}
|
||||
set sqlite_search_count 0
|
||||
do_execsql_test limit2-100.1 {
|
||||
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY b LIMIT 5;
|
||||
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
|
||||
set fast_count $sqlite_search_count
|
||||
set sqlite_search_count 0
|
||||
do_execsql_test limit2-100.2 {
|
||||
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY +b LIMIT 5;
|
||||
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
|
||||
do_test limit2-100.3 {
|
||||
set slow_count $sqlite_search_count
|
||||
expr {$fast_count < 0.02*$slow_count}
|
||||
} {1}
|
||||
|
||||
do_execsql_test limit2-110 {
|
||||
CREATE TABLE t2(x,y);
|
||||
INSERT INTO t2(x,y) VALUES('a',1),('a',2),('a',3),('a',4);
|
||||
INSERT INTO t2(x,y) VALUES('b',1),('c',2),('d',3),('e',4);
|
||||
CREATE INDEX t2xy ON t2(x,y);
|
||||
}
|
||||
set sqlite_search_count 0
|
||||
do_execsql_test limit2-110.1 {
|
||||
SELECT a, b, '|' FROM t2, t1 WHERE t2.x='a' AND t1.a=t2.y ORDER BY t1.b LIMIT 5;
|
||||
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
|
||||
set fast_count $sqlite_search_count
|
||||
set sqlite_search_count 0
|
||||
do_execsql_test limit2-110.2 {
|
||||
SELECT a, b, '|' FROM t2, t1 WHERE t2.x='a' AND t1.a=t2.y ORDER BY +t1.b LIMIT 5;
|
||||
} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |}
|
||||
set slow_count $sqlite_search_count
|
||||
do_test limit2-110.3 {
|
||||
expr {$fast_count < 0.02*$slow_count}
|
||||
} {1}
|
||||
|
||||
do_execsql_test limit2-120 {
|
||||
DROP INDEX t1ab;
|
||||
CREATE INDEX t1ab ON t1(a,b DESC);
|
||||
}
|
||||
set sqlite_search_count 0
|
||||
do_execsql_test limit2-120.1 {
|
||||
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY b DESC LIMIT 5;
|
||||
} {5 9999 | 1 1999 | 1 1998 | 1 1997 | 1 1996 |}
|
||||
set fast_count $sqlite_search_count
|
||||
set sqlite_search_count 0
|
||||
do_execsql_test limit2-120.2 {
|
||||
SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY +b DESC LIMIT 5;
|
||||
} {5 9999 | 1 1999 | 1 1998 | 1 1997 | 1 1996 |}
|
||||
do_test limit2-120.3 {
|
||||
set slow_count $sqlite_search_count
|
||||
expr {$fast_count < 0.02*$slow_count}
|
||||
} {1}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -571,8 +571,6 @@ ifcapable subquery&&compound {
|
||||
# Overflow the lemon parser stack by providing an overly complex
|
||||
# expression. Make sure that the overflow is detected and reported.
|
||||
#
|
||||
# This test fails when building with -DYYSTACKDEPTH=0
|
||||
#
|
||||
do_test misc5-7.1 {
|
||||
execsql {CREATE TABLE t1(x)}
|
||||
set sql "INSERT INTO t1 VALUES("
|
||||
|
||||
@@ -746,11 +746,6 @@ test_suite "inmemory_journal" -description {
|
||||
|
||||
# WAL mode is different.
|
||||
wal* tkt-2d1a5c67d.test backcompat.test e_wal* rowallock.test
|
||||
|
||||
# This test does not work as the "PRAGMA journal_mode = memory"
|
||||
# statement switches the database out of wal mode at inopportune
|
||||
# times.
|
||||
snapshot_fault.test
|
||||
}]
|
||||
|
||||
ifcapable mem3 {
|
||||
|
||||
@@ -915,9 +915,6 @@ do_test shell1-5.0 {
|
||||
}
|
||||
} {}
|
||||
|
||||
# These test cases do not work on MinGW
|
||||
if 0 {
|
||||
|
||||
# The string used here is the word "test" in Chinese.
|
||||
# In UTF-8, it is encoded as: \xE6\xB5\x8B\xE8\xAF\x95
|
||||
set test \u6D4B\u8BD5
|
||||
@@ -954,6 +951,5 @@ do_test shell1-6.1 {
|
||||
}
|
||||
forcedelete test3.db
|
||||
} {}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -352,6 +352,10 @@ do_test 6.4 {
|
||||
db2 eval { SELECT * FROM x1 }
|
||||
} {z zz zzz}
|
||||
|
||||
# EVIDENCE-OF: R-55491-50411 A snapshot will fail to open if the
|
||||
# database connection D has not previously completed at least one read
|
||||
# operation against the database file.
|
||||
#
|
||||
do_test 6.5 {
|
||||
db2 close
|
||||
sqlite3 db2 test.db
|
||||
|
||||
+4
-6
@@ -78,7 +78,10 @@ execsql { CREATE TABLE t1(x) }
|
||||
# each a bit over 100 bytes in size. In total a little more than 1MiB
|
||||
# of data.
|
||||
#
|
||||
breakpoint
|
||||
foreach {tn pgsz cachesz bTemp} {
|
||||
2 1024 1000 1
|
||||
|
||||
1 4096 1000 0
|
||||
2 1024 1000 1
|
||||
|
||||
@@ -94,12 +97,6 @@ foreach {tn pgsz cachesz bTemp} {
|
||||
PRAGMA cache_size = $cachesz;
|
||||
"
|
||||
|
||||
if {[db one {PRAGMA page_size}]!=$pgsz} {
|
||||
# SEE is not able to change page sizes and that messes up the
|
||||
# results that follow.
|
||||
continue
|
||||
}
|
||||
|
||||
do_test 2.$tn.1 {
|
||||
set ::iTemp 0
|
||||
catch { array unset F }
|
||||
@@ -116,3 +113,4 @@ foreach {tn pgsz cachesz bTemp} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# 2016 March 3
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix tempdb2
|
||||
|
||||
db close
|
||||
sqlite3 db ""
|
||||
|
||||
proc int2str {i} { string range [string repeat "$i." 450] 0 899 }
|
||||
db func int2str int2str
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# 1.1: Write a big transaction to the db. One so large that it forces
|
||||
# the file to be created and the cache flushed to disk on COMMIT.
|
||||
#
|
||||
# 1.2: Write a small transaction - one small enough that it remains in
|
||||
# memory on COMMIT. All the pages of table [t1] are now dirty.
|
||||
#
|
||||
# 1.3: Delete the contents of [t1]. This moves all of its leaves to the
|
||||
# free-list and causes the btree layer to call PagerDontWrite() on
|
||||
# each of them.
|
||||
#
|
||||
# Then do a big update on table [t2]. So big that the former leaves
|
||||
# of [t1] are forced out of the cache. Then roll back the transaction.
|
||||
# If the PagerDontWrite() calls are honoured and the data is not written
|
||||
# to disk, the update made in test 1.2 will be lost at this point. Or, if
|
||||
# they are ignored (as they should be for temp databases), the update
|
||||
# will be safely written out to disk before the cache entries are
|
||||
# discarded.
|
||||
#
|
||||
do_execsql_test 1.1 {
|
||||
PRAGMA page_size=1024;
|
||||
PRAGMA cache_size=50;
|
||||
|
||||
BEGIN;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, int2str(1));
|
||||
INSERT INTO t1 VALUES(2, int2str(1));
|
||||
INSERT INTO t1 VALUES(3, int2str(1));
|
||||
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
WITH c(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100 )
|
||||
INSERT INTO t2 SELECT x, int2str(x) FROM c;
|
||||
COMMIT;
|
||||
|
||||
PRAGMA lock_status;
|
||||
} {main unlocked temp closed}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
UPDATE t1 SET b=int2str(2);
|
||||
SELECT b=int2str(2) FROM t1
|
||||
} {1 1 1}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
UPDATE t2 SET b=int2str(a+1);
|
||||
ROLLBACK;
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT b=int2str(2) FROM t1
|
||||
} {1 1 1}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# 2016-05-10
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix temptable3
|
||||
|
||||
db close
|
||||
sqlite3 db {}
|
||||
do_execsql_test 1.1 {
|
||||
PRAGMA cache_size = 1;
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA auto_vacuum = 2;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES( randomblob(800) );
|
||||
INSERT INTO t1 VALUES( randomblob(800) );
|
||||
CREATE TABLE t2(x);
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
db close
|
||||
sqlite3 db {}
|
||||
do_execsql_test 1.2 {
|
||||
PRAGMA cache_size = 1;
|
||||
PRAGMA auto_vacuum = 2;
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(x UNIQUE);
|
||||
INSERT INTO t2 VALUES(1), (2), (3);
|
||||
DROP TABLE t1;
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
finish_test
|
||||
@@ -1,76 +0,0 @@
|
||||
# 2010 May 25
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/wal_common.tcl
|
||||
ifcapable !wal {finish_test ; return }
|
||||
set testprefix walcrash4
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# At one point, if "PRAGMA synchronous=full" is set and the platform
|
||||
# does not support POWERSAFE_OVERWRITE, and the last frame written to
|
||||
# the wal file in a transaction is aligned with a sector boundary, the
|
||||
# xSync() call was omitted.
|
||||
#
|
||||
# The following test verifies that this has been fixed.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA autovacuum = 0;
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA journal_mode = wal;
|
||||
PRAGMA main.synchronous = full;
|
||||
} {wal}
|
||||
|
||||
faultsim_save_and_close
|
||||
|
||||
for {set nExtra 0} {$nExtra < 10} {incr nExtra} {
|
||||
for {set i 0} {$i < 10} {incr i} {
|
||||
do_test 1.nExtra=$nExtra.i=$i.1 {
|
||||
faultsim_restore_and_reopen
|
||||
|
||||
set fd [open crash.tcl w]
|
||||
puts $fd [subst -nocommands {
|
||||
sqlite3_crash_enable 1
|
||||
sqlite3_test_control_pending_byte $::sqlite_pending_byte
|
||||
sqlite3 db test.db -vfs crash
|
||||
db eval {
|
||||
PRAGMA main.synchronous=FULL;
|
||||
BEGIN;
|
||||
CREATE TABLE t1(x UNIQUE);
|
||||
}
|
||||
for {set e 2} {[set e] < ($nExtra+2)} {incr e} {
|
||||
db eval "CREATE TABLE t[set e] (x)"
|
||||
}
|
||||
db eval {
|
||||
INSERT INTO t1 VALUES( randomblob(170000) );
|
||||
COMMIT;
|
||||
}
|
||||
sqlite3_crash_now
|
||||
}]
|
||||
close $fd
|
||||
|
||||
set r [catch { exec [info nameofexec] crash.tcl >@stdout } msg]
|
||||
list $r $msg
|
||||
} {1 {child process exited abnormally}}
|
||||
|
||||
do_execsql_test 1.nExtra=$nExtra.i=$i.2 {
|
||||
SELECT count(*) FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
} {1 ok}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
+22
-83
@@ -288,13 +288,10 @@ struct rule {
|
||||
const char *code; /* The code executed when this rule is reduced */
|
||||
const char *codePrefix; /* Setup code before code[] above */
|
||||
const char *codeSuffix; /* Breakdown code after code[] above */
|
||||
int noCode; /* True if this rule has no associated C code */
|
||||
int codeEmitted; /* True if the code has been emitted already */
|
||||
struct symbol *precsym; /* Precedence symbol for this rule */
|
||||
int index; /* An index number for this rule */
|
||||
int iRule; /* Rule number as used in the generated tables */
|
||||
Boolean canReduce; /* True if this rule is ever reduced */
|
||||
Boolean doesReduce; /* Reduce actions occur after optimization */
|
||||
struct rule *nextlhs; /* Next rule with the same LHS */
|
||||
struct rule *next; /* Next rule in the global list */
|
||||
};
|
||||
@@ -342,7 +339,6 @@ struct action {
|
||||
struct state *stp; /* The new state, if a shift */
|
||||
struct rule *rp; /* The rule, if a reduce */
|
||||
} x;
|
||||
struct symbol *spOpt; /* SHIFTREDUCE optimization to this symbol */
|
||||
struct action *next; /* Next action for this state */
|
||||
struct action *collide; /* Next action with the same hash */
|
||||
};
|
||||
@@ -353,7 +349,7 @@ struct state {
|
||||
struct config *bp; /* The basis configurations for this state */
|
||||
struct config *cfp; /* All configurations in this set */
|
||||
int statenum; /* Sequential number for this state */
|
||||
struct action *ap; /* List of actions for this state */
|
||||
struct action *ap; /* Array of actions for this state */
|
||||
int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */
|
||||
int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */
|
||||
int iDfltReduce; /* Default action is to REDUCE by this rule */
|
||||
@@ -534,7 +530,6 @@ void Action_add(
|
||||
*app = newaction;
|
||||
newaction->type = type;
|
||||
newaction->sp = sp;
|
||||
newaction->spOpt = 0;
|
||||
if( type==SHIFT ){
|
||||
newaction->x.stp = (struct state *)arg;
|
||||
}else{
|
||||
@@ -1504,7 +1499,7 @@ static void handle_T_option(char *z){
|
||||
lemon_strcpy(user_templatename, z);
|
||||
}
|
||||
|
||||
/* Merge together to lists of rules ordered by rule.iRule */
|
||||
/* Merge together to lists of rules order by rule.iRule */
|
||||
static struct rule *Rule_merge(struct rule *pA, struct rule *pB){
|
||||
struct rule *pFirst = 0;
|
||||
struct rule **ppPrev = &pFirst;
|
||||
@@ -1647,10 +1642,7 @@ int main(int argc, char **argv)
|
||||
for(i=1; ISUPPER(lem.symbols[i]->name[0]); i++);
|
||||
lem.nterminal = i;
|
||||
|
||||
/* Assign sequential rule numbers. Start with 0. Put rules that have no
|
||||
** reduce action C-code associated with them last, so that the switch()
|
||||
** statement that selects reduction actions will have a smaller jump table.
|
||||
*/
|
||||
/* Assign sequential rule numbers */
|
||||
for(i=0, rp=lem.rule; rp; rp=rp->next){
|
||||
rp->iRule = rp->code ? i++ : -1;
|
||||
}
|
||||
@@ -2219,7 +2211,6 @@ to follow the previous rule.");
|
||||
}else{
|
||||
psp->prevrule->line = psp->tokenlineno;
|
||||
psp->prevrule->code = &x[1];
|
||||
psp->prevrule->noCode = 0;
|
||||
}
|
||||
}else if( x[0]=='[' ){
|
||||
psp->state = PRECEDENCE_MARK_1;
|
||||
@@ -2326,7 +2317,6 @@ to follow the previous rule.");
|
||||
rp->lhsalias = psp->lhsalias;
|
||||
rp->nrhs = psp->nrhs;
|
||||
rp->code = 0;
|
||||
rp->noCode = 1;
|
||||
rp->precsym = 0;
|
||||
rp->index = psp->gp->nrule++;
|
||||
rp->nextlhs = rp->lhs->rule;
|
||||
@@ -3170,9 +3160,6 @@ int PrintAction(
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
if( result && ap->spOpt ){
|
||||
fprintf(fp," /* because %s==%s */", ap->sp->name, ap->spOpt->name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3540,8 +3527,9 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){
|
||||
}
|
||||
|
||||
/*
|
||||
** Write and transform the rp->code string so that symbols are expanded.
|
||||
** Populate the rp->codePrefix and rp->codeSuffix strings, as appropriate.
|
||||
** zCode is a string that is the action associated with a rule. Expand
|
||||
** the symbols in this string so that the refer to elements of the parser
|
||||
** stack.
|
||||
**
|
||||
** Return 1 if the expanded code requires that "yylhsminor" local variable
|
||||
** to be defined.
|
||||
@@ -3565,9 +3553,6 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){
|
||||
static char newlinestr[2] = { '\n', '\0' };
|
||||
rp->code = newlinestr;
|
||||
rp->line = rp->ruleline;
|
||||
rp->noCode = 1;
|
||||
}else{
|
||||
rp->noCode = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3583,7 +3568,6 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){
|
||||
append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0,
|
||||
rp->rhs[0]->index,1-rp->nrhs);
|
||||
rp->codePrefix = Strsafe(append_str(0,0,0,0));
|
||||
rp->noCode = 0;
|
||||
}
|
||||
}else if( rp->lhsalias==0 ){
|
||||
/* There is no LHS value symbol. */
|
||||
@@ -3731,10 +3715,7 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){
|
||||
|
||||
/* Suffix code generation complete */
|
||||
cp = append_str(0,0,0,0);
|
||||
if( cp && cp[0] ){
|
||||
rp->codeSuffix = Strsafe(cp);
|
||||
rp->noCode = 0;
|
||||
}
|
||||
if( cp && cp[0] ) rp->codeSuffix = Strsafe(cp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -4153,19 +4134,6 @@ void ReportTable(
|
||||
}
|
||||
free(ax);
|
||||
|
||||
/* Mark rules that are actually used for reduce actions after all
|
||||
** optimizations have been applied
|
||||
*/
|
||||
for(rp=lemp->rule; rp; rp=rp->next) rp->doesReduce = LEMON_FALSE;
|
||||
for(i=0; i<lemp->nxstate; i++){
|
||||
struct action *ap;
|
||||
for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){
|
||||
if( ap->type==REDUCE || ap->type==SHIFTREDUCE ){
|
||||
ap->x.rp->doesReduce = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish rendering the constants now that the action table has
|
||||
** been computed */
|
||||
fprintf(out,"#define YYNSTATE %d\n",lemp->nxstate); lineno++;
|
||||
@@ -4428,9 +4396,13 @@ void ReportTable(
|
||||
/* First output rules other than the default: rule */
|
||||
for(rp=lemp->rule; rp; rp=rp->next){
|
||||
struct rule *rp2; /* Other rules with the same action */
|
||||
if( rp->codeEmitted ) continue;
|
||||
if( rp->noCode ){
|
||||
/* No C code actions, so this will be part of the "default:" rule */
|
||||
if( rp->code==0 ) continue;
|
||||
if( rp->code[0]=='\n'
|
||||
&& rp->code[1]==0
|
||||
&& rp->codePrefix==0
|
||||
&& rp->codeSuffix==0
|
||||
){
|
||||
/* No actions, so this will be part of the "default:" rule */
|
||||
continue;
|
||||
}
|
||||
fprintf(out," case %d: /* ", rp->iRule);
|
||||
@@ -4442,27 +4414,24 @@ void ReportTable(
|
||||
fprintf(out," case %d: /* ", rp2->iRule);
|
||||
writeRuleText(out, rp2);
|
||||
fprintf(out," */ yytestcase(yyruleno==%d);\n", rp2->iRule); lineno++;
|
||||
rp2->codeEmitted = 1;
|
||||
rp2->code = 0;
|
||||
}
|
||||
}
|
||||
emit_code(out,rp,lemp,&lineno);
|
||||
fprintf(out," break;\n"); lineno++;
|
||||
rp->codeEmitted = 1;
|
||||
rp->code = 0;
|
||||
}
|
||||
/* Finally, output the default: rule. We choose as the default: all
|
||||
** empty actions. */
|
||||
fprintf(out," default:\n"); lineno++;
|
||||
for(rp=lemp->rule; rp; rp=rp->next){
|
||||
if( rp->codeEmitted ) continue;
|
||||
assert( rp->noCode );
|
||||
if( rp->code==0 ) continue;
|
||||
assert( rp->code[0]=='\n' && rp->code[1]==0 );
|
||||
assert( rp->codePrefix==0 );
|
||||
assert( rp->codeSuffix==0 );
|
||||
fprintf(out," /* (%d) ", rp->iRule);
|
||||
writeRuleText(out, rp);
|
||||
if( rp->doesReduce ){
|
||||
fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->iRule); lineno++;
|
||||
}else{
|
||||
fprintf(out, " (OPTIMIZED OUT) */ assert(yyruleno!=%d);\n",
|
||||
rp->iRule); lineno++;
|
||||
}
|
||||
fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->iRule); lineno++;
|
||||
}
|
||||
fprintf(out," break;\n"); lineno++;
|
||||
tplt_xfer(lemp->name,in,out,&lineno);
|
||||
@@ -4533,7 +4502,7 @@ void ReportHeader(struct lemon *lemp)
|
||||
void CompressTables(struct lemon *lemp)
|
||||
{
|
||||
struct state *stp;
|
||||
struct action *ap, *ap2, *nextap;
|
||||
struct action *ap, *ap2;
|
||||
struct rule *rp, *rp2, *rbest;
|
||||
int nbest, n;
|
||||
int i;
|
||||
@@ -4610,36 +4579,6 @@ void CompressTables(struct lemon *lemp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If a SHIFTREDUCE action specifies a rule that has a single RHS term
|
||||
** (meaning that the SHIFTREDUCE will land back in the state where it
|
||||
** started) and if there is no C-code associated with the reduce action,
|
||||
** then we can go ahead and convert the action to be the same as the
|
||||
** action for the RHS of the rule.
|
||||
*/
|
||||
for(i=0; i<lemp->nstate; i++){
|
||||
stp = lemp->sorted[i];
|
||||
for(ap=stp->ap; ap; ap=nextap){
|
||||
nextap = ap->next;
|
||||
if( ap->type!=SHIFTREDUCE ) continue;
|
||||
rp = ap->x.rp;
|
||||
if( rp->noCode==0 ) continue;
|
||||
if( rp->nrhs!=1 ) continue;
|
||||
#if 1
|
||||
/* Only apply this optimization to non-terminals. It would be OK to
|
||||
** apply it to terminal symbols too, but that makes the parser tables
|
||||
** larger. */
|
||||
if( ap->sp->index<lemp->nterminal ) continue;
|
||||
#endif
|
||||
/* If we reach this point, it means the optimization can be applied */
|
||||
nextap = ap;
|
||||
for(ap2=stp->ap; ap2 && (ap2==ap || ap2->sp!=rp->lhs); ap2=ap2->next){}
|
||||
assert( ap2!=0 );
|
||||
ap->spOpt = ap2->sp;
|
||||
ap->type = ap2->type;
|
||||
ap->x = ap2->x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+74
-78
@@ -203,9 +203,9 @@ typedef struct yyStackEntry yyStackEntry;
|
||||
/* The state of the parser is completely contained in an instance of
|
||||
** the following structure */
|
||||
struct yyParser {
|
||||
yyStackEntry *yytos; /* Pointer to top element of the stack */
|
||||
int yyidx; /* Index of top element in stack */
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
int yyhwm; /* High-water mark of the stack */
|
||||
int yyidxMax; /* Maximum value of yyidx */
|
||||
#endif
|
||||
#ifndef YYNOERRORRECOVERY
|
||||
int yyerrcnt; /* Shifts left before out of the error */
|
||||
@@ -214,7 +214,6 @@ struct yyParser {
|
||||
#if YYSTACKDEPTH<=0
|
||||
int yystksz; /* Current side of the stack */
|
||||
yyStackEntry *yystack; /* The parser's stack */
|
||||
yyStackEntry yystk0; /* First stack entry */
|
||||
#else
|
||||
yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
|
||||
#endif
|
||||
@@ -272,34 +271,24 @@ static const char *const yyRuleName[] = {
|
||||
|
||||
#if YYSTACKDEPTH<=0
|
||||
/*
|
||||
** Try to increase the size of the parser stack. Return the number
|
||||
** of errors. Return 0 on success.
|
||||
** Try to increase the size of the parser stack.
|
||||
*/
|
||||
static int yyGrowStack(yyParser *p){
|
||||
static void yyGrowStack(yyParser *p){
|
||||
int newSize;
|
||||
int idx;
|
||||
yyStackEntry *pNew;
|
||||
|
||||
newSize = p->yystksz*2 + 100;
|
||||
idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
|
||||
if( p->yystack==&p->yystk0 ){
|
||||
pNew = malloc(newSize*sizeof(pNew[0]));
|
||||
if( pNew ) pNew[0] = p->yystk0;
|
||||
}else{
|
||||
pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
|
||||
}
|
||||
pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
|
||||
if( pNew ){
|
||||
p->yystack = pNew;
|
||||
p->yytos = &p->yystack[idx];
|
||||
p->yystksz = newSize;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
|
||||
yyTracePrompt, p->yystksz, newSize);
|
||||
fprintf(yyTraceFILE,"%sStack grows to %d entries!\n",
|
||||
yyTracePrompt, p->yystksz);
|
||||
}
|
||||
#endif
|
||||
p->yystksz = newSize;
|
||||
}
|
||||
return pNew==0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -328,21 +317,15 @@ void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
|
||||
yyParser *pParser;
|
||||
pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
|
||||
if( pParser ){
|
||||
pParser->yyidx = -1;
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
pParser->yyhwm = 0;
|
||||
pParser->yyidxMax = 0;
|
||||
#endif
|
||||
#if YYSTACKDEPTH<=0
|
||||
pParser->yytos = NULL;
|
||||
pParser->yystack = NULL;
|
||||
pParser->yystksz = 0;
|
||||
if( yyGrowStack(pParser) ){
|
||||
pParser->yystack = &pParser->yystk0;
|
||||
pParser->yystksz = 1;
|
||||
}
|
||||
yyGrowStack(pParser);
|
||||
#endif
|
||||
pParser->yytos = pParser->yystack;
|
||||
pParser->yystack[0].stateno = 0;
|
||||
pParser->yystack[0].major = 0;
|
||||
}
|
||||
return pParser;
|
||||
}
|
||||
@@ -386,8 +369,8 @@ static void yy_destructor(
|
||||
*/
|
||||
static void yy_pop_parser_stack(yyParser *pParser){
|
||||
yyStackEntry *yytos;
|
||||
assert( pParser->yytos!=0 );
|
||||
yytos = pParser->yytos--;
|
||||
assert( pParser->yyidx>=0 );
|
||||
yytos = &pParser->yystack[pParser->yyidx--];
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sPopping %s\n",
|
||||
@@ -414,9 +397,9 @@ void ParseFree(
|
||||
#ifndef YYPARSEFREENEVERNULL
|
||||
if( pParser==0 ) return;
|
||||
#endif
|
||||
while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
|
||||
while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
|
||||
#if YYSTACKDEPTH<=0
|
||||
if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
|
||||
free(pParser->yystack);
|
||||
#endif
|
||||
(*freeProc)((void*)pParser);
|
||||
}
|
||||
@@ -427,7 +410,7 @@ void ParseFree(
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
int ParseStackPeak(void *p){
|
||||
yyParser *pParser = (yyParser*)p;
|
||||
return pParser->yyhwm;
|
||||
return pParser->yyidxMax;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -440,7 +423,7 @@ static unsigned int yy_find_shift_action(
|
||||
YYCODETYPE iLookAhead /* The look-ahead token */
|
||||
){
|
||||
int i;
|
||||
int stateno = pParser->yytos->stateno;
|
||||
int stateno = pParser->yystack[pParser->yyidx].stateno;
|
||||
|
||||
if( stateno>=YY_MIN_REDUCE ) return stateno;
|
||||
assert( stateno <= YY_SHIFT_COUNT );
|
||||
@@ -533,13 +516,13 @@ static int yy_find_reduce_action(
|
||||
*/
|
||||
static void yyStackOverflow(yyParser *yypParser){
|
||||
ParseARG_FETCH;
|
||||
yypParser->yytos--;
|
||||
yypParser->yyidx--;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
|
||||
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
|
||||
/* Here code is inserted which will execute if the parser
|
||||
** stack every overflows */
|
||||
/******** Begin %stack_overflow code ******************************************/
|
||||
@@ -556,11 +539,11 @@ static void yyTraceShift(yyParser *yypParser, int yyNewState){
|
||||
if( yyTraceFILE ){
|
||||
if( yyNewState<YYNSTATE ){
|
||||
fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
|
||||
yyTracePrompt,yyTokenName[yypParser->yytos->major],
|
||||
yyTracePrompt,yyTokenName[yypParser->yystack[yypParser->yyidx].major],
|
||||
yyNewState);
|
||||
}else{
|
||||
fprintf(yyTraceFILE,"%sShift '%s'\n",
|
||||
yyTracePrompt,yyTokenName[yypParser->yytos->major]);
|
||||
yyTracePrompt,yyTokenName[yypParser->yystack[yypParser->yyidx].major]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -578,27 +561,27 @@ static void yy_shift(
|
||||
ParseTOKENTYPE yyMinor /* The minor token to shift in */
|
||||
){
|
||||
yyStackEntry *yytos;
|
||||
yypParser->yytos++;
|
||||
yypParser->yyidx++;
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
|
||||
yypParser->yyhwm++;
|
||||
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
|
||||
if( yypParser->yyidx>yypParser->yyidxMax ){
|
||||
yypParser->yyidxMax = yypParser->yyidx;
|
||||
}
|
||||
#endif
|
||||
#if YYSTACKDEPTH>0
|
||||
if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH] ){
|
||||
if( yypParser->yyidx>=YYSTACKDEPTH ){
|
||||
yyStackOverflow(yypParser);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
|
||||
if( yyGrowStack(yypParser) ){
|
||||
if( yypParser->yyidx>=yypParser->yystksz ){
|
||||
yyGrowStack(yypParser);
|
||||
if( yypParser->yyidx>=yypParser->yystksz ){
|
||||
yyStackOverflow(yypParser);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
yytos = yypParser->yytos;
|
||||
yytos = &yypParser->yystack[yypParser->yyidx];
|
||||
yytos->stateno = (YYACTIONTYPE)yyNewState;
|
||||
yytos->major = (YYCODETYPE)yyMajor;
|
||||
yytos->minor.yy0 = yyMinor;
|
||||
@@ -630,7 +613,7 @@ static void yy_reduce(
|
||||
yyStackEntry *yymsp; /* The top of the parser's stack */
|
||||
int yysize; /* Amount to pop the stack */
|
||||
ParseARG_FETCH;
|
||||
yymsp = yypParser->yytos;
|
||||
yymsp = &yypParser->yystack[yypParser->yyidx];
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
|
||||
yysize = yyRuleInfo[yyruleno].nrhs;
|
||||
@@ -644,23 +627,22 @@ static void yy_reduce(
|
||||
** enough on the stack to push the LHS value */
|
||||
if( yyRuleInfo[yyruleno].nrhs==0 ){
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
|
||||
yypParser->yyhwm++;
|
||||
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
|
||||
if( yypParser->yyidx>yypParser->yyidxMax ){
|
||||
yypParser->yyidxMax = yypParser->yyidx;
|
||||
}
|
||||
#endif
|
||||
#if YYSTACKDEPTH>0
|
||||
if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH-1] ){
|
||||
if( yypParser->yyidx>=YYSTACKDEPTH-1 ){
|
||||
yyStackOverflow(yypParser);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
|
||||
if( yyGrowStack(yypParser) ){
|
||||
if( yypParser->yyidx>=yypParser->yystksz-1 ){
|
||||
yyGrowStack(yypParser);
|
||||
if( yypParser->yyidx>=yypParser->yystksz-1 ){
|
||||
yyStackOverflow(yypParser);
|
||||
return;
|
||||
}
|
||||
yymsp = yypParser->yytos;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -683,17 +665,15 @@ static void yy_reduce(
|
||||
yysize = yyRuleInfo[yyruleno].nrhs;
|
||||
yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
|
||||
if( yyact <= YY_MAX_SHIFTREDUCE ){
|
||||
if( yyact>YY_MAX_SHIFT ){
|
||||
yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
|
||||
}
|
||||
if( yyact>YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
|
||||
yypParser->yyidx -= yysize - 1;
|
||||
yymsp -= yysize-1;
|
||||
yypParser->yytos = yymsp;
|
||||
yymsp->stateno = (YYACTIONTYPE)yyact;
|
||||
yymsp->major = (YYCODETYPE)yygoto;
|
||||
yyTraceShift(yypParser, yyact);
|
||||
}else{
|
||||
assert( yyact == YY_ACCEPT_ACTION );
|
||||
yypParser->yytos -= yysize;
|
||||
yypParser->yyidx -= yysize;
|
||||
yy_accept(yypParser);
|
||||
}
|
||||
}
|
||||
@@ -711,7 +691,7 @@ static void yy_parse_failed(
|
||||
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
|
||||
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
|
||||
/* Here code is inserted which will be executed whenever the
|
||||
** parser fails */
|
||||
/************ Begin %parse_failure code ***************************************/
|
||||
@@ -749,7 +729,7 @@ static void yy_accept(
|
||||
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
assert( yypParser->yytos==yypParser->yystack );
|
||||
while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
|
||||
/* Here code is inserted which will be executed whenever the
|
||||
** parser accepts */
|
||||
/*********** Begin %parse_accept code *****************************************/
|
||||
@@ -795,7 +775,26 @@ void Parse(
|
||||
|
||||
/* (re)initialize the parser, if necessary */
|
||||
yypParser = (yyParser*)yyp;
|
||||
assert( yypParser->yytos!=0 );
|
||||
if( yypParser->yyidx<0 ){
|
||||
#if YYSTACKDEPTH<=0
|
||||
if( yypParser->yystksz <=0 ){
|
||||
yyStackOverflow(yypParser);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
yypParser->yyidx = 0;
|
||||
#ifndef YYNOERRORRECOVERY
|
||||
yypParser->yyerrcnt = -1;
|
||||
#endif
|
||||
yypParser->yystack[0].stateno = 0;
|
||||
yypParser->yystack[0].major = 0;
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sInitialize. Empty stack. State 0\n",
|
||||
yyTracePrompt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
|
||||
yyendofinput = (yymajor==0);
|
||||
#endif
|
||||
@@ -810,9 +809,7 @@ void Parse(
|
||||
do{
|
||||
yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
|
||||
if( yyact <= YY_MAX_SHIFTREDUCE ){
|
||||
if( yyact > YY_MAX_SHIFT ){
|
||||
yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
|
||||
}
|
||||
if( yyact > YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
|
||||
yy_shift(yypParser,yyact,yymajor,yyminor);
|
||||
#ifndef YYNOERRORRECOVERY
|
||||
yypParser->yyerrcnt--;
|
||||
@@ -854,7 +851,7 @@ void Parse(
|
||||
if( yypParser->yyerrcnt<0 ){
|
||||
yy_syntax_error(yypParser,yymajor,yyminor);
|
||||
}
|
||||
yymx = yypParser->yytos->major;
|
||||
yymx = yypParser->yystack[yypParser->yyidx].major;
|
||||
if( yymx==YYERRORSYMBOL || yyerrorhit ){
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
@@ -865,15 +862,16 @@ void Parse(
|
||||
yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
|
||||
yymajor = YYNOCODE;
|
||||
}else{
|
||||
while( yypParser->yytos >= &yypParser->yystack
|
||||
&& yymx != YYERRORSYMBOL
|
||||
&& (yyact = yy_find_reduce_action(
|
||||
yypParser->yytos->stateno,
|
||||
while(
|
||||
yypParser->yyidx >= 0 &&
|
||||
yymx != YYERRORSYMBOL &&
|
||||
(yyact = yy_find_reduce_action(
|
||||
yypParser->yystack[yypParser->yyidx].stateno,
|
||||
YYERRORSYMBOL)) >= YY_MIN_REDUCE
|
||||
){
|
||||
yy_pop_parser_stack(yypParser);
|
||||
}
|
||||
if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
|
||||
if( yypParser->yyidx < 0 || yymajor==0 ){
|
||||
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
|
||||
yy_parse_failed(yypParser);
|
||||
yymajor = YYNOCODE;
|
||||
@@ -916,16 +914,14 @@ void Parse(
|
||||
yymajor = YYNOCODE;
|
||||
#endif
|
||||
}
|
||||
}while( yymajor!=YYNOCODE && yypParser->yytos>yypParser->yystack );
|
||||
}while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
yyStackEntry *i;
|
||||
char cDiv = '[';
|
||||
int i;
|
||||
fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
|
||||
for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
|
||||
fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
|
||||
cDiv = ' ';
|
||||
}
|
||||
for(i=1; i<=yypParser->yyidx; i++)
|
||||
fprintf(yyTraceFILE,"%c%s", i==1 ? '[' : ' ',
|
||||
yyTokenName[yypParser->yystack[i].major]);
|
||||
fprintf(yyTraceFILE,"]\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
** Compile this program against an SQLite library of unknown version
|
||||
** and then run this program, and it will print out the SQLite version
|
||||
** information.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
extern const char *sqlite3_libversion(void);
|
||||
extern const char *sqlite3_sourceid(void);
|
||||
|
||||
int main(int argc, char **argv){
|
||||
printf("SQLite version %s\n", sqlite3_libversion());
|
||||
printf("SQLite source %s\n", sqlite3_sourceid());
|
||||
return 0;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/tclsh
|
||||
#
|
||||
# Run this script, redirecting input from cachegrind output, to compute the
|
||||
# number of CPU cycles used by each VDBE opcode.
|
||||
#
|
||||
# The cachegrind output should be configured so that it reports a single
|
||||
# column of Ir at the left margin. Ex:
|
||||
#
|
||||
# cg_annotation --show=Ir --auto=yes cachegrind.out.* | tclsh opcodesum.tcl
|
||||
#
|
||||
set currentop x
|
||||
set ncycle(x) 0
|
||||
while {![eof stdin]} {
|
||||
set line [string map {\173 x \175 x \042 x} [gets stdin]]
|
||||
if {[regexp { \. case OP_.*:} $line]} {
|
||||
regexp {OP_(.+):} $line all currentop
|
||||
set ncycle($currentop) 0
|
||||
} elseif {[lindex $line 1]=="default:"
|
||||
&& [regexp {really OP_Noop and OP_Explain} $line]} {
|
||||
break
|
||||
} elseif {[lindex $line 0]!="."} {
|
||||
regsub -all {[^0-9]} [lindex $line 0] {} n
|
||||
if {$n!=""} {incr ncycle($currentop) $n}
|
||||
}
|
||||
}
|
||||
unset ncycle(x)
|
||||
set results {}
|
||||
foreach op [lsort [array names ncycle]] {
|
||||
if {$ncycle($op)==0} continue
|
||||
lappend results [list $ncycle($op) $op]
|
||||
}
|
||||
foreach entry [lsort -index 0 -int -decr $results] {
|
||||
puts [format {%-16s %10d} [lindex $entry 1] [lindex $entry 0]]
|
||||
}
|
||||
+8
-13
@@ -152,7 +152,6 @@ set tabledef {CREATE TABLE space_used(
|
||||
name clob, -- Name of a table or index in the database file
|
||||
tblname clob, -- Name of associated table
|
||||
is_index boolean, -- TRUE if it is an index, false for a table
|
||||
is_without_rowid boolean, -- TRUE if WITHOUT ROWID table
|
||||
nentry int, -- Number of entries in the BTree
|
||||
leaf_entries int, -- Number of leaf entries
|
||||
depth int, -- Depth of the b-tree
|
||||
@@ -185,7 +184,7 @@ set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
|
||||
foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
|
||||
|
||||
set is_index [expr {$name!=$tblname}]
|
||||
set is_without_rowid [is_without_rowid $name]
|
||||
set idx_btree [expr {$is_index || [is_without_rowid $name]}]
|
||||
db eval {
|
||||
SELECT
|
||||
sum(ncell) AS nentry,
|
||||
@@ -236,7 +235,6 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
|
||||
$name,
|
||||
$tblname,
|
||||
$is_index,
|
||||
$is_without_rowid,
|
||||
$nentry,
|
||||
$leaf_entries,
|
||||
$depth,
|
||||
@@ -332,15 +330,12 @@ proc subreport {title where showFrag} {
|
||||
# following query returns exactly one row (because it is an aggregate).
|
||||
#
|
||||
# The results of the query are stored directly by SQLite into local
|
||||
# variables (i.e. $nentry, $payload etc.).
|
||||
# variables (i.e. $nentry, $nleaf etc.).
|
||||
#
|
||||
mem eval "
|
||||
SELECT
|
||||
int(sum(
|
||||
CASE WHEN (is_without_rowid OR is_index) THEN nentry
|
||||
ELSE leaf_entries
|
||||
END
|
||||
)) AS nentry,
|
||||
int(sum(nentry)) AS nentry,
|
||||
int(sum(leaf_entries)) AS nleaf,
|
||||
int(sum(payload)) AS payload,
|
||||
int(sum(ovfl_payload)) AS ovfl_payload,
|
||||
max(mx_payload) AS mx_payload,
|
||||
@@ -380,8 +375,8 @@ proc subreport {title where showFrag} {
|
||||
set storage [expr {$total_pages*$pageSize}]
|
||||
set payload_percent [percent $payload $storage {of storage consumed}]
|
||||
set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}]
|
||||
set avg_payload [divide $payload $nentry]
|
||||
set avg_unused [divide $total_unused $nentry]
|
||||
set avg_payload [divide $payload $nleaf]
|
||||
set avg_unused [divide $total_unused $nleaf]
|
||||
if {$int_pages>0} {
|
||||
# TODO: Is this formula correct?
|
||||
set nTab [mem eval "
|
||||
@@ -395,12 +390,12 @@ proc subreport {title where showFrag} {
|
||||
"]
|
||||
set avg_fanout [format %.2f $avg_fanout]
|
||||
}
|
||||
set ovfl_cnt_percent [percent $ovfl_cnt $nentry {of all entries}]
|
||||
set ovfl_cnt_percent [percent $ovfl_cnt $nleaf {of all entries}]
|
||||
|
||||
# Print out the sub-report statistics.
|
||||
#
|
||||
statline {Percentage of total database} $total_pages_percent
|
||||
statline {Number of entries} $nentry
|
||||
statline {Number of entries} $nleaf
|
||||
statline {Bytes of storage consumed} $storage
|
||||
if {$compressed_size!=$storage} {
|
||||
set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}]
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This is a template for a script used for day-to-day size and
|
||||
# performance monitoring of SQLite. Typical usage:
|
||||
#
|
||||
# sh run-speed-test.sh trunk # Baseline measurement of trunk
|
||||
# sh run-speed-test.sh x1 # Measure some experimental change
|
||||
# fossil test-diff --tk cout-trunk.txt cout-x1.txt # View chanages
|
||||
#
|
||||
# There are multiple output files, all with a base name given by
|
||||
# the first argument:
|
||||
#
|
||||
# summary-$BASE.txt # Copy of standard output
|
||||
# cout-$BASE.txt # cachegrind output
|
||||
# explain-$BASE.txt # EXPLAIN listings (only with --explain)
|
||||
#
|
||||
if test "$1" = ""
|
||||
then
|
||||
echo "Usage: $0 OUTPUTFILE [OPTIONS]"
|
||||
exit
|
||||
fi
|
||||
NAME=$1
|
||||
shift
|
||||
CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5"
|
||||
SPEEDTEST_OPTS="--shrink-memory --reprepare --heap 10000000 64"
|
||||
SIZE=5
|
||||
doExplain=0
|
||||
doCachegrind=1
|
||||
while test "$1" != ""; do
|
||||
case $1 in
|
||||
--reprepare)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
|
||||
;;
|
||||
--autovacuum)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
|
||||
;;
|
||||
--utf16be)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
|
||||
;;
|
||||
--stats)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
|
||||
;;
|
||||
--without-rowid)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
|
||||
;;
|
||||
--nomemstat)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
|
||||
;;
|
||||
--temp)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --temp 6"
|
||||
;;
|
||||
--wal)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --journal wal"
|
||||
;;
|
||||
--size)
|
||||
shift; SIZE=$1
|
||||
;;
|
||||
--explain)
|
||||
doExplain=1
|
||||
;;
|
||||
--vdbeprofile)
|
||||
rm -f vdbe_profile.out
|
||||
CC_OPTS="$CC_OPTS -DVDBE_PROFILE"
|
||||
doCachegrind=0
|
||||
;;
|
||||
--heap)
|
||||
CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_MEMSYS5"
|
||||
shift;
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --heap $1 64"
|
||||
;;
|
||||
*)
|
||||
CC_OPTS="$CC_OPTS $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --size $SIZE"
|
||||
echo "NAME = $NAME" | tee summary-$NAME.txt
|
||||
echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt
|
||||
echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt
|
||||
rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o
|
||||
gcc -g -Os -Wall -I. $CC_OPTS -c sqlite3.c
|
||||
size sqlite3.o | tee -a summary-$NAME.txt
|
||||
if test $doExplain -eq 1; then
|
||||
gcc -g -Os -Wall -I. $CC_OPTS \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
./shell.c ./sqlite3.c -o sqlite3 -ldl -lpthread
|
||||
fi
|
||||
SRC=./speedtest1.c
|
||||
gcc -g -Os -Wall -I. $CC_OPTS $SRC ./sqlite3.o -o speedtest1 -ldl -lpthread
|
||||
ls -l speedtest1 | tee -a summary-$NAME.txt
|
||||
if test $doCachegrind -eq 1; then
|
||||
valgrind --tool=cachegrind ./speedtest1 speedtest1.db \
|
||||
$SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt
|
||||
else
|
||||
./speedtest1 speedtest1.db $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt
|
||||
fi
|
||||
size sqlite3.o | tee -a summary-$NAME.txt
|
||||
wc sqlite3.c
|
||||
if test $doCachegrind -eq 1; then
|
||||
cg_anno.tcl cachegrind.out.* >cout-$NAME.txt
|
||||
fi
|
||||
if test $doExplain -eq 1; then
|
||||
./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt
|
||||
fi
|
||||
Reference in New Issue
Block a user