track 3.6.13
This commit is contained in:
parent
6e36436ed9
commit
81f606f59f
17
Makefile.in
17
Makefile.in
|
@ -163,14 +163,14 @@ NAWK = @AWK@
|
|||
|
||||
# Object files for the SQLite library (non-amalgamation).
|
||||
#
|
||||
OBJS0 = alter.lo analyze.lo attach.lo auth.lo bitvec.lo btmutex.lo \
|
||||
OBJS0 = alter.lo analyze.lo attach.lo auth.lo backup.lo bitvec.lo btmutex.lo \
|
||||
btree.lo build.lo callback.lo complete.lo date.lo \
|
||||
delete.lo expr.lo fault.lo func.lo global.lo \
|
||||
hash.lo journal.lo insert.lo legacy.lo loadext.lo \
|
||||
main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \
|
||||
memjournal.lo \
|
||||
mutex.lo mutex_noop.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \
|
||||
opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \
|
||||
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \
|
||||
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||
random.lo resolve.lo rowset.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo update.lo \
|
||||
|
@ -231,6 +231,7 @@ SRC = \
|
|||
$(TOP)/src/mutex_os2.c \
|
||||
$(TOP)/src/mutex_unix.c \
|
||||
$(TOP)/src/mutex_w32.c \
|
||||
$(TOP)/src/notify.c \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
|
@ -518,6 +519,9 @@ attach.lo: $(TOP)/src/attach.c $(HDR)
|
|||
auth.lo: $(TOP)/src/auth.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/auth.c
|
||||
|
||||
backup.lo: $(TOP)/src/backup.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/backup.c
|
||||
|
||||
bitvec.lo: $(TOP)/src/bitvec.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/bitvec.c
|
||||
|
||||
|
@ -608,6 +612,9 @@ mutex_unix.lo: $(TOP)/src/mutex_unix.c $(HDR)
|
|||
mutex_w32.lo: $(TOP)/src/mutex_w32.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/mutex_w32.c
|
||||
|
||||
notify.lo: $(TOP)/src/notify.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/notify.c
|
||||
|
||||
pager.lo: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/pager.c
|
||||
|
||||
|
@ -765,9 +772,11 @@ sqlite3_analyzer$(TEXE): $(TESTFIXTURE_SRC) $(TOP)/tool/spaceanal.tcl
|
|||
$(TEMP_STORE) -o $@ $(TESTFIXTURE_SRC) $(LIBTCL)
|
||||
|
||||
|
||||
install: sqlite3$(BEXE) libsqlite3.la sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install}
|
||||
lib_install: libsqlite3.la
|
||||
$(INSTALL) -d $(DESTDIR)$(libdir)
|
||||
$(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir)
|
||||
|
||||
install: sqlite3$(BEXE) lib_install sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install}
|
||||
$(INSTALL) -d $(DESTDIR)$(bindir)
|
||||
$(LTINSTALL) sqlite3$(BEXE) $(DESTDIR)$(bindir)
|
||||
$(INSTALL) -d $(DESTDIR)$(includedir)
|
||||
|
@ -778,7 +787,7 @@ install: sqlite3$(BEXE) libsqlite3.la sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_inst
|
|||
|
||||
pkgIndex.tcl:
|
||||
echo 'package ifneeded sqlite3 $(RELEASE) [list load $(TCLLIBDIR)/libtclsqlite3.so sqlite3]' > $@
|
||||
tcl_install: libtclsqlite3.la pkgIndex.tcl
|
||||
tcl_install: lib_install libtclsqlite3.la pkgIndex.tcl
|
||||
$(INSTALL) -d $(DESTDIR)$(TCLLIBDIR)
|
||||
$(LTINSTALL) libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)
|
||||
rm -f $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.a
|
||||
|
|
|
@ -13,7 +13,7 @@ include $(WIND_USR)/tool/gnu/make.$(FORCPU)
|
|||
#### The toplevel directory of the source tree. This is the directory
|
||||
# that contains this "Makefile.in" and the "configure.in" script.
|
||||
#
|
||||
TOP = ../sqlite-3.6.5
|
||||
TOP = .
|
||||
|
||||
#### C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
|
@ -42,13 +42,14 @@ THREADLIB =
|
|||
|
||||
#### Specify any extra libraries needed to access required functions.
|
||||
#
|
||||
#TLIBS = -lrt # fdatasync on Solaris 8
|
||||
#for x86 vxWorks
|
||||
#TLIBS += TLIBS = $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS)
|
||||
#for SH4 shared library
|
||||
TLIBS_SHARED += -L$(WIND_USR)/lib/sh/SH32/commonle/PIC
|
||||
#TLIBS_SHARED += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS)
|
||||
#for SH4 static
|
||||
ifeq ($(CPU),SH32)
|
||||
# for SH4 shared library
|
||||
TLIBS_SHARED += -L$(WIND_USR)/lib/sh/SH32/commonle/PIC
|
||||
else
|
||||
# for all other CPUs shared library
|
||||
TLIBS_SHARED += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS)
|
||||
endif
|
||||
# for static library
|
||||
TLIBS += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS)
|
||||
|
||||
#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1
|
||||
|
@ -193,18 +194,18 @@ TCCX_SHARED = $(TCC_SHARED) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -I$(TOP)/ext/rtree
|
|||
# Object files for the SQLite library.
|
||||
#
|
||||
LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
||||
bitvec.o btmutex.o btree.o build.o \
|
||||
backup.o bitvec.o btmutex.o btree.o build.o \
|
||||
callback.o complete.o date.o delete.o \
|
||||
expr.o fault.o func.o global.o hash.o \
|
||||
icu.o insert.o journal.o legacy.o loadext.o \
|
||||
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o memjournal.o \
|
||||
mutex.o mutex_os2.o mutex_unix.o mutex_w32.o mutex_noop.o \
|
||||
opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||
pager.o parse.o pcache.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rtree.o select.o status.o \
|
||||
pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
update.o util.o vacuum.o \
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbefifo.o vdbemem.o \
|
||||
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o \
|
||||
walker.o where.o utf.o vtab.o
|
||||
|
||||
|
||||
|
@ -215,6 +216,7 @@ SRC = \
|
|||
$(TOP)/src/analyze.c \
|
||||
$(TOP)/src/attach.c \
|
||||
$(TOP)/src/auth.c \
|
||||
$(TOP)/src/backup.c \
|
||||
$(TOP)/src/bitvec.c \
|
||||
$(TOP)/src/btmutex.c \
|
||||
$(TOP)/src/btree.c \
|
||||
|
@ -260,12 +262,14 @@ SRC = \
|
|||
$(TOP)/src/pager.h \
|
||||
$(TOP)/src/parse.y \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/pcache.h \
|
||||
$(TOP)/src/pragma.c \
|
||||
$(TOP)/src/prepare.c \
|
||||
$(TOP)/src/printf.c \
|
||||
$(TOP)/src/random.c \
|
||||
$(TOP)/src/resolve.c \
|
||||
$(TOP)/src/rowset.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/status.c \
|
||||
$(TOP)/src/shell.c \
|
||||
|
@ -286,7 +290,6 @@ SRC = \
|
|||
$(TOP)/src/vdbeapi.c \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbeblob.c \
|
||||
$(TOP)/src/vdbefifo.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbeInt.h \
|
||||
$(TOP)/src/vtab.c \
|
||||
|
@ -316,6 +319,8 @@ SRC += \
|
|||
SRC += \
|
||||
$(TOP)/ext/fts3/fts3.c \
|
||||
$(TOP)/ext/fts3/fts3.h \
|
||||
$(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_expr.h \
|
||||
$(TOP)/ext/fts3/fts3_hash.c \
|
||||
$(TOP)/ext/fts3/fts3_hash.h \
|
||||
$(TOP)/ext/fts3/fts3_icu.c \
|
11
configure.ac
11
configure.ac
|
@ -89,10 +89,19 @@
|
|||
#
|
||||
AC_INIT(sqlite, m4_esyscmd([cat VERSION | tr -d '\n']))
|
||||
|
||||
dnl Make sure the local VERSION file matches this configure script
|
||||
sqlite_version_sanity_check=`cat VERSION | tr -d '\n'`
|
||||
if test "$PACKAGE_VERSION" != "$sqlite_version_sanity_check" ; then
|
||||
AC_MSG_ERROR([configure script is out of date:
|
||||
configure \$PACKAGE_VERSION = $PACKAGE_VERSION
|
||||
top level VERSION file = $sqlite_version_sanity_check
|
||||
please regen with autoconf])
|
||||
fi
|
||||
|
||||
dnl Put the RCS revision string after AC_INIT so that it will also
|
||||
dnl show in in configure.
|
||||
# The following RCS revision string applies to configure.in
|
||||
# $Revision: 1.54 $
|
||||
# $Revision: 1.55 $
|
||||
|
||||
#########
|
||||
# Programs needed
|
||||
|
|
|
@ -1214,7 +1214,7 @@ static int sql_step_statement(fulltext_vtab *v, fulltext_statement iStmt,
|
|||
if( rc==SQLITE_BUSY ) continue;
|
||||
if( rc!=SQLITE_ERROR ) return rc;
|
||||
|
||||
/* If an SQLITE_SCHEMA error has occured, then finalizing this
|
||||
/* If an SQLITE_SCHEMA error has occurred, then finalizing this
|
||||
* statement is going to delete the fulltext_vtab structure. If
|
||||
* the statement just executed is in the pFulltextStatements[]
|
||||
* array, it will be finalized twice. So remove it before
|
||||
|
|
|
@ -6249,7 +6249,7 @@ static void optimizeFunc(sqlite3_context *pContext,
|
|||
i++;
|
||||
}
|
||||
|
||||
/* If we managed to succesfully read them all, optimize them. */
|
||||
/* If we managed to successfully read them all, optimize them. */
|
||||
if( rc==SQLITE_DONE ){
|
||||
assert( i==nReaders );
|
||||
rc = optimizeInternal(v, readers, nReaders, &writer);
|
||||
|
@ -6836,7 +6836,7 @@ int sqlite3Fts2Init(sqlite3 *db){
|
|||
);
|
||||
}
|
||||
|
||||
/* An error has occured. Delete the hash table and return the error code. */
|
||||
/* An error has occurred. Delete the hash table and return the error code. */
|
||||
assert( rc!=SQLITE_OK );
|
||||
if( pHash ){
|
||||
sqlite3Fts2HashClear(pHash);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6410,7 +6410,7 @@ static void optimizeFunc(sqlite3_context *pContext,
|
|||
i++;
|
||||
}
|
||||
|
||||
/* If we managed to succesfully read them all, optimize them. */
|
||||
/* If we managed to successfully read them all, optimize them. */
|
||||
if( rc==SQLITE_DONE ){
|
||||
assert( i==nReaders );
|
||||
rc = optimizeInternal(v, readers, nReaders, &writer);
|
||||
|
@ -7001,7 +7001,7 @@ int sqlite3Fts3Init(sqlite3 *db){
|
|||
);
|
||||
}
|
||||
|
||||
/* An error has occured. Delete the hash table and return the error code. */
|
||||
/* An error has occurred. Delete the hash table and return the error code. */
|
||||
assert( rc!=SQLITE_OK );
|
||||
if( pHash ){
|
||||
sqlite3Fts3HashClear(pHash);
|
||||
|
|
|
@ -212,8 +212,6 @@ static int getNextString(
|
|||
if( ii==0 ){
|
||||
memset(p, 0, nByte);
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
p->eType = FTSQUERY_PHRASE;
|
||||
p->pPhrase->iColumn = pParse->iDefaultCol;
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
p->pPhrase->nToken = ii+1;
|
||||
|
@ -237,19 +235,25 @@ static int getNextString(
|
|||
char *zNew;
|
||||
int nNew = 0;
|
||||
int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
|
||||
nByte += (p->pPhrase->nToken-1) * sizeof(struct PhraseToken);
|
||||
nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken);
|
||||
p = fts3ReallocOrFree(p, nByte + nTemp);
|
||||
if( !p ){
|
||||
goto no_mem;
|
||||
}
|
||||
if( zTemp ){
|
||||
zNew = &(((char *)p)[nByte]);
|
||||
memcpy(zNew, zTemp, nTemp);
|
||||
}else{
|
||||
memset(p, 0, nByte+nTemp);
|
||||
}
|
||||
p->pPhrase = (Fts3Phrase *)&p[1];
|
||||
zNew = &(((char *)p)[nByte]);
|
||||
memcpy(zNew, zTemp, nTemp);
|
||||
for(jj=0; jj<p->pPhrase->nToken; jj++){
|
||||
p->pPhrase->aToken[jj].z = &zNew[nNew];
|
||||
nNew += p->pPhrase->aToken[jj].n;
|
||||
}
|
||||
sqlite3_free(zTemp);
|
||||
p->eType = FTSQUERY_PHRASE;
|
||||
p->pPhrase->iColumn = pParse->iDefaultCol;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
|
|
3
main.mk
3
main.mk
|
@ -59,7 +59,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
|||
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \
|
||||
memjournal.o \
|
||||
mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \
|
||||
opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||
notify.o opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||
pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
|
@ -112,6 +112,7 @@ SRC = \
|
|||
$(TOP)/src/mutex_os2.c \
|
||||
$(TOP)/src/mutex_unix.c \
|
||||
$(TOP)/src/mutex_w32.c \
|
||||
$(TOP)/src/notify.c \
|
||||
$(TOP)/src/os.c \
|
||||
$(TOP)/src/os.h \
|
||||
$(TOP)/src/os_common.h \
|
||||
|
|
150
sqlite3.def
150
sqlite3.def
|
@ -1,150 +0,0 @@
|
|||
EXPORTS
|
||||
sqlite3_aggregate_context
|
||||
sqlite3_aggregate_count
|
||||
sqlite3_auto_extension
|
||||
sqlite3_bind_blob
|
||||
sqlite3_bind_double
|
||||
sqlite3_bind_int
|
||||
sqlite3_bind_int64
|
||||
sqlite3_bind_null
|
||||
sqlite3_bind_parameter_count
|
||||
sqlite3_bind_parameter_index
|
||||
sqlite3_bind_parameter_name
|
||||
sqlite3_bind_text
|
||||
sqlite3_bind_text16
|
||||
sqlite3_bind_value
|
||||
sqlite3_bind_zeroblob
|
||||
sqlite3_blob_bytes
|
||||
sqlite3_blob_close
|
||||
sqlite3_blob_open
|
||||
sqlite3_blob_read
|
||||
sqlite3_blob_write
|
||||
sqlite3_busy_handler
|
||||
sqlite3_busy_timeout
|
||||
sqlite3_changes
|
||||
sqlite3_clear_bindings
|
||||
sqlite3_close
|
||||
sqlite3_collation_needed
|
||||
sqlite3_collation_needed16
|
||||
sqlite3_column_blob
|
||||
sqlite3_column_bytes
|
||||
sqlite3_column_bytes16
|
||||
sqlite3_column_count
|
||||
sqlite3_column_decltype
|
||||
sqlite3_column_decltype16
|
||||
sqlite3_column_double
|
||||
sqlite3_column_int
|
||||
sqlite3_column_int64
|
||||
sqlite3_column_name
|
||||
sqlite3_column_name16
|
||||
sqlite3_column_text
|
||||
sqlite3_column_text16
|
||||
sqlite3_column_type
|
||||
sqlite3_column_value
|
||||
sqlite3_commit_hook
|
||||
sqlite3_complete
|
||||
sqlite3_complete16
|
||||
sqlite3_create_collation
|
||||
sqlite3_create_collation16
|
||||
sqlite3_create_collation_v2
|
||||
sqlite3_create_function
|
||||
sqlite3_create_function16
|
||||
sqlite3_create_module
|
||||
sqlite3_create_module_v2
|
||||
sqlite3_data_count
|
||||
sqlite3_db_handle
|
||||
sqlite3_declare_vtab
|
||||
sqlite3_enable_load_extension
|
||||
sqlite3_enable_shared_cache
|
||||
sqlite3_errcode
|
||||
sqlite3_errmsg
|
||||
sqlite3_errmsg16
|
||||
sqlite3_exec
|
||||
sqlite3_expired
|
||||
sqlite3_extended_result_codes
|
||||
sqlite3_file_control
|
||||
sqlite3_finalize
|
||||
sqlite3_free
|
||||
sqlite3_free_table
|
||||
sqlite3_get_autocommit
|
||||
sqlite3_get_auxdata
|
||||
sqlite3_get_table
|
||||
sqlite3_global_recover
|
||||
sqlite3_interrupt
|
||||
sqlite3_last_insert_rowid
|
||||
sqlite3_libversion
|
||||
sqlite3_libversion_number
|
||||
sqlite3_load_extension
|
||||
sqlite3_malloc
|
||||
sqlite3_memory_alarm
|
||||
sqlite3_memory_highwater
|
||||
sqlite3_memory_used
|
||||
sqlite3_mprintf
|
||||
sqlite3_mutex_alloc
|
||||
sqlite3_mutex_enter
|
||||
sqlite3_mutex_free
|
||||
sqlite3_mutex_leave
|
||||
sqlite3_mutex_try
|
||||
sqlite3_open
|
||||
sqlite3_open16
|
||||
sqlite3_open_v2
|
||||
sqlite3_overload_function
|
||||
sqlite3_prepare
|
||||
sqlite3_prepare16
|
||||
sqlite3_prepare16_v2
|
||||
sqlite3_prepare_v2
|
||||
sqlite3_profile
|
||||
sqlite3_progress_handler
|
||||
sqlite3_randomness
|
||||
sqlite3_realloc
|
||||
sqlite3_release_memory
|
||||
sqlite3_reset
|
||||
sqlite3_reset_auto_extension
|
||||
sqlite3_result_blob
|
||||
sqlite3_result_double
|
||||
sqlite3_result_error
|
||||
sqlite3_result_error16
|
||||
sqlite3_result_error_code
|
||||
sqlite3_result_error_nomem
|
||||
sqlite3_result_error_toobig
|
||||
sqlite3_result_int
|
||||
sqlite3_result_int64
|
||||
sqlite3_result_null
|
||||
sqlite3_result_text
|
||||
sqlite3_result_text16
|
||||
sqlite3_result_text16be
|
||||
sqlite3_result_text16le
|
||||
sqlite3_result_value
|
||||
sqlite3_result_zeroblob
|
||||
sqlite3_rollback_hook
|
||||
sqlite3_set_authorizer
|
||||
sqlite3_set_auxdata
|
||||
sqlite3_sleep
|
||||
sqlite3_snprintf
|
||||
sqlite3_soft_heap_limit
|
||||
sqlite3_sql
|
||||
sqlite3_step
|
||||
sqlite3_test_control
|
||||
sqlite3_thread_cleanup
|
||||
sqlite3_threadsafe
|
||||
sqlite3_total_changes
|
||||
sqlite3_trace
|
||||
sqlite3_transfer_bindings
|
||||
sqlite3_update_hook
|
||||
sqlite3_user_data
|
||||
sqlite3_value_blob
|
||||
sqlite3_value_bytes
|
||||
sqlite3_value_bytes16
|
||||
sqlite3_value_double
|
||||
sqlite3_value_int
|
||||
sqlite3_value_int64
|
||||
sqlite3_value_numeric_type
|
||||
sqlite3_value_text
|
||||
sqlite3_value_text16
|
||||
sqlite3_value_text16be
|
||||
sqlite3_value_text16le
|
||||
sqlite3_value_type
|
||||
sqlite3_vfs_find
|
||||
sqlite3_vfs_register
|
||||
sqlite3_vfs_unregister
|
||||
sqlite3_vmprintf
|
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that used to generate VDBE code
|
||||
** that implements the ALTER TABLE command.
|
||||
**
|
||||
** $Id: alter.c,v 1.53 2009/02/13 03:43:32 drh Exp $
|
||||
** $Id: alter.c,v 1.55 2009/03/24 15:08:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -193,7 +193,7 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
|
|||
*/
|
||||
if( pTab->pSchema!=pTempSchema ){
|
||||
sqlite3 *db = pParse->db;
|
||||
for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
|
||||
for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
|
||||
if( pTrig->pSchema==pTempSchema ){
|
||||
if( !zWhere ){
|
||||
zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name);
|
||||
|
@ -232,7 +232,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
|
|||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
/* Drop any table triggers from the internal schema. */
|
||||
for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){
|
||||
for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
|
||||
int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
|
||||
assert( iTrigDb==iDb || iTrigDb==1 );
|
||||
sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0);
|
||||
|
@ -593,7 +593,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
|||
if( !pNew ) goto exit_begin_add_column;
|
||||
pParse->pNewTable = pNew;
|
||||
pNew->nRef = 1;
|
||||
pNew->db = db;
|
||||
pNew->dbMem = pTab->dbMem;
|
||||
pNew->nCol = pTab->nCol;
|
||||
assert( pNew->nCol>0 );
|
||||
nAlloc = (((pNew->nCol-1)/8)*8)+8;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** This file contains code associated with the ANALYZE command.
|
||||
**
|
||||
** @(#) $Id: analyze.c,v 1.48 2009/02/13 16:59:53 drh Exp $
|
||||
** @(#) $Id: analyze.c,v 1.51 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
#include "sqliteInt.h"
|
||||
|
@ -74,8 +74,8 @@ static void openStatTable(
|
|||
if( !createStat1 ){
|
||||
sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1");
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 3);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
|
||||
sqlite3VdbeChangeP5(v, createStat1);
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ static void analyzeOneTable(
|
|||
/* Establish a read-lock on the table at the shared-cache level. */
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
iIdxCur = pParse->nTab;
|
||||
iIdxCur = pParse->nTab++;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
int regFields; /* Register block for building records */
|
||||
|
@ -131,7 +131,6 @@ static void analyzeOneTable(
|
|||
*/
|
||||
assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
|
||||
nCol = pIdx->nColumn;
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nCol+1);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
|
||||
(char *)pKey, P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
|
@ -189,7 +188,7 @@ static void analyzeOneTable(
|
|||
** The result is a single row of the sqlite_stat1 table. The first
|
||||
** two columns are the names of the table and index. The third column
|
||||
** is a string composed of a list of integer statistics about the
|
||||
** index. The first integer in the list is the total number of entires
|
||||
** index. The first integer in the list is the total number of entries
|
||||
** in the index. There is one additional integer in the list for each
|
||||
** column of the table. This additional integer is a guess of how many
|
||||
** rows of the table the index will select. If D is the count of distinct
|
||||
|
|
12
src/attach.c
12
src/attach.c
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** This file contains code used to implement the ATTACH and DETACH commands.
|
||||
**
|
||||
** $Id: attach.c,v 1.82 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** $Id: attach.c,v 1.84 2009/04/08 13:51:51 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -488,11 +488,11 @@ int sqlite3FixExpr(
|
|||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pExpr->pList) ){
|
||||
return 1;
|
||||
if( ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_SpanToken) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
|
||||
}else{
|
||||
if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pExpr->pRight) ){
|
||||
return 1;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
** This file contains the implementation of the sqlite3_backup_XXX()
|
||||
** API functions and the related features.
|
||||
**
|
||||
** $Id: backup.c,v 1.12 2009/02/16 17:55:47 shane Exp $
|
||||
** $Id: backup.c,v 1.13 2009/03/16 13:19:36 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
|
@ -292,10 +292,10 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
|
|||
int bCloseTrans = 0; /* True if src db requires unlocking */
|
||||
|
||||
/* If the source pager is currently in a write-transaction, return
|
||||
** SQLITE_LOCKED immediately.
|
||||
** SQLITE_BUSY immediately.
|
||||
*/
|
||||
if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
|
||||
rc = SQLITE_LOCKED;
|
||||
rc = SQLITE_BUSY;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
|
10
src/bitvec.c
10
src/bitvec.c
|
@ -34,7 +34,7 @@
|
|||
** start of a transaction, and is thus usually less than a few thousand,
|
||||
** but can be as large as 2 billion for a really big database.
|
||||
**
|
||||
** @(#) $Id: bitvec.c,v 1.13 2009/01/20 17:06:27 danielk1977 Exp $
|
||||
** @(#) $Id: bitvec.c,v 1.14 2009/04/01 23:49:04 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -94,8 +94,9 @@
|
|||
*/
|
||||
struct Bitvec {
|
||||
u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */
|
||||
u32 nSet; /* Number of bits that are set - only valid for aHash element */
|
||||
/* Max nSet is BITVEC_NINT. For BITVEC_SZ of 512, this would be 125. */
|
||||
u32 nSet; /* Number of bits that are set - only valid for aHash
|
||||
** element. Max is BITVEC_NINT. For BITVEC_SZ of 512,
|
||||
** this would be 125. */
|
||||
u32 iDivisor; /* Number of bits handled by each apSub[] entry. */
|
||||
/* Should >=0 for apSub element. */
|
||||
/* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */
|
||||
|
@ -377,7 +378,8 @@ int sqlite3BitvecBuiltinTest(int sz, int *aOp){
|
|||
** is found.
|
||||
*/
|
||||
rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1)
|
||||
+ sqlite3BitvecTest(pBitvec, 0);
|
||||
+ sqlite3BitvecTest(pBitvec, 0)
|
||||
+ (sqlite3BitvecSize(pBitvec) - sz);
|
||||
for(i=1; i<=sz; i++){
|
||||
if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){
|
||||
rc = i;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: btmutex.c,v 1.12 2008/11/17 19:18:55 danielk1977 Exp $
|
||||
** $Id: btmutex.c,v 1.15 2009/04/10 12:55:17 danielk1977 Exp $
|
||||
**
|
||||
** This file contains code used to implement mutexes on Btree objects.
|
||||
** This code really belongs in btree.c. But btree.c is getting too
|
||||
|
@ -18,8 +18,37 @@
|
|||
** a good breakout.
|
||||
*/
|
||||
#include "btreeInt.h"
|
||||
#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
#if SQLITE_THREADSAFE
|
||||
|
||||
/*
|
||||
** Obtain the BtShared mutex associated with B-Tree handle p. Also,
|
||||
** set BtShared.db to the database handle associated with p and the
|
||||
** p->locked boolean to true.
|
||||
*/
|
||||
static void lockBtreeMutex(Btree *p){
|
||||
assert( p->locked==0 );
|
||||
assert( sqlite3_mutex_notheld(p->pBt->mutex) );
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
sqlite3_mutex_enter(p->pBt->mutex);
|
||||
p->pBt->db = p->db;
|
||||
p->locked = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release the BtShared mutex associated with B-Tree handle p and
|
||||
** clear the p->locked boolean.
|
||||
*/
|
||||
static void unlockBtreeMutex(Btree *p){
|
||||
assert( p->locked==1 );
|
||||
assert( sqlite3_mutex_held(p->pBt->mutex) );
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
assert( p->db==p->pBt->db );
|
||||
|
||||
sqlite3_mutex_leave(p->pBt->mutex);
|
||||
p->locked = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Enter a mutex on the given BTree object.
|
||||
|
@ -57,6 +86,10 @@ void sqlite3BtreeEnter(Btree *p){
|
|||
/* We should already hold a lock on the database connection */
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
/* Unless the database is sharable and unlocked, then BtShared.db
|
||||
** should already be set correctly. */
|
||||
assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db );
|
||||
|
||||
if( !p->sharable ) return;
|
||||
p->wantToLock++;
|
||||
if( p->locked ) return;
|
||||
|
@ -66,6 +99,7 @@ void sqlite3BtreeEnter(Btree *p){
|
|||
** procedure that follows. Just be sure not to block.
|
||||
*/
|
||||
if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
|
||||
p->pBt->db = p->db;
|
||||
p->locked = 1;
|
||||
return;
|
||||
}
|
||||
|
@ -80,16 +114,13 @@ void sqlite3BtreeEnter(Btree *p){
|
|||
assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
|
||||
assert( !pLater->locked || pLater->wantToLock>0 );
|
||||
if( pLater->locked ){
|
||||
sqlite3_mutex_leave(pLater->pBt->mutex);
|
||||
pLater->locked = 0;
|
||||
unlockBtreeMutex(pLater);
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_enter(p->pBt->mutex);
|
||||
p->locked = 1;
|
||||
lockBtreeMutex(p);
|
||||
for(pLater=p->pNext; pLater; pLater=pLater->pNext){
|
||||
if( pLater->wantToLock ){
|
||||
sqlite3_mutex_enter(pLater->pBt->mutex);
|
||||
pLater->locked = 1;
|
||||
lockBtreeMutex(pLater);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,25 +133,25 @@ void sqlite3BtreeLeave(Btree *p){
|
|||
assert( p->wantToLock>0 );
|
||||
p->wantToLock--;
|
||||
if( p->wantToLock==0 ){
|
||||
assert( p->locked );
|
||||
sqlite3_mutex_leave(p->pBt->mutex);
|
||||
p->locked = 0;
|
||||
unlockBtreeMutex(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
/*
|
||||
** Return true if the BtShared mutex is held on the btree.
|
||||
**
|
||||
** This routine makes no determination one why or another if the
|
||||
** database connection mutex is held.
|
||||
** Return true if the BtShared mutex is held on the btree, or if the
|
||||
** B-Tree is not marked as sharable.
|
||||
**
|
||||
** This routine is used only from within assert() statements.
|
||||
*/
|
||||
int sqlite3BtreeHoldsMutex(Btree *p){
|
||||
return (p->sharable==0 ||
|
||||
(p->locked && p->wantToLock && sqlite3_mutex_held(p->pBt->mutex)));
|
||||
assert( p->sharable==0 || p->locked==0 || p->wantToLock>0 );
|
||||
assert( p->sharable==0 || p->locked==0 || p->db==p->pBt->db );
|
||||
assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->pBt->mutex) );
|
||||
assert( p->sharable==0 || p->locked==0 || sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
return (p->sharable==0 || p->locked);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -160,6 +191,7 @@ void sqlite3BtreeEnterAll(sqlite3 *db){
|
|||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
for(i=0; i<db->nDb; i++){
|
||||
p = db->aDb[i].pBt;
|
||||
assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db );
|
||||
if( p && p->sharable ){
|
||||
p->wantToLock++;
|
||||
if( !p->locked ){
|
||||
|
@ -168,13 +200,11 @@ void sqlite3BtreeEnterAll(sqlite3 *db){
|
|||
while( p->locked && p->pNext ) p = p->pNext;
|
||||
for(pLater = p->pNext; pLater; pLater=pLater->pNext){
|
||||
if( pLater->locked ){
|
||||
sqlite3_mutex_leave(pLater->pBt->mutex);
|
||||
pLater->locked = 0;
|
||||
unlockBtreeMutex(pLater);
|
||||
}
|
||||
}
|
||||
while( p ){
|
||||
sqlite3_mutex_enter(p->pBt->mutex);
|
||||
p->locked++;
|
||||
lockBtreeMutex(p);
|
||||
p = p->pNext;
|
||||
}
|
||||
}
|
||||
|
@ -191,9 +221,7 @@ void sqlite3BtreeLeaveAll(sqlite3 *db){
|
|||
assert( p->wantToLock>0 );
|
||||
p->wantToLock--;
|
||||
if( p->wantToLock==0 ){
|
||||
assert( p->locked );
|
||||
sqlite3_mutex_leave(p->pBt->mutex);
|
||||
p->locked = 0;
|
||||
unlockBtreeMutex(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,8 +310,7 @@ void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
|
|||
|
||||
p->wantToLock++;
|
||||
if( !p->locked && p->sharable ){
|
||||
sqlite3_mutex_enter(p->pBt->mutex);
|
||||
p->locked = 1;
|
||||
lockBtreeMutex(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -305,11 +332,23 @@ void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
|
|||
|
||||
p->wantToLock--;
|
||||
if( p->wantToLock==0 && p->locked ){
|
||||
sqlite3_mutex_leave(p->pBt->mutex);
|
||||
p->locked = 0;
|
||||
unlockBtreeMutex(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* SQLITE_THREADSAFE && !SQLITE_OMIT_SHARED_CACHE */
|
||||
#else
|
||||
void sqlite3BtreeEnter(Btree *p){
|
||||
p->pBt->db = p->db;
|
||||
}
|
||||
void sqlite3BtreeEnterAll(sqlite3 *db){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *p = db->aDb[i].pBt;
|
||||
if( p ){
|
||||
p->pBt->db = p->db;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* if SQLITE_THREADSAFE */
|
||||
#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */
|
||||
|
|
660
src/btree.c
660
src/btree.c
File diff suppressed because it is too large
Load Diff
52
src/btree.h
52
src/btree.h
|
@ -13,7 +13,7 @@
|
|||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.108 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** @(#) $Id: btree.h,v 1.113 2009/04/10 12:55:17 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
|
@ -80,7 +80,7 @@ int sqlite3BtreeClose(Btree*);
|
|||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
int sqlite3BtreeSetSafetyLevel(Btree*,int,int);
|
||||
int sqlite3BtreeSyncDisabled(Btree*);
|
||||
int sqlite3BtreeSetPageSize(Btree*,int,int);
|
||||
int sqlite3BtreeSetPageSize(Btree*,int,int,int);
|
||||
int sqlite3BtreeGetPageSize(Btree*);
|
||||
int sqlite3BtreeMaxPageCount(Btree*,int);
|
||||
int sqlite3BtreeGetReserve(Btree*);
|
||||
|
@ -91,12 +91,9 @@ int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
|||
int sqlite3BtreeCommitPhaseTwo(Btree*);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*);
|
||||
int sqlite3BtreeCommitStmt(Btree*);
|
||||
int sqlite3BtreeRollbackStmt(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*,int);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInStmt(Btree*);
|
||||
int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
|
@ -165,6 +162,8 @@ const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
|
|||
const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
|
||||
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
|
||||
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
|
||||
void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64);
|
||||
sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
|
@ -173,6 +172,10 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
|
|||
void sqlite3BtreeCacheOverflow(BtCursor *);
|
||||
void sqlite3BtreeClearCursor(BtCursor *);
|
||||
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
int sqlite3BtreeCount(BtCursor *, i64 *);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
|
||||
void sqlite3BtreeCursorList(Btree*);
|
||||
|
@ -183,42 +186,39 @@ void sqlite3BtreeCursorList(Btree*);
|
|||
** use mutexes to access the BtShared structures. So make the
|
||||
** Enter and Leave procedures no-ops.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
void sqlite3BtreeEnter(Btree*);
|
||||
void sqlite3BtreeLeave(Btree*);
|
||||
#ifndef NDEBUG
|
||||
/* This routine is used inside assert() statements only. */
|
||||
int sqlite3BtreeHoldsMutex(Btree*);
|
||||
void sqlite3BtreeEnterAll(sqlite3*);
|
||||
#else
|
||||
# define sqlite3BtreeEnter(X)
|
||||
# define sqlite3BtreeEnterAll(X)
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
|
||||
void sqlite3BtreeLeave(Btree*);
|
||||
void sqlite3BtreeEnterCursor(BtCursor*);
|
||||
void sqlite3BtreeLeaveCursor(BtCursor*);
|
||||
void sqlite3BtreeEnterAll(sqlite3*);
|
||||
void sqlite3BtreeLeaveAll(sqlite3*);
|
||||
#ifndef NDEBUG
|
||||
/* This routine is used inside assert() statements only. */
|
||||
int sqlite3BtreeHoldsAllMutexes(sqlite3*);
|
||||
#endif
|
||||
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*);
|
||||
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*);
|
||||
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*);
|
||||
#else
|
||||
# define sqlite3BtreeEnter(X)
|
||||
# define sqlite3BtreeLeave(X)
|
||||
#ifndef NDEBUG
|
||||
/* This routine is used inside assert() statements only. */
|
||||
# define sqlite3BtreeHoldsMutex(X) 1
|
||||
/* These routines are used inside assert() statements only. */
|
||||
int sqlite3BtreeHoldsMutex(Btree*);
|
||||
int sqlite3BtreeHoldsAllMutexes(sqlite3*);
|
||||
#endif
|
||||
#else
|
||||
|
||||
# define sqlite3BtreeLeave(X)
|
||||
# define sqlite3BtreeEnterCursor(X)
|
||||
# define sqlite3BtreeLeaveCursor(X)
|
||||
# define sqlite3BtreeEnterAll(X)
|
||||
# define sqlite3BtreeLeaveAll(X)
|
||||
#ifndef NDEBUG
|
||||
/* This routine is used inside assert() statements only. */
|
||||
# define sqlite3BtreeHoldsAllMutexes(X) 1
|
||||
#endif
|
||||
# define sqlite3BtreeMutexArrayEnter(X)
|
||||
# define sqlite3BtreeMutexArrayLeave(X)
|
||||
# define sqlite3BtreeMutexArrayInsert(X,Y)
|
||||
|
||||
# define sqlite3BtreeHoldsMutex(X) 1
|
||||
# define sqlite3BtreeHoldsAllMutexes(X) 1
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btreeInt.h,v 1.42 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** $Id: btreeInt.h,v 1.46 2009/03/20 14:18:52 danielk1977 Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
|
@ -205,11 +205,6 @@
|
|||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/* Round up a number to the next larger multiple of 8. This is used
|
||||
** to force 8-byte alignment on 64-bit architectures.
|
||||
*/
|
||||
#define ROUND8(x) ((x+7)&~7)
|
||||
|
||||
|
||||
/* The following value is the maximum cell size assuming a maximum page
|
||||
** size give above.
|
||||
|
@ -356,13 +351,30 @@ struct Btree {
|
|||
** may not be modified once it is initially set as long as nRef>0.
|
||||
** The pSchema field may be set once under BtShared.mutex and
|
||||
** thereafter is unchanged as long as nRef>0.
|
||||
**
|
||||
** isPending:
|
||||
**
|
||||
** If a BtShared client fails to obtain a write-lock on a database
|
||||
** table (because there exists one or more read-locks on the table),
|
||||
** the shared-cache enters 'pending-lock' state and isPending is
|
||||
** set to true.
|
||||
**
|
||||
** The shared-cache leaves the 'pending lock' state when either of
|
||||
** the following occur:
|
||||
**
|
||||
** 1) The current writer (BtShared.pWriter) concludes its transaction, OR
|
||||
** 2) The number of locks held by other connections drops to zero.
|
||||
**
|
||||
** while in the 'pending-lock' state, no connection may start a new
|
||||
** transaction.
|
||||
**
|
||||
** This feature is included to help prevent writer-starvation.
|
||||
*/
|
||||
struct BtShared {
|
||||
Pager *pPager; /* The page cache */
|
||||
sqlite3 *db; /* Database connection currently using this Btree */
|
||||
BtCursor *pCursor; /* A list of all open cursors */
|
||||
MemPage *pPage1; /* First page of the database */
|
||||
u8 inStmt; /* True if we are in a statement subtransaction */
|
||||
u8 readOnly; /* True if the underlying file is readonly */
|
||||
u8 pageSizeFixed; /* True if the page size can no longer be changed */
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
|
@ -385,7 +397,9 @@ struct BtShared {
|
|||
int nRef; /* Number of references to this structure */
|
||||
BtShared *pNext; /* Next on a list of sharable BtShared structs */
|
||||
BtLock *pLock; /* List of locks held on this shared-btree struct */
|
||||
Btree *pExclusive; /* Btree with an EXCLUSIVE lock on the whole db */
|
||||
Btree *pWriter; /* Btree with currently open write transaction */
|
||||
u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
|
||||
u8 isPending; /* If waiting for read-locks to clear */
|
||||
#endif
|
||||
u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
|
||||
};
|
||||
|
@ -438,6 +452,7 @@ struct BtCursor {
|
|||
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
|
||||
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
|
||||
Pgno pgnoRoot; /* The root page of this tree */
|
||||
sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
|
||||
CellInfo info; /* A parse of the cell we are pointing at */
|
||||
u8 wrFlag; /* True if writable */
|
||||
u8 atLast; /* Cursor pointing to the last entry */
|
||||
|
|
188
src/build.c
188
src/build.c
|
@ -22,7 +22,7 @@
|
|||
** COMMIT
|
||||
** ROLLBACK
|
||||
**
|
||||
** $Id: build.c,v 1.518 2009/02/13 03:43:32 drh Exp $
|
||||
** $Id: build.c,v 1.528 2009/04/08 13:51:51 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -178,19 +178,6 @@ void sqlite3FinishCoding(Parse *pParse){
|
|||
codeTableLocks(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
if( !db->init.busy ){
|
||||
/* Change the P4 argument of the first opcode (which will always be
|
||||
** an OP_Trace) to be the complete text of the current SQL statement.
|
||||
*/
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(v, 0);
|
||||
if( pOp && pOp->opcode==OP_Trace ){
|
||||
sqlite3VdbeChangeP4(v, 0, pParse->zSql,
|
||||
(int)(pParse->zTail - pParse->zSql));
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TRACE */
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,8 +189,8 @@ void sqlite3FinishCoding(Parse *pParse){
|
|||
sqlite3VdbeTrace(v, trace);
|
||||
#endif
|
||||
assert( pParse->disableColCache==0 ); /* Disables and re-enables match */
|
||||
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3,
|
||||
pParse->nTab+3, pParse->explain);
|
||||
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
|
||||
pParse->nTab, pParse->explain);
|
||||
pParse->rc = SQLITE_DONE;
|
||||
pParse->colNamesSet = 0;
|
||||
}else if( pParse->rc==SQLITE_OK ){
|
||||
|
@ -352,7 +339,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
|
|||
** Reclaim the memory used by an index
|
||||
*/
|
||||
static void freeIndex(Index *p){
|
||||
sqlite3 *db = p->pTable->db;
|
||||
sqlite3 *db = p->pTable->dbMem;
|
||||
sqlite3DbFree(db, p->zColAff);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
@ -480,7 +467,7 @@ void sqlite3CommitInternalChanges(sqlite3 *db){
|
|||
static void sqliteResetColumnNames(Table *pTable){
|
||||
int i;
|
||||
Column *pCol;
|
||||
sqlite3 *db = pTable->db;
|
||||
sqlite3 *db = pTable->dbMem;
|
||||
assert( pTable!=0 );
|
||||
if( (pCol = pTable->aCol)!=0 ){
|
||||
for(i=0; i<pTable->nCol; i++, pCol++){
|
||||
|
@ -511,7 +498,7 @@ void sqlite3DeleteTable(Table *pTable){
|
|||
sqlite3 *db;
|
||||
|
||||
if( pTable==0 ) return;
|
||||
db = pTable->db;
|
||||
db = pTable->dbMem;
|
||||
|
||||
/* Do not delete the table until the reference count reaches zero. */
|
||||
pTable->nRef--;
|
||||
|
@ -616,8 +603,11 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
|
|||
void sqlite3OpenMasterTable(Parse *p, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(p);
|
||||
sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, 5);/* sqlite_master has 5 columns */
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */
|
||||
if( p->nTab==0 ){
|
||||
p->nTab = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -846,7 +836,7 @@ void sqlite3StartTable(
|
|||
pTable->iPKey = -1;
|
||||
pTable->pSchema = db->aDb[iDb].pSchema;
|
||||
pTable->nRef = 1;
|
||||
pTable->db = db;
|
||||
pTable->dbMem = db->lookaside.bEnabled ? db : 0;
|
||||
if( pParse->pNewTable ) sqlite3DeleteTable(pParse->pNewTable);
|
||||
pParse->pNewTable = pTable;
|
||||
|
||||
|
@ -901,9 +891,10 @@ void sqlite3StartTable(
|
|||
** The record created does not contain anything yet. It will be replaced
|
||||
** by the real entry in code generated at sqlite3EndTable().
|
||||
**
|
||||
** The rowid for the new entry is left on the top of the stack.
|
||||
** The rowid value is needed by the code that sqlite3EndTable will
|
||||
** generate.
|
||||
** The rowid for the new entry is left in register pParse->regRowid.
|
||||
** The root page number of the new table is left in reg pParse->regRoot.
|
||||
** The rowid and root page number values are needed by the code that
|
||||
** sqlite3EndTable will generate.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
if( isView || isVirtual ){
|
||||
|
@ -1116,12 +1107,12 @@ void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){
|
|||
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
|
||||
pCol->zName);
|
||||
}else{
|
||||
Expr *pCopy;
|
||||
/* A copy of pExpr is used instead of the original, as pExpr contains
|
||||
** tokens that point to volatile memory. The 'span' of the expression
|
||||
** is required by pragma table_info.
|
||||
*/
|
||||
sqlite3ExprDelete(db, pCol->pDflt);
|
||||
pCol->pDflt = pCopy = sqlite3ExprDup(db, pExpr);
|
||||
if( pCopy ){
|
||||
sqlite3TokenCopy(db, &pCopy->span, &pExpr->span);
|
||||
}
|
||||
pCol->pDflt = sqlite3ExprDup(db, pExpr, EXPRDUP_REDUCE|EXPRDUP_SPAN);
|
||||
}
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
|
@ -1217,7 +1208,7 @@ void sqlite3AddCheckConstraint(
|
|||
** to malloced space and not the (ephemeral) text of the CREATE TABLE
|
||||
** statement */
|
||||
pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck,
|
||||
sqlite3ExprDup(db, pCheckExpr));
|
||||
sqlite3ExprDup(db, pCheckExpr, 0));
|
||||
}
|
||||
#endif
|
||||
sqlite3ExprDelete(db, pCheckExpr);
|
||||
|
@ -1340,18 +1331,100 @@ static int identLength(const char *z){
|
|||
}
|
||||
|
||||
/*
|
||||
** Write an identifier onto the end of the given string. Add
|
||||
** quote characters as needed.
|
||||
** This function is a wrapper around sqlite3GetToken() used by
|
||||
** isValidDimension(). This function differs from sqlite3GetToken() in
|
||||
** that:
|
||||
**
|
||||
** * Whitespace is ignored, and
|
||||
** * The output variable *peToken is set to 0 if the end of the
|
||||
** nul-terminated input string is reached.
|
||||
*/
|
||||
static void identPut(char *z, int *pIdx, char *zSignedIdent){
|
||||
static int getTokenNoSpace(unsigned char *z, int *peToken){
|
||||
int n = 0;
|
||||
while( sqlite3Isspace(z[n]) ) n++;
|
||||
if( !z[n] ){
|
||||
*peToken = 0;
|
||||
return 0;
|
||||
}
|
||||
return n + sqlite3GetToken(&z[n], peToken);
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter z points to a nul-terminated string. Return true if, when
|
||||
** whitespace is ignored, the contents of this string matches one of
|
||||
** the following patterns:
|
||||
**
|
||||
** ""
|
||||
** "(number)"
|
||||
** "(number,number)"
|
||||
*/
|
||||
static int isValidDimension(unsigned char *z){
|
||||
int eToken;
|
||||
int n = 0;
|
||||
n += getTokenNoSpace(&z[n], &eToken);
|
||||
if( eToken ){
|
||||
if( eToken!=TK_LP ) return 0;
|
||||
n += getTokenNoSpace(&z[n], &eToken);
|
||||
if( eToken==TK_PLUS || eToken==TK_MINUS ){
|
||||
n += getTokenNoSpace(&z[n], &eToken);
|
||||
}
|
||||
if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0;
|
||||
n += getTokenNoSpace(&z[n], &eToken);
|
||||
if( eToken==TK_COMMA ){
|
||||
n += getTokenNoSpace(&z[n], &eToken);
|
||||
if( eToken==TK_PLUS || eToken==TK_MINUS ){
|
||||
n += getTokenNoSpace(&z[n], &eToken);
|
||||
}
|
||||
if( eToken!=TK_INTEGER && eToken!=TK_FLOAT ) return 0;
|
||||
n += getTokenNoSpace(&z[n], &eToken);
|
||||
}
|
||||
if( eToken!=TK_RP ) return 0;
|
||||
getTokenNoSpace(&z[n], &eToken);
|
||||
}
|
||||
if( eToken ) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** The first parameter is a pointer to an output buffer. The second
|
||||
** parameter is a pointer to an integer that contains the offset at
|
||||
** which to write into the output buffer. This function copies the
|
||||
** nul-terminated string pointed to by the third parameter, zSignedIdent,
|
||||
** to the specified offset in the buffer and updates *pIdx to refer
|
||||
** to the first byte after the last byte written before returning.
|
||||
**
|
||||
** If the string zSignedIdent consists entirely of alpha-numeric
|
||||
** characters, does not begin with a digit and is not an SQL keyword,
|
||||
** then it is copied to the output buffer exactly as it is. Otherwise,
|
||||
** it is quoted using double-quotes.
|
||||
*/
|
||||
static void identPut(char *z, int *pIdx, char *zSignedIdent, int isTypename){
|
||||
unsigned char *zIdent = (unsigned char*)zSignedIdent;
|
||||
int i, j, needQuote;
|
||||
i = *pIdx;
|
||||
|
||||
for(j=0; zIdent[j]; j++){
|
||||
if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
|
||||
}
|
||||
needQuote = zIdent[j]!=0 || sqlite3Isdigit(zIdent[0])
|
||||
|| sqlite3KeywordCode(zIdent, j)!=TK_ID;
|
||||
needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID;
|
||||
if( !needQuote ){
|
||||
if( isTypename ){
|
||||
/* If this is a type-name, allow a little more flexibility. In SQLite,
|
||||
** a type-name is specified as:
|
||||
**
|
||||
** ids [ids] [(number [, number])]
|
||||
**
|
||||
** where "ids" is either a quoted string or a simple identifier (in the
|
||||
** above notation, [] means optional). It is a bit tricky to check
|
||||
** for all cases, but it is good to avoid unnecessarily quoting common
|
||||
** typenames like VARCHAR(10).
|
||||
*/
|
||||
needQuote = !isValidDimension(&zIdent[j]);
|
||||
}else{
|
||||
needQuote = zIdent[j];
|
||||
}
|
||||
}
|
||||
|
||||
if( needQuote ) z[i++] = '"';
|
||||
for(j=0; zIdent[j]; j++){
|
||||
z[i++] = zIdent[j];
|
||||
|
@ -1377,7 +1450,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
|
|||
n += identLength(pCol->zName);
|
||||
z = pCol->zType;
|
||||
if( z ){
|
||||
n += (sqlite3Strlen30(z) + 1);
|
||||
n += identLength(z);
|
||||
}
|
||||
}
|
||||
n += identLength(p->zName);
|
||||
|
@ -1398,18 +1471,17 @@ static char *createTableStmt(sqlite3 *db, Table *p){
|
|||
}
|
||||
sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
|
||||
k = sqlite3Strlen30(zStmt);
|
||||
identPut(zStmt, &k, p->zName);
|
||||
identPut(zStmt, &k, p->zName, 0);
|
||||
zStmt[k++] = '(';
|
||||
for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
|
||||
sqlite3_snprintf(n-k, &zStmt[k], zSep);
|
||||
k += sqlite3Strlen30(&zStmt[k]);
|
||||
zSep = zSep2;
|
||||
identPut(zStmt, &k, pCol->zName);
|
||||
identPut(zStmt, &k, pCol->zName, 0);
|
||||
if( (z = pCol->zType)!=0 ){
|
||||
zStmt[k++] = ' ';
|
||||
assert( (int)(sqlite3Strlen30(z)+k+1)<=n );
|
||||
sqlite3_snprintf(n-k, &zStmt[k], "%s", z);
|
||||
k += sqlite3Strlen30(z);
|
||||
identPut(zStmt, &k, z, 1);
|
||||
}
|
||||
}
|
||||
sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd);
|
||||
|
@ -1489,8 +1561,7 @@ void sqlite3EndTable(
|
|||
}
|
||||
|
||||
/* If not initializing, then create a record for the new table
|
||||
** in the SQLITE_MASTER table of the database. The record number
|
||||
** for the new table entry should already be on the stack.
|
||||
** in the SQLITE_MASTER table of the database.
|
||||
**
|
||||
** If this is a TEMPORARY table, write the entry into the auxiliary
|
||||
** file instead of into the main database file.
|
||||
|
@ -1507,9 +1578,8 @@ void sqlite3EndTable(
|
|||
|
||||
sqlite3VdbeAddOp1(v, OP_Close, 0);
|
||||
|
||||
/* Create the rootpage for the new table and push it onto the stack.
|
||||
** A view has no rootpage, so just push a zero onto the stack for
|
||||
** views. Initialize zType at the same time.
|
||||
/*
|
||||
** Initialize zType for the new view or table.
|
||||
*/
|
||||
if( p->pSelect==0 ){
|
||||
/* A regular table */
|
||||
|
@ -1525,7 +1595,7 @@ void sqlite3EndTable(
|
|||
|
||||
/* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
|
||||
** statement to populate the new table. The root-page number for the
|
||||
** new table is on the top of the vdbe stack.
|
||||
** new table is in register pParse->regRoot.
|
||||
**
|
||||
** Once the SELECT has been coded by sqlite3Select(), it is in a
|
||||
** suitable state to query for the column names and types to be used
|
||||
|
@ -1540,7 +1610,7 @@ void sqlite3EndTable(
|
|||
SelectDest dest;
|
||||
Table *pSelTab;
|
||||
|
||||
assert(pParse->nTab==0);
|
||||
assert(pParse->nTab==1);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
pParse->nTab = 2;
|
||||
|
@ -1571,9 +1641,7 @@ void sqlite3EndTable(
|
|||
|
||||
/* A slot for the record has already been allocated in the
|
||||
** SQLITE_MASTER table. We just need to update that slot with all
|
||||
** the information we've collected. The rowid for the preallocated
|
||||
** slot is the 2nd item on the stack. The top of the stack is the
|
||||
** root page for the new table (or a 0 if this is a view).
|
||||
** the information we've collected.
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s "
|
||||
|
@ -1701,7 +1769,7 @@ void sqlite3CreateView(
|
|||
** allocated rather than point to the input string - which means that
|
||||
** they will persist after the current sqlite3_exec() call returns.
|
||||
*/
|
||||
p->pSelect = sqlite3SelectDup(db, pSelect);
|
||||
p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
if( db->mallocFailed ){
|
||||
return;
|
||||
|
@ -1783,11 +1851,13 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
|||
** statement that defines the view.
|
||||
*/
|
||||
assert( pTable->pSelect );
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect);
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
|
||||
if( pSel ){
|
||||
u8 enableLookaside = db->lookaside.bEnabled;
|
||||
n = pParse->nTab;
|
||||
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
||||
pTable->nCol = -1;
|
||||
db->lookaside.bEnabled = 0;
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
|
@ -1796,6 +1866,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
|||
#else
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
|
||||
#endif
|
||||
db->lookaside.bEnabled = enableLookaside;
|
||||
pParse->nTab = n;
|
||||
if( pSelTab ){
|
||||
assert( pTable->aCol==0 );
|
||||
|
@ -1892,8 +1963,8 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
|||
** location iTable. The following code modifies the sqlite_master table to
|
||||
** reflect this.
|
||||
**
|
||||
** The "#%d" in the SQL is a special constant that means whatever value
|
||||
** is on the top of the stack. See sqlite3RegisterExpr().
|
||||
** The "#NNN" in the SQL is a special constant that means whatever value
|
||||
** is in register NNN. See sqlite3RegisterExpr().
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
|
||||
|
@ -2068,7 +2139,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
|||
** is generated to remove entries from sqlite_master and/or
|
||||
** sqlite_temp_master if required.
|
||||
*/
|
||||
pTrigger = pTab->pTrigger;
|
||||
pTrigger = sqlite3TriggerList(pParse, pTab);
|
||||
while( pTrigger ){
|
||||
assert( pTrigger->pSchema==pTab->pSchema ||
|
||||
pTrigger->pSchema==db->aDb[1].pSchema );
|
||||
|
@ -2277,8 +2348,8 @@ void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){
|
|||
*/
|
||||
static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
Table *pTab = pIndex->pTable; /* The table that is indexed */
|
||||
int iTab = pParse->nTab; /* Btree cursor used for pTab */
|
||||
int iIdx = pParse->nTab+1; /* Btree cursor used for pIndex */
|
||||
int iTab = pParse->nTab++; /* Btree cursor used for pTab */
|
||||
int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */
|
||||
int addr1; /* Address of top of loop */
|
||||
int tnum; /* Root page of index */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
|
@ -2773,7 +2844,8 @@ void sqlite3CreateIndex(
|
|||
/* Clean up before exiting */
|
||||
exit_create_index:
|
||||
if( pIndex ){
|
||||
freeIndex(pIndex);
|
||||
sqlite3_free(pIndex->zColAff);
|
||||
sqlite3DbFree(db, pIndex);
|
||||
}
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
sqlite3SrcListDelete(db, pTblName);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
** This file contains functions used to access the internal hash tables
|
||||
** of user defined functions and collation sequences.
|
||||
**
|
||||
** $Id: callback.c,v 1.35 2009/01/31 22:28:49 drh Exp $
|
||||
** $Id: callback.c,v 1.37 2009/03/24 15:08:10 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
@ -175,7 +175,7 @@ static CollSeq *findCollSeqEntry(
|
|||
pColl[0].zName[nName] = 0;
|
||||
pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl);
|
||||
|
||||
/* If a malloc() failure occured in sqlite3HashInsert(), it will
|
||||
/* If a malloc() failure occurred in sqlite3HashInsert(), it will
|
||||
** return the pColl pointer to be deleted (because it wasn't added
|
||||
** to the hash table).
|
||||
*/
|
||||
|
@ -423,6 +423,7 @@ void sqlite3SchemaFree(void *p){
|
|||
sqlite3HashInit(&pSchema->tblHash, 0);
|
||||
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
|
||||
Table *pTab = sqliteHashData(pElem);
|
||||
assert( pTab->dbMem==0 );
|
||||
sqlite3DeleteTable(pTab);
|
||||
}
|
||||
sqlite3HashClear(&temp1);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: date.c,v 1.103 2009/02/04 03:59:25 shane Exp $
|
||||
** $Id: date.c,v 1.105 2009/04/03 12:04:37 drh Exp $
|
||||
**
|
||||
** SQLite processes all times and dates as Julian Day numbers. The
|
||||
** dates and times are stored as the number of days since noon
|
||||
|
@ -953,8 +953,8 @@ static void strftimeFunc(
|
|||
case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break;
|
||||
case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break;
|
||||
case 's': {
|
||||
sqlite3_snprintf(30,&z[j],"%d",
|
||||
(int)(x.iJD/1000.0 - 210866760000.0));
|
||||
sqlite3_snprintf(30,&z[j],"%lld",
|
||||
(i64)(x.iJD/1000 - 21086676*(i64)10000));
|
||||
j += sqlite3Strlen30(&z[j]);
|
||||
break;
|
||||
}
|
||||
|
|
53
src/delete.c
53
src/delete.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** in order to generate code for DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.191 2008/12/23 23:56:22 drh Exp $
|
||||
** $Id: delete.c,v 1.198 2009/03/05 03:48:07 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -77,8 +77,8 @@ void sqlite3OpenTable(
|
|||
v = sqlite3GetVdbe(p);
|
||||
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
|
||||
sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName);
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
}
|
||||
|
||||
|
@ -99,12 +99,12 @@ void sqlite3MaterializeView(
|
|||
Select *pDup;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
pDup = sqlite3SelectDup(db, pView->pSelect);
|
||||
pDup = sqlite3SelectDup(db, pView->pSelect, 0);
|
||||
if( pWhere ){
|
||||
SrcList *pFrom;
|
||||
Token viewName;
|
||||
|
||||
pWhere = sqlite3ExprDup(db, pWhere);
|
||||
pWhere = sqlite3ExprDup(db, pWhere, 0);
|
||||
viewName.z = (u8*)pView->zName;
|
||||
viewName.n = (unsigned int)sqlite3Strlen30((const char*)viewName.z);
|
||||
pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &viewName, pDup, 0,0);
|
||||
|
@ -174,14 +174,15 @@ Expr *sqlite3LimitWhere(
|
|||
|
||||
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
||||
** and the SELECT subtree. */
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc);
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
|
||||
if( pSelectSrc == 0 ) {
|
||||
sqlite3ExprListDelete(pParse->db, pEList);
|
||||
goto limit_where_cleanup_2;
|
||||
}
|
||||
|
||||
/* generate the SELECT expression tree. */
|
||||
pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,pOrderBy,0,pLimit,pOffset);
|
||||
pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,
|
||||
pOrderBy,0,pLimit,pOffset);
|
||||
if( pSelect == 0 ) return 0;
|
||||
|
||||
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
||||
|
@ -190,7 +191,8 @@ Expr *sqlite3LimitWhere(
|
|||
pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
|
||||
if( pInClause == 0 ) goto limit_where_cleanup_1;
|
||||
|
||||
pInClause->pSelect = pSelect;
|
||||
pInClause->x.pSelect = pSelect;
|
||||
pInClause->flags |= EP_xIsSelect;
|
||||
sqlite3ExprSetHeight(pParse, pInClause);
|
||||
return pInClause;
|
||||
|
||||
|
@ -238,7 +240,7 @@ void sqlite3DeleteFrom(
|
|||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to delete from a view */
|
||||
int triggers_exist = 0; /* True if any triggers exist */
|
||||
Trigger *pTrigger; /* List of table triggers, if required */
|
||||
#endif
|
||||
int iBeginAfterTrigger = 0; /* Address of after trigger program */
|
||||
int iEndAfterTrigger = 0; /* Exit of after trigger program */
|
||||
|
@ -265,10 +267,10 @@ void sqlite3DeleteFrom(
|
|||
** deleted from is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_DELETE, 0);
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
|
@ -276,7 +278,7 @@ void sqlite3DeleteFrom(
|
|||
# define isView 0
|
||||
#endif
|
||||
|
||||
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
|
||||
if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
|
@ -287,7 +289,7 @@ void sqlite3DeleteFrom(
|
|||
if( rcauth==SQLITE_DENY ){
|
||||
goto delete_from_cleanup;
|
||||
}
|
||||
assert(!isView || triggers_exist);
|
||||
assert(!isView || pTrigger);
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
|
@ -297,7 +299,7 @@ void sqlite3DeleteFrom(
|
|||
|
||||
/* Allocate a cursor used to store the old.* data for a trigger.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
oldIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
|
@ -322,21 +324,21 @@ void sqlite3DeleteFrom(
|
|||
goto delete_from_cleanup;
|
||||
}
|
||||
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
|
||||
sqlite3BeginWriteOperation(pParse, triggers_exist, iDb);
|
||||
sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb);
|
||||
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
|
||||
int iGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
addr = sqlite3VdbeMakeLabel(v);
|
||||
|
||||
iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
|
||||
-1, oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
(void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
|
||||
TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
|
||||
iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
(void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1,
|
||||
oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
(void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
|
||||
TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
|
||||
iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
|
||||
sqlite3VdbeJumpHere(v, iGoto);
|
||||
|
@ -373,7 +375,7 @@ void sqlite3DeleteFrom(
|
|||
** It is easier just to erase the whole table. Note, however, that
|
||||
** this means that the row change count will be incorrect.
|
||||
*/
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !triggers_exist && !IsVirtual(pTab) ){
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) ){
|
||||
assert( !isView );
|
||||
sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt);
|
||||
if( !pParse->nested ){
|
||||
|
@ -405,9 +407,8 @@ void sqlite3DeleteFrom(
|
|||
|
||||
/* Open the pseudo-table used to store OLD if there are triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp1(v, OP_OpenPseudo, oldIdx);
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
|
||||
}
|
||||
|
||||
/* Delete every item whose key was written to the list during the
|
||||
|
@ -426,12 +427,12 @@ void sqlite3DeleteFrom(
|
|||
/* This is the beginning of the delete loop. If a trigger encounters
|
||||
** an IGNORE constraint, it jumps back to here.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeResolveLabel(v, addr);
|
||||
}
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
|
||||
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
int iData = ++pParse->nMem; /* For storing row data of OLD table */
|
||||
|
||||
/* If the record is no longer present in the table, jump to the
|
||||
|
@ -469,7 +470,7 @@ void sqlite3DeleteFrom(
|
|||
/* If there are row triggers, close all cursors then invoke
|
||||
** the AFTER triggers
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
/* Jump back and run the AFTER triggers */
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
|
||||
sqlite3VdbeJumpHere(v, iEndAfterTrigger);
|
||||
|
|
543
src/expr.c
543
src/expr.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.411 2009/02/04 03:59:25 shane Exp $
|
||||
** $Id: expr.c,v 1.426 2009/04/08 13:51:51 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -35,7 +35,8 @@
|
|||
char sqlite3ExprAffinity(Expr *pExpr){
|
||||
int op = pExpr->op;
|
||||
if( op==TK_SELECT ){
|
||||
return sqlite3ExprAffinity(pExpr->pSelect->pEList->a[0].pExpr);
|
||||
assert( pExpr->flags&EP_xIsSelect );
|
||||
return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_CAST
|
||||
if( op==TK_CAST ){
|
||||
|
@ -155,11 +156,9 @@ static char comparisonAffinity(Expr *pExpr){
|
|||
aff = sqlite3ExprAffinity(pExpr->pLeft);
|
||||
if( pExpr->pRight ){
|
||||
aff = sqlite3CompareAffinity(pExpr->pRight, aff);
|
||||
}
|
||||
else if( pExpr->pSelect ){
|
||||
aff = sqlite3CompareAffinity(pExpr->pSelect->pEList->a[0].pExpr, aff);
|
||||
}
|
||||
else if( !aff ){
|
||||
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff);
|
||||
}else if( !aff ){
|
||||
aff = SQLITE_AFF_NONE;
|
||||
}
|
||||
return aff;
|
||||
|
@ -345,8 +344,11 @@ static void exprSetHeight(Expr *p){
|
|||
int nHeight = 0;
|
||||
heightOfExpr(p->pLeft, &nHeight);
|
||||
heightOfExpr(p->pRight, &nHeight);
|
||||
heightOfExprList(p->pList, &nHeight);
|
||||
heightOfSelect(p->pSelect, &nHeight);
|
||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
heightOfSelect(p->x.pSelect, &nHeight);
|
||||
}else{
|
||||
heightOfExprList(p->x.pList, &nHeight);
|
||||
}
|
||||
p->nHeight = nHeight + 1;
|
||||
}
|
||||
|
||||
|
@ -402,8 +404,24 @@ Expr *sqlite3Expr(
|
|||
pNew->iAgg = -1;
|
||||
pNew->span.z = (u8*)"";
|
||||
if( pToken ){
|
||||
int c;
|
||||
assert( pToken->dyn==0 );
|
||||
pNew->span = pNew->token = *pToken;
|
||||
pNew->span = *pToken;
|
||||
|
||||
/* The pToken->z value is read-only. But the new expression
|
||||
** node created here might be passed to sqlite3DequoteExpr() which
|
||||
** will attempt to modify pNew->token.z. Hence, if the token
|
||||
** is quoted, make a copy now so that DequoteExpr() will change
|
||||
** the copy rather than the original text.
|
||||
*/
|
||||
if( pToken->n>=2
|
||||
&& ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){
|
||||
sqlite3TokenCopy(db, &pNew->token, pToken);
|
||||
}else{
|
||||
pNew->token = *pToken;
|
||||
pNew->flags |= EP_Dequoted;
|
||||
VVA_ONLY( pNew->vvaFlags |= EVVA_ReadOnlyToken; )
|
||||
}
|
||||
}else if( pLeft ){
|
||||
if( pRight ){
|
||||
if( pRight->span.dyn==0 && pLeft->span.dyn==0 ){
|
||||
|
@ -492,7 +510,10 @@ void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
|
|||
assert( pLeft!=0 );
|
||||
if( pExpr ){
|
||||
pExpr->span.z = pLeft->z;
|
||||
pExpr->span.n = pRight->n + (pRight->z - pLeft->z);
|
||||
/* The following assert() may fail when this is called
|
||||
** via sqlite3PExpr()/sqlite3Expr() from addWhereTerm(). */
|
||||
/* assert(pRight->z >= pLeft->z); */
|
||||
pExpr->span.n = pRight->n + (unsigned)(pRight->z - pLeft->z);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,15 +527,15 @@ Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){
|
|||
assert( pToken );
|
||||
pNew = sqlite3DbMallocZero(db, sizeof(Expr) );
|
||||
if( pNew==0 ){
|
||||
sqlite3ExprListDelete(db, pList); /* Avoid leaking memory when malloc fails */
|
||||
sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */
|
||||
return 0;
|
||||
}
|
||||
pNew->op = TK_FUNCTION;
|
||||
pNew->pList = pList;
|
||||
pNew->x.pList = pList;
|
||||
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
|
||||
assert( pToken->dyn==0 );
|
||||
pNew->token = *pToken;
|
||||
pNew->span = pNew->token;
|
||||
|
||||
pNew->span = *pToken;
|
||||
sqlite3TokenCopy(db, &pNew->token, pToken);
|
||||
sqlite3ExprSetHeight(pParse, pNew);
|
||||
return pNew;
|
||||
}
|
||||
|
@ -607,12 +628,25 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
|
|||
** Substructure is deleted.
|
||||
*/
|
||||
void sqlite3ExprClear(sqlite3 *db, Expr *p){
|
||||
if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z);
|
||||
if( p->token.dyn ) sqlite3DbFree(db, (char*)p->token.z);
|
||||
sqlite3ExprDelete(db, p->pLeft);
|
||||
sqlite3ExprDelete(db, p->pRight);
|
||||
sqlite3ExprListDelete(db, p->pList);
|
||||
sqlite3SelectDelete(db, p->pSelect);
|
||||
if( !ExprHasAnyProperty(p, EP_TokenOnly|EP_SpanToken) ){
|
||||
if( p->span.dyn ) sqlite3DbFree(db, (char*)p->span.z);
|
||||
if( ExprHasProperty(p, EP_Reduced) ){
|
||||
/* Subtrees are part of the same memory allocation when EP_Reduced set */
|
||||
if( p->pLeft ) sqlite3ExprClear(db, p->pLeft);
|
||||
if( p->pRight ) sqlite3ExprClear(db, p->pRight);
|
||||
}else{
|
||||
/* Subtrees are separate allocations when EP_Reduced is clear */
|
||||
sqlite3ExprDelete(db, p->pLeft);
|
||||
sqlite3ExprDelete(db, p->pRight);
|
||||
}
|
||||
/* x.pSelect and x.pList are always separately allocated */
|
||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
sqlite3SelectDelete(db, p->x.pSelect);
|
||||
}else{
|
||||
sqlite3ExprListDelete(db, p->x.pList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -628,15 +662,192 @@ void sqlite3ExprDelete(sqlite3 *db, Expr *p){
|
|||
** The Expr.token field might be a string literal that is quoted.
|
||||
** If so, remove the quotation marks.
|
||||
*/
|
||||
void sqlite3DequoteExpr(sqlite3 *db, Expr *p){
|
||||
if( ExprHasAnyProperty(p, EP_Dequoted) ){
|
||||
return;
|
||||
void sqlite3DequoteExpr(Expr *p){
|
||||
if( !ExprHasAnyProperty(p, EP_Dequoted) ){
|
||||
ExprSetProperty(p, EP_Dequoted);
|
||||
assert( (p->vvaFlags & EVVA_ReadOnlyToken)==0 );
|
||||
sqlite3Dequote((char*)p->token.z);
|
||||
}
|
||||
ExprSetProperty(p, EP_Dequoted);
|
||||
if( p->token.dyn==0 ){
|
||||
sqlite3TokenCopy(db, &p->token, &p->token);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes allocated for the expression structure
|
||||
** passed as the first argument. This is always one of EXPR_FULLSIZE,
|
||||
** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE.
|
||||
*/
|
||||
static int exprStructSize(Expr *p){
|
||||
if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE;
|
||||
if( ExprHasProperty(p, EP_SpanToken) ) return EXPR_SPANTOKENSIZE;
|
||||
if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE;
|
||||
return EXPR_FULLSIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3ExprDup() has been called to create a copy of expression p with
|
||||
** the EXPRDUP_XXX flags passed as the second argument. This function
|
||||
** returns the space required for the copy of the Expr structure only.
|
||||
** This is always one of EXPR_FULLSIZE, EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE.
|
||||
*/
|
||||
static int dupedExprStructSize(Expr *p, int flags){
|
||||
int nSize;
|
||||
if( 0==(flags&EXPRDUP_REDUCE) ){
|
||||
nSize = EXPR_FULLSIZE;
|
||||
}else if( p->pLeft || p->pRight || p->pColl || p->x.pList ){
|
||||
nSize = EXPR_REDUCEDSIZE;
|
||||
}else if( flags&EXPRDUP_SPAN ){
|
||||
nSize = EXPR_SPANTOKENSIZE;
|
||||
}else{
|
||||
nSize = EXPR_TOKENONLYSIZE;
|
||||
}
|
||||
sqlite3Dequote((char*)p->token.z);
|
||||
return nSize;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3ExprDup() has been called to create a copy of expression p with
|
||||
** the EXPRDUP_XXX passed as the second argument. This function returns
|
||||
** the space in bytes required to store the copy of the Expr structure
|
||||
** and the copies of the Expr.token.z and Expr.span.z (if applicable)
|
||||
** string buffers.
|
||||
*/
|
||||
static int dupedExprNodeSize(Expr *p, int flags){
|
||||
int nByte = dupedExprStructSize(p, flags) + (p->token.z ? p->token.n + 1 : 0);
|
||||
if( (flags&EXPRDUP_SPAN)!=0
|
||||
&& (p->token.z!=p->span.z || p->token.n!=p->span.n)
|
||||
){
|
||||
nByte += p->span.n;
|
||||
}
|
||||
return ROUND8(nByte);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes required to create a duplicate of the
|
||||
** expression passed as the first argument. The second argument is a
|
||||
** mask containing EXPRDUP_XXX flags.
|
||||
**
|
||||
** The value returned includes space to create a copy of the Expr struct
|
||||
** itself and the buffer referred to by Expr.token, if any. If the
|
||||
** EXPRDUP_SPAN flag is set, then space to create a copy of the buffer
|
||||
** refered to by Expr.span is also included.
|
||||
**
|
||||
** If the EXPRDUP_REDUCE flag is set, then the return value includes
|
||||
** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
|
||||
** and Expr.pRight variables (but not for any structures pointed to or
|
||||
** descended from the Expr.x.pList or Expr.x.pSelect variables).
|
||||
*/
|
||||
static int dupedExprSize(Expr *p, int flags){
|
||||
int nByte = 0;
|
||||
if( p ){
|
||||
nByte = dupedExprNodeSize(p, flags);
|
||||
if( flags&EXPRDUP_REDUCE ){
|
||||
int f = flags&(~EXPRDUP_SPAN);
|
||||
nByte += dupedExprSize(p->pLeft, f) + dupedExprSize(p->pRight, f);
|
||||
}
|
||||
}
|
||||
return nByte;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is similar to sqlite3ExprDup(), except that if pzBuffer
|
||||
** is not NULL then *pzBuffer is assumed to point to a buffer large enough
|
||||
** to store the copy of expression p, the copies of p->token and p->span
|
||||
** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
|
||||
** if any. Before returning, *pzBuffer is set to the first byte passed the
|
||||
** portion of the buffer copied into by this function.
|
||||
*/
|
||||
static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
|
||||
Expr *pNew = 0; /* Value to return */
|
||||
if( p ){
|
||||
const int isRequireSpan = (flags&EXPRDUP_SPAN);
|
||||
const int isReduced = (flags&EXPRDUP_REDUCE);
|
||||
u8 *zAlloc;
|
||||
|
||||
assert( pzBuffer==0 || isReduced );
|
||||
|
||||
/* Figure out where to write the new Expr structure. */
|
||||
if( pzBuffer ){
|
||||
zAlloc = *pzBuffer;
|
||||
}else{
|
||||
zAlloc = sqlite3DbMallocRaw(db, dupedExprSize(p, flags));
|
||||
}
|
||||
pNew = (Expr *)zAlloc;
|
||||
|
||||
if( pNew ){
|
||||
/* Set nNewSize to the size allocated for the structure pointed to
|
||||
** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or
|
||||
** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed
|
||||
** by the copy of the p->token.z string (if any).
|
||||
*/
|
||||
const int nNewSize = dupedExprStructSize(p, flags);
|
||||
const int nToken = (p->token.z ? p->token.n + 1 : 0);
|
||||
if( isReduced ){
|
||||
assert( ExprHasProperty(p, EP_Reduced)==0 );
|
||||
memcpy(zAlloc, p, nNewSize);
|
||||
}else{
|
||||
int nSize = exprStructSize(p);
|
||||
memcpy(zAlloc, p, nSize);
|
||||
memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
|
||||
}
|
||||
|
||||
/* Set the EP_Reduced and EP_TokenOnly flags appropriately. */
|
||||
pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_SpanToken);
|
||||
switch( nNewSize ){
|
||||
case EXPR_REDUCEDSIZE: pNew->flags |= EP_Reduced; break;
|
||||
case EXPR_TOKENONLYSIZE: pNew->flags |= EP_TokenOnly; break;
|
||||
case EXPR_SPANTOKENSIZE: pNew->flags |= EP_SpanToken; break;
|
||||
}
|
||||
|
||||
/* Copy the p->token string, if any. */
|
||||
if( nToken ){
|
||||
unsigned char *zToken = &zAlloc[nNewSize];
|
||||
memcpy(zToken, p->token.z, nToken-1);
|
||||
zToken[nToken-1] = '\0';
|
||||
pNew->token.dyn = 0;
|
||||
pNew->token.z = zToken;
|
||||
}
|
||||
|
||||
if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){
|
||||
/* Fill in the pNew->span token, if required. */
|
||||
if( isRequireSpan ){
|
||||
if( p->token.z!=p->span.z || p->token.n!=p->span.n ){
|
||||
pNew->span.z = &zAlloc[nNewSize+nToken];
|
||||
memcpy((char *)pNew->span.z, p->span.z, p->span.n);
|
||||
pNew->span.dyn = 0;
|
||||
}else{
|
||||
pNew->span.z = pNew->token.z;
|
||||
pNew->span.n = pNew->token.n;
|
||||
}
|
||||
}else{
|
||||
pNew->span.z = 0;
|
||||
pNew->span.n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_SpanToken)) ){
|
||||
/* Fill in the pNew->x.pSelect or pNew->x.pList member. */
|
||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, isReduced);
|
||||
}else{
|
||||
pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, isReduced);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in pNew->pLeft and pNew->pRight. */
|
||||
if( ExprHasAnyProperty(pNew, EP_Reduced|EP_TokenOnly|EP_SpanToken) ){
|
||||
zAlloc += dupedExprNodeSize(p, flags);
|
||||
if( ExprHasProperty(pNew, EP_Reduced) ){
|
||||
pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc);
|
||||
pNew->pRight = exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc);
|
||||
}
|
||||
if( pzBuffer ){
|
||||
*pzBuffer = zAlloc;
|
||||
}
|
||||
}else if( !ExprHasAnyProperty(p, EP_TokenOnly|EP_SpanToken) ){
|
||||
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
|
||||
pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -650,27 +861,21 @@ void sqlite3DequoteExpr(sqlite3 *db, Expr *p){
|
|||
** by subsequent calls to sqlite*ListAppend() routines.
|
||||
**
|
||||
** Any tables that the SrcList might point to are not duplicated.
|
||||
**
|
||||
** The flags parameter contains a combination of the EXPRDUP_XXX flags. If
|
||||
** the EXPRDUP_SPAN flag is set in the argument parameter, then the
|
||||
** Expr.span field of the input expression is copied. If EXPRDUP_SPAN is
|
||||
** clear, then the Expr.span field of the returned expression structure
|
||||
** is zeroed.
|
||||
**
|
||||
** If the EXPRDUP_REDUCE flag is set, then the structure returned is a
|
||||
** truncated version of the usual Expr structure that will be stored as
|
||||
** part of the in-memory representation of the database schema.
|
||||
*/
|
||||
Expr *sqlite3ExprDup(sqlite3 *db, Expr *p){
|
||||
Expr *pNew;
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
|
||||
if( pNew==0 ) return 0;
|
||||
memcpy(pNew, p, sizeof(*pNew));
|
||||
if( p->token.z!=0 ){
|
||||
pNew->token.z = (u8*)sqlite3DbStrNDup(db, (char*)p->token.z, p->token.n);
|
||||
pNew->token.dyn = 1;
|
||||
}else{
|
||||
assert( pNew->token.z==0 );
|
||||
}
|
||||
pNew->span.z = 0;
|
||||
pNew->pLeft = sqlite3ExprDup(db, p->pLeft);
|
||||
pNew->pRight = sqlite3ExprDup(db, p->pRight);
|
||||
pNew->pList = sqlite3ExprListDup(db, p->pList);
|
||||
pNew->pSelect = sqlite3SelectDup(db, p->pSelect);
|
||||
return pNew;
|
||||
Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){
|
||||
return exprDup(db, p, flags, 0);
|
||||
}
|
||||
void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){
|
||||
void sqlite3TokenCopy(sqlite3 *db, Token *pTo, const Token *pFrom){
|
||||
if( pTo->dyn ) sqlite3DbFree(db, (char*)pTo->z);
|
||||
if( pFrom->z ){
|
||||
pTo->n = pFrom->n;
|
||||
|
@ -680,7 +885,7 @@ void sqlite3TokenCopy(sqlite3 *db, Token *pTo, Token *pFrom){
|
|||
pTo->z = 0;
|
||||
}
|
||||
}
|
||||
ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
|
||||
ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
|
||||
ExprList *pNew;
|
||||
struct ExprList_item *pItem, *pOldItem;
|
||||
int i;
|
||||
|
@ -696,17 +901,9 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
|
|||
}
|
||||
pOldItem = p->a;
|
||||
for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){
|
||||
Expr *pNewExpr, *pOldExpr;
|
||||
pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr = pOldItem->pExpr);
|
||||
if( pOldExpr->span.z!=0 && pNewExpr ){
|
||||
/* Always make a copy of the span for top-level expressions in the
|
||||
** expression list. The logic in SELECT processing that determines
|
||||
** the names of columns in the result set needs this information */
|
||||
sqlite3TokenCopy(db, &pNewExpr->span, &pOldExpr->span);
|
||||
}
|
||||
assert( pNewExpr==0 || pNewExpr->span.z!=0
|
||||
|| pOldExpr->span.z==0
|
||||
|| db->mallocFailed );
|
||||
Expr *pNewExpr;
|
||||
Expr *pOldExpr = pOldItem->pExpr;
|
||||
pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr, flags);
|
||||
pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
|
||||
pItem->sortOrder = pOldItem->sortOrder;
|
||||
pItem->done = 0;
|
||||
|
@ -724,7 +921,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p){
|
|||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
|
||||
|| !defined(SQLITE_OMIT_SUBQUERY)
|
||||
SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){
|
||||
SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
|
||||
SrcList *pNew;
|
||||
int i;
|
||||
int nByte;
|
||||
|
@ -750,8 +947,8 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p){
|
|||
if( pTab ){
|
||||
pTab->nRef++;
|
||||
}
|
||||
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect);
|
||||
pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn);
|
||||
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
|
||||
pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
|
||||
pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
|
||||
pNewItem->colUsed = pOldItem->colUsed;
|
||||
}
|
||||
|
@ -777,21 +974,24 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
|
|||
}
|
||||
return pNew;
|
||||
}
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p){
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
||||
Select *pNew;
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->pEList = sqlite3ExprListDup(db, p->pEList);
|
||||
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc);
|
||||
pNew->pWhere = sqlite3ExprDup(db, p->pWhere);
|
||||
pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy);
|
||||
pNew->pHaving = sqlite3ExprDup(db, p->pHaving);
|
||||
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy);
|
||||
/* Always make a copy of the span for top-level expressions in the
|
||||
** expression list. The logic in SELECT processing that determines
|
||||
** the names of columns in the result set needs this information */
|
||||
pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags|EXPRDUP_SPAN);
|
||||
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
|
||||
pNew->pWhere = sqlite3ExprDup(db, p->pWhere, flags);
|
||||
pNew->pGroupBy = sqlite3ExprListDup(db, p->pGroupBy, flags);
|
||||
pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags);
|
||||
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
|
||||
pNew->op = p->op;
|
||||
pNew->pPrior = sqlite3SelectDup(db, p->pPrior);
|
||||
pNew->pLimit = sqlite3ExprDup(db, p->pLimit);
|
||||
pNew->pOffset = sqlite3ExprDup(db, p->pOffset);
|
||||
pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags);
|
||||
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
|
||||
pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
|
||||
pNew->iLimit = 0;
|
||||
pNew->iOffset = 0;
|
||||
pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
|
||||
|
@ -802,7 +1002,7 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p){
|
|||
return pNew;
|
||||
}
|
||||
#else
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p){
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
||||
assert( p==0 );
|
||||
return 0;
|
||||
}
|
||||
|
@ -1143,13 +1343,19 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
** If this is the case, it may be possible to use an existing table
|
||||
** or index instead of generating an epheremal table.
|
||||
*/
|
||||
p = pX->pSelect;
|
||||
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
|
||||
if( isCandidateForInOpt(p) ){
|
||||
sqlite3 *db = pParse->db;
|
||||
Index *pIdx;
|
||||
Expr *pExpr = p->pEList->a[0].pExpr;
|
||||
int iCol = pExpr->iColumn;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Expr *pExpr = p->pEList->a[0].pExpr; /* Expression <column> */
|
||||
int iCol = pExpr->iColumn; /* Index of column <column> */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
|
||||
Table *pTab = p->pSrc->a[0].pTab; /* Table <table>. */
|
||||
int iDb; /* Database idx for pTab */
|
||||
|
||||
/* Code an OP_VerifyCookie and OP_TableLock for <table>. */
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
/* This function is only called from two places. In both cases the vdbe
|
||||
** has already been allocated. So assume sqlite3GetVdbe() is always
|
||||
|
@ -1159,8 +1365,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
if( iCol<0 ){
|
||||
int iMem = ++pParse->nMem;
|
||||
int iAddr;
|
||||
Table *pTab = p->pSrc->a[0].pTab;
|
||||
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
|
||||
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
|
||||
|
@ -1171,17 +1375,17 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
|
||||
sqlite3VdbeJumpHere(v, iAddr);
|
||||
}else{
|
||||
Index *pIdx; /* Iterator variable */
|
||||
|
||||
/* The collation sequence used by the comparison. If an index is to
|
||||
** be used in place of a temp-table, it must be ordered according
|
||||
** to this collation sequence.
|
||||
*/
|
||||
** to this collation sequence. */
|
||||
CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pExpr);
|
||||
|
||||
/* Check that the affinity that will be used to perform the
|
||||
** comparison is the same as the affinity of the column. If
|
||||
** it is not, it is not possible to use any index.
|
||||
*/
|
||||
Table *pTab = p->pSrc->a[0].pTab;
|
||||
char aff = comparisonAffinity(pX);
|
||||
int affinity_ok = (pTab->aCol[iCol].affinity==aff||aff==SQLITE_AFF_NONE);
|
||||
|
||||
|
@ -1190,7 +1394,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
&& (pReq==sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], -1, 0))
|
||||
&& (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
|
||||
){
|
||||
int iDb;
|
||||
int iMem = ++pParse->nMem;
|
||||
int iAddr;
|
||||
char *pKey;
|
||||
|
@ -1202,7 +1405,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIdx->nColumn);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
|
||||
pKey,P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
|
@ -1222,7 +1424,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
eType = IN_INDEX_EPH;
|
||||
if( prNotFound ){
|
||||
*prNotFound = rMayHaveNull = ++pParse->nMem;
|
||||
}else if( pX->pLeft->iColumn<0 && pX->pSelect==0 ){
|
||||
}else if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){
|
||||
eType = IN_INDEX_ROWID;
|
||||
}
|
||||
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
|
||||
|
@ -1311,7 +1513,7 @@ void sqlite3CodeSubselect(
|
|||
memset(&keyInfo, 0, sizeof(keyInfo));
|
||||
keyInfo.nField = 1;
|
||||
|
||||
if( pExpr->pSelect ){
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
**
|
||||
** Generate code to write the results of the select into the temporary
|
||||
|
@ -1324,15 +1526,15 @@ void sqlite3CodeSubselect(
|
|||
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
|
||||
dest.affinity = (u8)affinity;
|
||||
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
|
||||
if( sqlite3Select(pParse, pExpr->pSelect, &dest) ){
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
return;
|
||||
}
|
||||
pEList = pExpr->pSelect->pEList;
|
||||
pEList = pExpr->x.pSelect->pEList;
|
||||
if( pEList && pEList->nExpr>0 ){
|
||||
keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
||||
pEList->a[0].pExpr);
|
||||
}
|
||||
}else if( pExpr->pList ){
|
||||
}else if( pExpr->x.pList ){
|
||||
/* Case 2: expr IN (exprlist)
|
||||
**
|
||||
** For each expression, build an index key from the evaluation and
|
||||
|
@ -1341,7 +1543,7 @@ void sqlite3CodeSubselect(
|
|||
** a column, use numeric affinity.
|
||||
*/
|
||||
int i;
|
||||
ExprList *pList = pExpr->pList;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
struct ExprList_item *pItem;
|
||||
int r1, r2, r3;
|
||||
|
||||
|
@ -1401,7 +1603,8 @@ void sqlite3CodeSubselect(
|
|||
Select *pSel;
|
||||
SelectDest dest;
|
||||
|
||||
pSel = pExpr->pSelect;
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pSel = pExpr->x.pSelect;
|
||||
sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
|
||||
if( pExpr->op==TK_SELECT ){
|
||||
dest.eDest = SRT_Mem;
|
||||
|
@ -1795,7 +1998,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
break;
|
||||
}
|
||||
case TK_STRING: {
|
||||
sqlite3DequoteExpr(db, pExpr);
|
||||
sqlite3DequoteExpr(pExpr);
|
||||
sqlite3VdbeAddOp4(v,OP_String8, 0, target, 0,
|
||||
(char*)pExpr->token.z, pExpr->token.n);
|
||||
break;
|
||||
|
@ -1821,9 +2024,26 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
}
|
||||
#endif
|
||||
case TK_VARIABLE: {
|
||||
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iTable, target);
|
||||
if( pExpr->token.n>1 ){
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n);
|
||||
int iPrior;
|
||||
VdbeOp *pOp;
|
||||
if( pExpr->token.n<=1
|
||||
&& (iPrior = sqlite3VdbeCurrentAddr(v)-1)>=0
|
||||
&& (pOp = sqlite3VdbeGetOp(v, iPrior))->opcode==OP_Variable
|
||||
&& pOp->p1+pOp->p3==pExpr->iTable
|
||||
&& pOp->p2+pOp->p3==target
|
||||
&& pOp->p4.z==0
|
||||
){
|
||||
/* If the previous instruction was a copy of the previous unnamed
|
||||
** parameter into the previous register, then simply increment the
|
||||
** repeat count on the prior instruction rather than making a new
|
||||
** instruction.
|
||||
*/
|
||||
pOp->p3++;
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iTable, target, 1);
|
||||
if( pExpr->token.n>1 ){
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pExpr->token.z, pExpr->token.n);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1985,28 +2205,34 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
}
|
||||
case TK_CONST_FUNC:
|
||||
case TK_FUNCTION: {
|
||||
ExprList *pList = pExpr->pList;
|
||||
int nExpr = pList ? pList->nExpr : 0;
|
||||
FuncDef *pDef;
|
||||
int nId;
|
||||
const char *zId;
|
||||
int constMask = 0;
|
||||
int i;
|
||||
u8 enc = ENC(db);
|
||||
CollSeq *pColl = 0;
|
||||
ExprList *pFarg; /* List of function arguments */
|
||||
int nFarg; /* Number of function arguments */
|
||||
FuncDef *pDef; /* The function definition object */
|
||||
int nId; /* Length of the function name in bytes */
|
||||
const char *zId; /* The function name */
|
||||
int constMask = 0; /* Mask of function arguments that are constant */
|
||||
int i; /* Loop counter */
|
||||
u8 enc = ENC(db); /* The text encoding used by this database */
|
||||
CollSeq *pColl = 0; /* A collating sequence */
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
testcase( op==TK_CONST_FUNC );
|
||||
testcase( op==TK_FUNCTION );
|
||||
if( ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_SpanToken) ){
|
||||
pFarg = 0;
|
||||
}else{
|
||||
pFarg = pExpr->x.pList;
|
||||
}
|
||||
nFarg = pFarg ? pFarg->nExpr : 0;
|
||||
zId = (char*)pExpr->token.z;
|
||||
nId = pExpr->token.n;
|
||||
pDef = sqlite3FindFunction(db, zId, nId, nExpr, enc, 0);
|
||||
pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0);
|
||||
assert( pDef!=0 );
|
||||
if( pList ){
|
||||
nExpr = pList->nExpr;
|
||||
r1 = sqlite3GetTempRange(pParse, nExpr);
|
||||
sqlite3ExprCodeExprList(pParse, pList, r1, 1);
|
||||
if( pFarg ){
|
||||
r1 = sqlite3GetTempRange(pParse, nFarg);
|
||||
sqlite3ExprCodeExprList(pParse, pFarg, r1, 1);
|
||||
}else{
|
||||
nExpr = r1 = 0;
|
||||
r1 = 0;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Possibly overload the function if the first argument is
|
||||
|
@ -2021,18 +2247,18 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
** "glob(B,A). We want to use the A in "A glob B" to test
|
||||
** for function overloading. But we use the B term in "glob(B,A)".
|
||||
*/
|
||||
if( nExpr>=2 && (pExpr->flags & EP_InfixFunc) ){
|
||||
pDef = sqlite3VtabOverloadFunction(db, pDef, nExpr, pList->a[1].pExpr);
|
||||
}else if( nExpr>0 ){
|
||||
pDef = sqlite3VtabOverloadFunction(db, pDef, nExpr, pList->a[0].pExpr);
|
||||
if( nFarg>=2 && (pExpr->flags & EP_InfixFunc) ){
|
||||
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[1].pExpr);
|
||||
}else if( nFarg>0 ){
|
||||
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr);
|
||||
}
|
||||
#endif
|
||||
for(i=0; i<nExpr && i<32; i++){
|
||||
if( sqlite3ExprIsConstant(pList->a[i].pExpr) ){
|
||||
for(i=0; i<nFarg && i<32; i++){
|
||||
if( sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
|
||||
constMask |= (1<<i);
|
||||
}
|
||||
if( (pDef->flags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
|
||||
pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
|
||||
pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
|
||||
}
|
||||
}
|
||||
if( pDef->flags & SQLITE_FUNC_NEEDCOLL ){
|
||||
|
@ -2041,11 +2267,11 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target,
|
||||
(char*)pDef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, (u8)nExpr);
|
||||
if( nExpr ){
|
||||
sqlite3ReleaseTempRange(pParse, r1, nExpr);
|
||||
sqlite3VdbeChangeP5(v, (u8)nFarg);
|
||||
if( nFarg ){
|
||||
sqlite3ReleaseTempRange(pParse, r1, nFarg);
|
||||
}
|
||||
sqlite3ExprCacheAffinityChange(pParse, r1, nExpr);
|
||||
sqlite3ExprCacheAffinityChange(pParse, r1, nFarg);
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
|
@ -2162,7 +2388,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
*/
|
||||
case TK_BETWEEN: {
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
struct ExprList_item *pLItem = pExpr->pList->a;
|
||||
struct ExprList_item *pLItem = pExpr->x.pList->a;
|
||||
Expr *pRight = pLItem->pExpr;
|
||||
|
||||
codeCompareOperands(pParse, pLeft, &r1, ®Free1,
|
||||
|
@ -2222,10 +2448,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
Expr *pX; /* The X expression */
|
||||
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
|
||||
|
||||
assert(pExpr->pList);
|
||||
assert((pExpr->pList->nExpr % 2) == 0);
|
||||
assert(pExpr->pList->nExpr > 0);
|
||||
pEList = pExpr->pList;
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
|
||||
assert((pExpr->x.pList->nExpr % 2) == 0);
|
||||
assert(pExpr->x.pList->nExpr > 0);
|
||||
pEList = pExpr->x.pList;
|
||||
aListelem = pEList->a;
|
||||
nExpr = pEList->nExpr;
|
||||
endLabel = sqlite3VdbeMakeLabel(v);
|
||||
|
@ -2273,15 +2499,15 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
|||
"RAISE() may only be used within a trigger-program");
|
||||
return 0;
|
||||
}
|
||||
if( pExpr->iColumn!=OE_Ignore ){
|
||||
assert( pExpr->iColumn==OE_Rollback ||
|
||||
pExpr->iColumn == OE_Abort ||
|
||||
pExpr->iColumn == OE_Fail );
|
||||
sqlite3DequoteExpr(db, pExpr);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, 0,
|
||||
if( pExpr->affinity!=OE_Ignore ){
|
||||
assert( pExpr->affinity==OE_Rollback ||
|
||||
pExpr->affinity == OE_Abort ||
|
||||
pExpr->affinity == OE_Fail );
|
||||
sqlite3DequoteExpr(pExpr);
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0,
|
||||
(char*)pExpr->token.z, pExpr->token.n);
|
||||
} else {
|
||||
assert( pExpr->iColumn == OE_Ignore );
|
||||
assert( pExpr->affinity == OE_Ignore );
|
||||
sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
|
||||
VdbeComment((v, "raise(IGNORE)"));
|
||||
|
@ -2438,7 +2664,8 @@ static int evalConstExpr(Walker *pWalker, Expr *pExpr){
|
|||
** Mark them this way to avoid generated unneeded OP_SCopy
|
||||
** instructions.
|
||||
*/
|
||||
ExprList *pList = pExpr->pList;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
if( pList ){
|
||||
int i = pList->nExpr;
|
||||
struct ExprList_item *pItem = pList->a;
|
||||
|
@ -2614,16 +2841,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||
Expr compRight;
|
||||
Expr exprX;
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
exprX = *pExpr->pLeft;
|
||||
exprAnd.op = TK_AND;
|
||||
exprAnd.pLeft = &compLeft;
|
||||
exprAnd.pRight = &compRight;
|
||||
compLeft.op = TK_GE;
|
||||
compLeft.pLeft = &exprX;
|
||||
compLeft.pRight = pExpr->pList->a[0].pExpr;
|
||||
compLeft.pRight = pExpr->x.pList->a[0].pExpr;
|
||||
compRight.op = TK_LE;
|
||||
compRight.pLeft = &exprX;
|
||||
compRight.pRight = pExpr->pList->a[1].pExpr;
|
||||
compRight.pRight = pExpr->x.pList->a[1].pExpr;
|
||||
exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1);
|
||||
testcase( regFree1==0 );
|
||||
exprX.op = TK_REGISTER;
|
||||
|
@ -2765,16 +2993,17 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
|||
Expr compRight;
|
||||
Expr exprX;
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
exprX = *pExpr->pLeft;
|
||||
exprAnd.op = TK_AND;
|
||||
exprAnd.pLeft = &compLeft;
|
||||
exprAnd.pRight = &compRight;
|
||||
compLeft.op = TK_GE;
|
||||
compLeft.pLeft = &exprX;
|
||||
compLeft.pRight = pExpr->pList->a[0].pExpr;
|
||||
compLeft.pRight = pExpr->x.pList->a[0].pExpr;
|
||||
compRight.op = TK_LE;
|
||||
compRight.pLeft = &exprX;
|
||||
compRight.pRight = pExpr->pList->a[1].pExpr;
|
||||
compRight.pRight = pExpr->x.pList->a[1].pExpr;
|
||||
exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1);
|
||||
testcase( regFree1==0 );
|
||||
exprX.op = TK_REGISTER;
|
||||
|
@ -2813,22 +3042,25 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
|
|||
if( pA==0||pB==0 ){
|
||||
return pB==pA;
|
||||
}
|
||||
if( pA->op!=pB->op ) return 0;
|
||||
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 0;
|
||||
if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0;
|
||||
if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0;
|
||||
if( pA->pList ){
|
||||
if( pB->pList==0 ) return 0;
|
||||
if( pA->pList->nExpr!=pB->pList->nExpr ) return 0;
|
||||
for(i=0; i<pA->pList->nExpr; i++){
|
||||
if( !sqlite3ExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}else if( pB->pList ){
|
||||
if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){
|
||||
return 0;
|
||||
}
|
||||
if( pA->pSelect || pB->pSelect ) return 0;
|
||||
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 0;
|
||||
if( pA->op!=pB->op ) return 0;
|
||||
if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0;
|
||||
if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0;
|
||||
|
||||
if( pA->x.pList && pB->x.pList ){
|
||||
if( pA->x.pList->nExpr!=pB->x.pList->nExpr ) return 0;
|
||||
for(i=0; i<pA->x.pList->nExpr; i++){
|
||||
Expr *pExprA = pA->x.pList->a[i].pExpr;
|
||||
Expr *pExprB = pB->x.pList->a[i].pExpr;
|
||||
if( !sqlite3ExprCompare(pExprA, pExprB) ) return 0;
|
||||
}
|
||||
}else if( pA->x.pList || pB->x.pList ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0;
|
||||
if( pA->op!=TK_COLUMN && pA->token.z ){
|
||||
if( pB->token.z==0 ) return 0;
|
||||
|
@ -2976,12 +3208,13 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
|||
u8 enc = ENC(pParse->db);
|
||||
i = addAggInfoFunc(pParse->db, pAggInfo);
|
||||
if( i>=0 ){
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pItem = &pAggInfo->aFunc[i];
|
||||
pItem->pExpr = pExpr;
|
||||
pItem->iMem = ++pParse->nMem;
|
||||
pItem->pFunc = sqlite3FindFunction(pParse->db,
|
||||
(char*)pExpr->token.z, pExpr->token.n,
|
||||
pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
|
||||
pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
|
||||
if( pExpr->flags & EP_Distinct ){
|
||||
pItem->iDistinct = pParse->nTab++;
|
||||
}else{
|
||||
|
|
110
src/func.c
110
src/func.c
|
@ -16,7 +16,7 @@
|
|||
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
|
||||
** All other code has file scope.
|
||||
**
|
||||
** $Id: func.c,v 1.222 2009/02/04 03:59:25 shane Exp $
|
||||
** $Id: func.c,v 1.231 2009/04/08 23:04:14 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -266,16 +266,22 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
|
|||
/*
|
||||
** Allocate nByte bytes of space using sqlite3_malloc(). If the
|
||||
** allocation fails, call sqlite3_result_error_nomem() to notify
|
||||
** the database handle that malloc() has failed.
|
||||
** the database handle that malloc() has failed and return NULL.
|
||||
** If nByte is larger than the maximum string or blob length, then
|
||||
** raise an SQLITE_TOOBIG exception and return NULL.
|
||||
*/
|
||||
static void *contextMalloc(sqlite3_context *context, i64 nByte){
|
||||
char *z;
|
||||
if( nByte>sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
assert( nByte>0 );
|
||||
testcase( nByte==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
testcase( nByte==db->aLimit[SQLITE_LIMIT_LENGTH]+1 );
|
||||
if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
z = 0;
|
||||
}else{
|
||||
z = sqlite3Malloc((int)nByte);
|
||||
if( !z && nByte>0 ){
|
||||
if( !z ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}
|
||||
}
|
||||
|
@ -356,8 +362,17 @@ static void randomFunc(
|
|||
sqlite_int64 r;
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
sqlite3_randomness(sizeof(r), &r);
|
||||
if( (r<<1)==0 ) r = 0; /* Prevent 0x8000.... as the result so that we */
|
||||
/* can always do abs() of the result */
|
||||
if( r<0 ){
|
||||
/* We need to prevent a random number of 0x8000000000000000
|
||||
** (or -9223372036854775808) since when you do abs() of that
|
||||
** number of you get the same value back again. To do this
|
||||
** in a way that is testable, mask the sign bit off of negative
|
||||
** values, resulting in a positive value. Then take the
|
||||
** 2s complement of that positive value. The end result can
|
||||
** therefore be no less than -9223372036854775807.
|
||||
*/
|
||||
r = -(r ^ (((sqlite3_int64)1)<<63));
|
||||
}
|
||||
sqlite3_result_int64(context, r);
|
||||
}
|
||||
|
||||
|
@ -444,7 +459,7 @@ struct compareInfo {
|
|||
** whereas only characters less than 0x80 do in ASCII.
|
||||
*/
|
||||
#if defined(SQLITE_EBCDIC)
|
||||
# define sqlite3Utf8Read(A,B,C) (*(A++))
|
||||
# define sqlite3Utf8Read(A,C) (*(A++))
|
||||
# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
|
||||
#else
|
||||
# define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
|
||||
|
@ -501,18 +516,18 @@ static int patternCompare(
|
|||
u8 noCase = pInfo->noCase;
|
||||
int prevEscape = 0; /* True if the previous character was 'escape' */
|
||||
|
||||
while( (c = sqlite3Utf8Read(zPattern,0,&zPattern))!=0 ){
|
||||
while( (c = sqlite3Utf8Read(zPattern,&zPattern))!=0 ){
|
||||
if( !prevEscape && c==matchAll ){
|
||||
while( (c=sqlite3Utf8Read(zPattern,0,&zPattern)) == matchAll
|
||||
while( (c=sqlite3Utf8Read(zPattern,&zPattern)) == matchAll
|
||||
|| c == matchOne ){
|
||||
if( c==matchOne && sqlite3Utf8Read(zString, 0, &zString)==0 ){
|
||||
if( c==matchOne && sqlite3Utf8Read(zString, &zString)==0 ){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if( c==0 ){
|
||||
return 1;
|
||||
}else if( c==esc ){
|
||||
c = sqlite3Utf8Read(zPattern, 0, &zPattern);
|
||||
c = sqlite3Utf8Read(zPattern, &zPattern);
|
||||
if( c==0 ){
|
||||
return 0;
|
||||
}
|
||||
|
@ -524,17 +539,17 @@ static int patternCompare(
|
|||
}
|
||||
return *zString!=0;
|
||||
}
|
||||
while( (c2 = sqlite3Utf8Read(zString,0,&zString))!=0 ){
|
||||
while( (c2 = sqlite3Utf8Read(zString,&zString))!=0 ){
|
||||
if( noCase ){
|
||||
GlogUpperToLower(c2);
|
||||
GlogUpperToLower(c);
|
||||
while( c2 != 0 && c2 != c ){
|
||||
c2 = sqlite3Utf8Read(zString, 0, &zString);
|
||||
c2 = sqlite3Utf8Read(zString, &zString);
|
||||
GlogUpperToLower(c2);
|
||||
}
|
||||
}else{
|
||||
while( c2 != 0 && c2 != c ){
|
||||
c2 = sqlite3Utf8Read(zString, 0, &zString);
|
||||
c2 = sqlite3Utf8Read(zString, &zString);
|
||||
}
|
||||
}
|
||||
if( c2==0 ) return 0;
|
||||
|
@ -542,7 +557,7 @@ static int patternCompare(
|
|||
}
|
||||
return 0;
|
||||
}else if( !prevEscape && c==matchOne ){
|
||||
if( sqlite3Utf8Read(zString, 0, &zString)==0 ){
|
||||
if( sqlite3Utf8Read(zString, &zString)==0 ){
|
||||
return 0;
|
||||
}
|
||||
}else if( c==matchSet ){
|
||||
|
@ -550,20 +565,20 @@ static int patternCompare(
|
|||
assert( esc==0 ); /* This only occurs for GLOB, not LIKE */
|
||||
seen = 0;
|
||||
invert = 0;
|
||||
c = sqlite3Utf8Read(zString, 0, &zString);
|
||||
c = sqlite3Utf8Read(zString, &zString);
|
||||
if( c==0 ) return 0;
|
||||
c2 = sqlite3Utf8Read(zPattern, 0, &zPattern);
|
||||
c2 = sqlite3Utf8Read(zPattern, &zPattern);
|
||||
if( c2=='^' ){
|
||||
invert = 1;
|
||||
c2 = sqlite3Utf8Read(zPattern, 0, &zPattern);
|
||||
c2 = sqlite3Utf8Read(zPattern, &zPattern);
|
||||
}
|
||||
if( c2==']' ){
|
||||
if( c==']' ) seen = 1;
|
||||
c2 = sqlite3Utf8Read(zPattern, 0, &zPattern);
|
||||
c2 = sqlite3Utf8Read(zPattern, &zPattern);
|
||||
}
|
||||
while( c2 && c2!=']' ){
|
||||
if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
|
||||
c2 = sqlite3Utf8Read(zPattern, 0, &zPattern);
|
||||
c2 = sqlite3Utf8Read(zPattern, &zPattern);
|
||||
if( c>=prior_c && c<=c2 ) seen = 1;
|
||||
prior_c = 0;
|
||||
}else{
|
||||
|
@ -572,7 +587,7 @@ static int patternCompare(
|
|||
}
|
||||
prior_c = c2;
|
||||
}
|
||||
c2 = sqlite3Utf8Read(zPattern, 0, &zPattern);
|
||||
c2 = sqlite3Utf8Read(zPattern, &zPattern);
|
||||
}
|
||||
if( c2==0 || (seen ^ invert)==0 ){
|
||||
return 0;
|
||||
|
@ -580,7 +595,7 @@ static int patternCompare(
|
|||
}else if( esc==c && !prevEscape ){
|
||||
prevEscape = 1;
|
||||
}else{
|
||||
c2 = sqlite3Utf8Read(zString, 0, &zString);
|
||||
c2 = sqlite3Utf8Read(zString, &zString);
|
||||
if( noCase ){
|
||||
GlogUpperToLower(c);
|
||||
GlogUpperToLower(c2);
|
||||
|
@ -623,6 +638,7 @@ static void likeFunc(
|
|||
){
|
||||
const unsigned char *zA, *zB;
|
||||
int escape = 0;
|
||||
int nPat;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
|
||||
zB = sqlite3_value_text(argv[0]);
|
||||
|
@ -631,8 +647,10 @@ static void likeFunc(
|
|||
/* Limit the length of the LIKE or GLOB pattern to avoid problems
|
||||
** of deep recursion and N*N behavior in patternCompare().
|
||||
*/
|
||||
if( sqlite3_value_bytes(argv[0]) >
|
||||
db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ){
|
||||
nPat = sqlite3_value_bytes(argv[0]);
|
||||
testcase( nPat==db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] );
|
||||
testcase( nPat==db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]+1 );
|
||||
if( nPat > db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ){
|
||||
sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
|
||||
return;
|
||||
}
|
||||
|
@ -649,7 +667,7 @@ static void likeFunc(
|
|||
"ESCAPE expression must be a single character", -1);
|
||||
return;
|
||||
}
|
||||
escape = sqlite3Utf8Read(zEsc, 0, &zEsc);
|
||||
escape = sqlite3Utf8Read(zEsc, &zEsc);
|
||||
}
|
||||
if( zA && zB ){
|
||||
struct compareInfo *pInfo = sqlite3_user_data(context);
|
||||
|
@ -808,10 +826,13 @@ static void zeroblobFunc(
|
|||
sqlite3_value **argv
|
||||
){
|
||||
i64 n;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
assert( argc==1 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
n = sqlite3_value_int64(argv[0]);
|
||||
if( n>SQLITE_MAX_LENGTH ){
|
||||
testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH]+1 );
|
||||
if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else{
|
||||
sqlite3_result_zeroblob(context, (int)n);
|
||||
|
@ -877,7 +898,9 @@ static void replaceFunc(
|
|||
u8 *zOld;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
nOut += nRep - nPattern;
|
||||
if( nOut>=db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
testcase( nOut-1==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
testcase( nOut-2==db->aLimit[SQLITE_LIMIT_LENGTH] );
|
||||
if( nOut-1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
sqlite3DbFree(db, zOut);
|
||||
return;
|
||||
|
@ -961,7 +984,7 @@ static void trimFunc(
|
|||
int len = 0;
|
||||
for(i=0; i<nChar; i++){
|
||||
len = aLen[i];
|
||||
if( memcmp(zIn, azChar[i], len)==0 ) break;
|
||||
if( len<=nIn && memcmp(zIn, azChar[i], len)==0 ) break;
|
||||
}
|
||||
if( i>=nChar ) break;
|
||||
zIn += len;
|
||||
|
@ -1155,6 +1178,13 @@ static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
|||
if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && p ){
|
||||
p->n++;
|
||||
}
|
||||
|
||||
/* The sqlite3_aggregate_count() function is deprecated. But just to make
|
||||
** sure it still operates correctly, verify that its count agrees with our
|
||||
** internal count when using count(*) and when the total count can be
|
||||
** expressed as a 32-bit integer. */
|
||||
assert( argc==1 || p==0 || p->n>0x7fffffff
|
||||
|| p->n==sqlite3_aggregate_count(context) );
|
||||
}
|
||||
static void countFinalize(sqlite3_context *context){
|
||||
CountCtx *p;
|
||||
|
@ -1203,7 +1233,7 @@ static void minMaxFinalize(sqlite3_context *context){
|
|||
sqlite3_value *pRes;
|
||||
pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0);
|
||||
if( pRes ){
|
||||
if( pRes->flags ){
|
||||
if( ALWAYS(pRes->flags) ){
|
||||
sqlite3_result_value(context, pRes);
|
||||
}
|
||||
sqlite3VdbeMemRelease(pRes);
|
||||
|
@ -1288,7 +1318,7 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
|
|||
FuncDef *pDef;
|
||||
pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
|
||||
2, SQLITE_UTF8, 0);
|
||||
if( pDef ){
|
||||
if( ALWAYS(pDef) ){
|
||||
pDef->flags = flagVal;
|
||||
}
|
||||
}
|
||||
|
@ -1305,9 +1335,9 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
|||
}else{
|
||||
pInfo = (struct compareInfo*)&likeInfoNorm;
|
||||
}
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
|
||||
sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0);
|
||||
sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY,
|
||||
(struct compareInfo*)&globInfo, likeFunc, 0,0);
|
||||
setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
|
||||
setLikeOptFlag(db, "like",
|
||||
|
@ -1323,15 +1353,16 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
|||
*/
|
||||
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
FuncDef *pDef;
|
||||
if( pExpr->op!=TK_FUNCTION || !pExpr->pList ){
|
||||
return 0;
|
||||
}
|
||||
if( pExpr->pList->nExpr!=2 ){
|
||||
if( pExpr->op!=TK_FUNCTION
|
||||
|| !pExpr->x.pList
|
||||
|| pExpr->x.pList->nExpr!=2
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pDef = sqlite3FindFunction(db, (char*)pExpr->token.z, pExpr->token.n, 2,
|
||||
SQLITE_UTF8, 0);
|
||||
if( pDef==0 || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){
|
||||
if( NEVER(pDef==0) || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1412,7 +1443,8 @@ void sqlite3RegisterGlobalFunctions(void){
|
|||
AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
|
||||
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
|
||||
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
|
||||
AGGREGATE(count, 0, 0, 0, countStep, countFinalize ),
|
||||
/* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
|
||||
{0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0},
|
||||
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
|
||||
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
|
||||
|
|
55
src/insert.c
55
src/insert.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.256 2008/12/10 21:19:57 drh Exp $
|
||||
** $Id: insert.c,v 1.260 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -168,7 +168,7 @@ static int autoIncBegin(
|
|||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Db *pDb = &pParse->db->aDb[iDb];
|
||||
int iCur = pParse->nTab;
|
||||
int iCur = pParse->nTab++;
|
||||
int addr; /* Address of the top of the loop */
|
||||
assert( v );
|
||||
pParse->nMem++; /* Holds name of table */
|
||||
|
@ -217,7 +217,7 @@ static void autoIncEnd(
|
|||
int memId /* Memory cell holding the maximum rowid */
|
||||
){
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
int iCur = pParse->nTab;
|
||||
int iCur = pParse->nTab++;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Db *pDb = &pParse->db->aDb[iDb];
|
||||
int j1;
|
||||
|
@ -401,7 +401,8 @@ void sqlite3Insert(
|
|||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True if attempting to insert into a view */
|
||||
int triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
|
||||
Trigger *pTrigger; /* List of triggers on pTab, if required */
|
||||
int tmask; /* Mask of trigger times */
|
||||
#endif
|
||||
|
||||
db = pParse->db;
|
||||
|
@ -431,22 +432,24 @@ void sqlite3Insert(
|
|||
** inserted into is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_INSERT, 0);
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
# define pTrigger 0
|
||||
# define tmask 0
|
||||
# define isView 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
# undef isView
|
||||
# define isView 0
|
||||
#endif
|
||||
assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) );
|
||||
|
||||
/* Ensure that:
|
||||
* (a) the table is not read-only,
|
||||
* (b) that if it is a view then ON INSERT triggers exist
|
||||
*/
|
||||
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
|
||||
if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
assert( pTab!=0 );
|
||||
|
@ -464,10 +467,10 @@ void sqlite3Insert(
|
|||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) goto insert_cleanup;
|
||||
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
|
||||
sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, iDb);
|
||||
sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb);
|
||||
|
||||
/* if there are row triggers, allocate a temp table for new.* references. */
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
newIdx = pParse->nTab++;
|
||||
}
|
||||
|
||||
|
@ -482,7 +485,7 @@ void sqlite3Insert(
|
|||
** This is the 2nd template.
|
||||
*/
|
||||
if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){
|
||||
assert( !triggers_exist );
|
||||
assert( !pTrigger );
|
||||
assert( pList==0 );
|
||||
goto insert_cleanup;
|
||||
}
|
||||
|
@ -557,7 +560,7 @@ void sqlite3Insert(
|
|||
** of the tables being read by the SELECT statement. Also use a
|
||||
** temp table in the case of row triggers.
|
||||
*/
|
||||
if( triggers_exist || readsTable(v, addrSelect, iDb, pTab) ){
|
||||
if( pTrigger || readsTable(v, addrSelect, iDb, pTab) ){
|
||||
useTempTable = 1;
|
||||
}
|
||||
|
||||
|
@ -676,9 +679,8 @@ void sqlite3Insert(
|
|||
|
||||
/* Open the temp table for FOR EACH ROW triggers
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
|
||||
}
|
||||
|
||||
/* Initialize the count of rows to be inserted
|
||||
|
@ -745,7 +747,7 @@ void sqlite3Insert(
|
|||
/* Run the BEFORE and INSTEAD OF triggers, if there are any
|
||||
*/
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
if( triggers_exist & TRIGGER_BEFORE ){
|
||||
if( tmask & TRIGGER_BEFORE ){
|
||||
int regTrigRowid;
|
||||
int regCols;
|
||||
int regRec;
|
||||
|
@ -813,8 +815,8 @@ void sqlite3Insert(
|
|||
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
|
||||
|
||||
/* Fire BEFORE or INSTEAD OF triggers */
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab,
|
||||
newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
|
||||
pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
|
@ -936,7 +938,7 @@ void sqlite3Insert(
|
|||
regIns,
|
||||
aRegIdx,
|
||||
0,
|
||||
(triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1,
|
||||
(tmask&TRIGGER_AFTER) ? newIdx : -1,
|
||||
appendFlag
|
||||
);
|
||||
}
|
||||
|
@ -948,10 +950,10 @@ void sqlite3Insert(
|
|||
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
|
||||
}
|
||||
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
/* Code AFTER triggers */
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab,
|
||||
newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
|
||||
pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
|
@ -1125,7 +1127,6 @@ void sqlite3GenerateConstraintChecks(
|
|||
if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
|
||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||
|| onError==OE_Ignore || onError==OE_Replace );
|
||||
switch( onError ){
|
||||
|
@ -1133,22 +1134,24 @@ void sqlite3GenerateConstraintChecks(
|
|||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
char *zMsg;
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_CONSTRAINT, onError);
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull,
|
||||
SQLITE_CONSTRAINT, onError, regData+i);
|
||||
zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL",
|
||||
pTab->zName, pTab->aCol[i].zName);
|
||||
sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
|
||||
break;
|
||||
}
|
||||
case OE_Ignore: {
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
|
||||
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
}
|
||||
|
||||
/* Test all CHECK constraints
|
||||
|
@ -1530,7 +1533,7 @@ static int xferOptimization(
|
|||
if( pSelect==0 ){
|
||||
return 0; /* Must be of the form INSERT INTO ... SELECT ... */
|
||||
}
|
||||
if( pDest->pTrigger ){
|
||||
if( sqlite3TriggerList(pParse, pDest) ){
|
||||
return 0; /* tab1 must not have triggers */
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: legacy.c,v 1.31 2009/01/20 16:53:40 danielk1977 Exp $
|
||||
** $Id: legacy.c,v 1.32 2009/03/19 18:51:07 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
@ -101,7 +101,7 @@ int sqlite3_exec(
|
|||
}
|
||||
if( xCallback(pArg, nCol, azVals, azCols) ){
|
||||
rc = SQLITE_ABORT;
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3VdbeFinalize((Vdbe *)pStmt);
|
||||
pStmt = 0;
|
||||
sqlite3Error(db, SQLITE_ABORT, 0);
|
||||
goto exec_out;
|
||||
|
@ -109,7 +109,7 @@ int sqlite3_exec(
|
|||
}
|
||||
|
||||
if( rc!=SQLITE_ROW ){
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
rc = sqlite3VdbeFinalize((Vdbe *)pStmt);
|
||||
pStmt = 0;
|
||||
if( rc!=SQLITE_SCHEMA ){
|
||||
nRetry = 0;
|
||||
|
@ -125,7 +125,7 @@ int sqlite3_exec(
|
|||
}
|
||||
|
||||
exec_out:
|
||||
if( pStmt ) sqlite3_finalize(pStmt);
|
||||
if( pStmt ) sqlite3VdbeFinalize((Vdbe *)pStmt);
|
||||
sqlite3DbFree(db, azCols);
|
||||
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
|
|
88
src/main.c
88
src/main.c
|
@ -14,7 +14,7 @@
|
|||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.528 2009/02/05 16:31:46 drh Exp $
|
||||
** $Id: main.c,v 1.536 2009/04/09 01:23:49 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -219,6 +219,7 @@ int sqlite3_shutdown(void){
|
|||
if( sqlite3GlobalConfig.isInit ){
|
||||
sqlite3_os_end();
|
||||
}
|
||||
sqlite3_reset_auto_extension();
|
||||
sqlite3MallocEnd();
|
||||
sqlite3MutexEnd();
|
||||
sqlite3GlobalConfig.isInit = 0;
|
||||
|
@ -404,12 +405,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
|||
sz = 0;
|
||||
pStart = 0;
|
||||
}else if( pBuf==0 ){
|
||||
sz = (sz + 7)&~7;
|
||||
sz = ROUND8(sz);
|
||||
sqlite3BeginBenignMalloc();
|
||||
pStart = sqlite3Malloc( sz*cnt );
|
||||
sqlite3EndBenignMalloc();
|
||||
}else{
|
||||
sz = sz&~7;
|
||||
sz = ROUNDDOWN8(sz);
|
||||
pStart = pBuf;
|
||||
}
|
||||
db->lookaside.pStart = pStart;
|
||||
|
@ -418,7 +419,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
|||
if( pStart ){
|
||||
int i;
|
||||
LookasideSlot *p;
|
||||
assert( sz > sizeof(LookasideSlot*) );
|
||||
assert( sz > (int)sizeof(LookasideSlot*) );
|
||||
p = (LookasideSlot*)pStart;
|
||||
for(i=cnt-1; i>=0; i--){
|
||||
p->pNext = db->lookaside.pFree;
|
||||
|
@ -560,6 +561,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){
|
|||
sqlite3DbFree(db, pTmp);
|
||||
}
|
||||
db->nSavepoint = 0;
|
||||
db->nStatement = 0;
|
||||
db->isTransactionSavepoint = 0;
|
||||
}
|
||||
|
||||
|
@ -629,6 +631,12 @@ int sqlite3_close(sqlite3 *db){
|
|||
}
|
||||
}
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
|
||||
/* Tell the code in notify.c that the connection no longer holds any
|
||||
** locks and does not require any further unlock-notify callbacks.
|
||||
*/
|
||||
sqlite3ConnectionClosed(db);
|
||||
|
||||
assert( db->nDb<=2 );
|
||||
assert( db->aDb==db->aDbStatic );
|
||||
for(j=0; j<ArraySize(db->aFunc.a); j++){
|
||||
|
@ -1238,15 +1246,15 @@ const char *sqlite3_errmsg(sqlite3 *db){
|
|||
if( !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
return sqlite3ErrStr(SQLITE_MISUSE);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
return sqlite3ErrStr(SQLITE_NOMEM);
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
z = (char*)sqlite3_value_text(db->pErr);
|
||||
assert( !db->mallocFailed );
|
||||
if( z==0 ){
|
||||
z = sqlite3ErrStr(db->errCode);
|
||||
if( db->mallocFailed ){
|
||||
z = sqlite3ErrStr(SQLITE_NOMEM);
|
||||
}else{
|
||||
z = (char*)sqlite3_value_text(db->pErr);
|
||||
assert( !db->mallocFailed );
|
||||
if( z==0 ){
|
||||
z = sqlite3ErrStr(db->errCode);
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return z;
|
||||
|
@ -1258,46 +1266,42 @@ const char *sqlite3_errmsg(sqlite3 *db){
|
|||
** error.
|
||||
*/
|
||||
const void *sqlite3_errmsg16(sqlite3 *db){
|
||||
/* Because all the characters in the string are in the unicode
|
||||
** range 0x00-0xFF, if we pad the big-endian string with a
|
||||
** zero byte, we can obtain the little-endian string with
|
||||
** &big_endian[1].
|
||||
*/
|
||||
static const char outOfMemBe[] = {
|
||||
0, 'o', 0, 'u', 0, 't', 0, ' ',
|
||||
0, 'o', 0, 'f', 0, ' ',
|
||||
0, 'm', 0, 'e', 0, 'm', 0, 'o', 0, 'r', 0, 'y', 0, 0, 0
|
||||
static const u16 outOfMem[] = {
|
||||
'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0
|
||||
};
|
||||
static const char misuseBe [] = {
|
||||
0, 'l', 0, 'i', 0, 'b', 0, 'r', 0, 'a', 0, 'r', 0, 'y', 0, ' ',
|
||||
0, 'r', 0, 'o', 0, 'u', 0, 't', 0, 'i', 0, 'n', 0, 'e', 0, ' ',
|
||||
0, 'c', 0, 'a', 0, 'l', 0, 'l', 0, 'e', 0, 'd', 0, ' ',
|
||||
0, 'o', 0, 'u', 0, 't', 0, ' ',
|
||||
0, 'o', 0, 'f', 0, ' ',
|
||||
0, 's', 0, 'e', 0, 'q', 0, 'u', 0, 'e', 0, 'n', 0, 'c', 0, 'e', 0, 0, 0
|
||||
static const u16 misuse[] = {
|
||||
'l', 'i', 'b', 'r', 'a', 'r', 'y', ' ',
|
||||
'r', 'o', 'u', 't', 'i', 'n', 'e', ' ',
|
||||
'c', 'a', 'l', 'l', 'e', 'd', ' ',
|
||||
'o', 'u', 't', ' ',
|
||||
'o', 'f', ' ',
|
||||
's', 'e', 'q', 'u', 'e', 'n', 'c', 'e', 0
|
||||
};
|
||||
|
||||
const void *z;
|
||||
if( !db ){
|
||||
return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
|
||||
return (void *)outOfMem;
|
||||
}
|
||||
if( !sqlite3SafetyCheckSickOrOk(db) ){
|
||||
return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
|
||||
return (void *)misuse;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
z = sqlite3_value_text16(db->pErr);
|
||||
if( z==0 ){
|
||||
sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
|
||||
SQLITE_UTF8, SQLITE_STATIC);
|
||||
if( db->mallocFailed ){
|
||||
z = (void *)outOfMem;
|
||||
}else{
|
||||
z = sqlite3_value_text16(db->pErr);
|
||||
if( z==0 ){
|
||||
sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
|
||||
SQLITE_UTF8, SQLITE_STATIC);
|
||||
z = sqlite3_value_text16(db->pErr);
|
||||
}
|
||||
/* A malloc() may have failed within the call to sqlite3_value_text16()
|
||||
** above. If this is the case, then the db->mallocFailed flag needs to
|
||||
** be cleared before returning. Do this directly, instead of via
|
||||
** sqlite3ApiExit(), to avoid setting the database handle error message.
|
||||
*/
|
||||
db->mallocFailed = 0;
|
||||
}
|
||||
/* A malloc() may have failed within the call to sqlite3_value_text16()
|
||||
** above. If this is the case, then the db->mallocFailed flag needs to
|
||||
** be cleared before returning. Do this directly, instead of via
|
||||
** sqlite3ApiExit(), to avoid setting the database handle error message.
|
||||
*/
|
||||
db->mallocFailed = 0;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return z;
|
||||
}
|
||||
|
@ -1940,7 +1944,6 @@ int sqlite3_table_column_metadata(
|
|||
(void)sqlite3SafetyOn(db);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
rc = sqlite3Init(db, &zErrMsg);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
if( SQLITE_OK!=rc ){
|
||||
goto error_out;
|
||||
}
|
||||
|
@ -1996,6 +1999,7 @@ int sqlite3_table_column_metadata(
|
|||
}
|
||||
|
||||
error_out:
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
(void)sqlite3SafetyOff(db);
|
||||
|
||||
/* Whether the function call succeeded or failed, set the output parameters
|
||||
|
|
104
src/malloc.c
104
src/malloc.c
|
@ -12,7 +12,7 @@
|
|||
**
|
||||
** Memory allocation functions used throughout sqlite.
|
||||
**
|
||||
** $Id: malloc.c,v 1.56 2009/02/17 18:37:29 drh Exp $
|
||||
** $Id: malloc.c,v 1.61 2009/03/24 15:08:10 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
|
@ -121,7 +121,7 @@ int sqlite3MallocInit(void){
|
|||
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
|
||||
&& sqlite3GlobalConfig.nScratch>=0 ){
|
||||
int i;
|
||||
sqlite3GlobalConfig.szScratch = (sqlite3GlobalConfig.szScratch - 4) & ~7;
|
||||
sqlite3GlobalConfig.szScratch = ROUNDDOWN8(sqlite3GlobalConfig.szScratch-4);
|
||||
mem0.aScratchFree = (u32*)&((char*)sqlite3GlobalConfig.pScratch)
|
||||
[sqlite3GlobalConfig.szScratch*sqlite3GlobalConfig.nScratch];
|
||||
for(i=0; i<sqlite3GlobalConfig.nScratch; i++){ mem0.aScratchFree[i] = i; }
|
||||
|
@ -134,7 +134,7 @@ int sqlite3MallocInit(void){
|
|||
&& sqlite3GlobalConfig.nPage>=1 ){
|
||||
int i;
|
||||
int overhead;
|
||||
int sz = sqlite3GlobalConfig.szPage & ~7;
|
||||
int sz = ROUNDDOWN8(sqlite3GlobalConfig.szPage);
|
||||
int n = sqlite3GlobalConfig.nPage;
|
||||
overhead = (4*n + sz - 1)/sz;
|
||||
sqlite3GlobalConfig.nPage -= overhead;
|
||||
|
@ -407,95 +407,6 @@ void sqlite3ScratchFree(void *p){
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate memory to be used by the page cache. Make use of the
|
||||
** memory buffer provided by SQLITE_CONFIG_PAGECACHE if there is one
|
||||
** and that memory is of the right size and is not completely
|
||||
** consumed. Otherwise, failover to sqlite3Malloc().
|
||||
*/
|
||||
#if 0
|
||||
void *sqlite3PageMalloc(int n){
|
||||
void *p;
|
||||
assert( n>0 );
|
||||
assert( (n & (n-1))==0 );
|
||||
assert( n>=512 && n<=32768 );
|
||||
|
||||
if( sqlite3GlobalConfig.szPage<n ){
|
||||
goto page_overflow;
|
||||
}else{
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
if( mem0.nPageFree==0 ){
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
goto page_overflow;
|
||||
}else{
|
||||
int i;
|
||||
i = mem0.aPageFree[--mem0.nPageFree];
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
i *= sqlite3GlobalConfig.szPage;
|
||||
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
|
||||
p = (void*)&((char*)sqlite3GlobalConfig.pPage)[i];
|
||||
}
|
||||
}
|
||||
return p;
|
||||
|
||||
page_overflow:
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, n);
|
||||
n = mallocWithAlarm(n, &p);
|
||||
if( p ) sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, n);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
void sqlite3PageFree(void *p){
|
||||
if( p ){
|
||||
if( sqlite3GlobalConfig.pPage==0
|
||||
|| p<sqlite3GlobalConfig.pPage
|
||||
|| p>=(void*)mem0.aPageFree ){
|
||||
/* In this case, the page allocation was obtained from a regular
|
||||
** call to sqlite3_mem_methods.xMalloc() (a page-cache-memory
|
||||
** "overflow"). Free the block with sqlite3_mem_methods.xFree().
|
||||
*/
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
int iSize = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
}else{
|
||||
sqlite3GlobalConfig.m.xFree(p);
|
||||
}
|
||||
}else{
|
||||
/* The page allocation was allocated from the sqlite3GlobalConfig.pPage
|
||||
** buffer. In this case all that is add the index of the page in
|
||||
** the sqlite3GlobalConfig.pPage array to the set of free indexes stored
|
||||
** in the mem0.aPageFree[] array.
|
||||
*/
|
||||
int i;
|
||||
i = (u8 *)p - (u8 *)sqlite3GlobalConfig.pPage;
|
||||
i /= sqlite3GlobalConfig.szPage;
|
||||
assert( i>=0 && i<sqlite3GlobalConfig.nPage );
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
assert( mem0.nPageFree<sqlite3GlobalConfig.nPage );
|
||||
mem0.aPageFree[mem0.nPageFree++] = i;
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
#if !defined(NDEBUG) && 0
|
||||
/* Assert that a duplicate was not just inserted into aPageFree[]. */
|
||||
for(i=0; i<mem0.nPageFree-1; i++){
|
||||
assert( mem0.aPageFree[i]!=mem0.aPageFree[mem0.nPageFree-1] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** TRUE if p is a lookaside memory allocation from db
|
||||
*/
|
||||
|
@ -515,6 +426,7 @@ int sqlite3MallocSize(void *p){
|
|||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
if( p==0 ){
|
||||
return 0;
|
||||
}else if( isLookaside(db, p) ){
|
||||
|
@ -544,6 +456,7 @@ void sqlite3_free(void *p){
|
|||
** connection.
|
||||
*/
|
||||
void sqlite3DbFree(sqlite3 *db, void *p){
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
if( isLookaside(db, p) ){
|
||||
LookasideSlot *pBuf = (LookasideSlot*)p;
|
||||
pBuf->pNext = db->lookaside.pFree;
|
||||
|
@ -652,6 +565,7 @@ void *sqlite3DbMallocZero(sqlite3 *db, int n){
|
|||
*/
|
||||
void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
void *p;
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
if( db ){
|
||||
LookasideSlot *pBuf;
|
||||
|
@ -686,6 +600,8 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
|||
*/
|
||||
void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
|
||||
void *pNew = 0;
|
||||
assert( db!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( db->mallocFailed==0 ){
|
||||
if( p==0 ){
|
||||
return sqlite3DbMallocRaw(db, n);
|
||||
|
@ -780,10 +696,10 @@ void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
|
|||
** sqlite3_realloc.
|
||||
**
|
||||
** The returned value is normally a copy of the second argument to this
|
||||
** function. However, if a malloc() failure has occured since the previous
|
||||
** function. However, if a malloc() failure has occurred since the previous
|
||||
** invocation SQLITE_NOMEM is returned instead.
|
||||
**
|
||||
** If the first argument, db, is not NULL and a malloc() error has occured,
|
||||
** If the first argument, db, is not NULL and a malloc() error has occurred,
|
||||
** then the connection error-code (the value returned by sqlite3_errcode())
|
||||
** is set to SQLITE_NOMEM.
|
||||
*/
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
** This file contains implementations of the low-level memory allocation
|
||||
** routines specified in the sqlite3_mem_methods object.
|
||||
**
|
||||
** $Id: mem1.c,v 1.29 2008/12/10 21:19:57 drh Exp $
|
||||
** $Id: mem1.c,v 1.30 2009/03/23 04:33:33 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
|||
static void *sqlite3MemMalloc(int nByte){
|
||||
sqlite3_int64 *p;
|
||||
assert( nByte>0 );
|
||||
nByte = (nByte+7)&~7;
|
||||
nByte = ROUND8(nByte);
|
||||
p = malloc( nByte+8 );
|
||||
if( p ){
|
||||
p[0] = nByte;
|
||||
|
@ -76,7 +76,7 @@ static void sqlite3MemFree(void *pPrior){
|
|||
static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
||||
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
|
||||
assert( pPrior!=0 && nByte>0 );
|
||||
nByte = (nByte+7)&~7;
|
||||
nByte = ROUND8(nByte);
|
||||
p = (sqlite3_int64*)pPrior;
|
||||
p--;
|
||||
p = realloc(p, nByte+8 );
|
||||
|
@ -103,7 +103,7 @@ static int sqlite3MemSize(void *pPrior){
|
|||
** Round up a request size to the next valid allocation size.
|
||||
*/
|
||||
static int sqlite3MemRoundup(int n){
|
||||
return (n+7) & ~7;
|
||||
return ROUND8(n);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
13
src/mem2.c
13
src/mem2.c
|
@ -19,7 +19,7 @@
|
|||
** This file contains implementations of the low-level memory allocation
|
||||
** routines specified in the sqlite3_mem_methods object.
|
||||
**
|
||||
** $Id: mem2.c,v 1.43 2009/02/05 03:00:06 shane Exp $
|
||||
** $Id: mem2.c,v 1.45 2009/03/23 04:33:33 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -128,7 +128,7 @@ static struct {
|
|||
** Adjust memory usage statistics
|
||||
*/
|
||||
static void adjustStats(int iSize, int increment){
|
||||
int i = ((iSize+7)&~7)/8;
|
||||
int i = ROUND8(iSize)/8;
|
||||
if( i>NCSIZE-1 ){
|
||||
i = NCSIZE - 1;
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
|
|||
p = (struct MemBlockHdr*)pAllocation;
|
||||
p--;
|
||||
assert( p->iForeGuard==(int)FOREGUARD );
|
||||
nReserve = (p->iSize+7)&~7;
|
||||
nReserve = ROUND8(p->iSize);
|
||||
pInt = (int*)pAllocation;
|
||||
pU8 = (u8*)pAllocation;
|
||||
assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD );
|
||||
|
@ -209,7 +209,7 @@ static void sqlite3MemShutdown(void *NotUsed){
|
|||
** Round up a request size to the next valid allocation size.
|
||||
*/
|
||||
static int sqlite3MemRoundup(int n){
|
||||
return (n+7) & ~7;
|
||||
return ROUND8(n);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -225,7 +225,7 @@ static void *sqlite3MemMalloc(int nByte){
|
|||
int nReserve;
|
||||
sqlite3_mutex_enter(mem.mutex);
|
||||
assert( mem.disallow==0 );
|
||||
nReserve = (nByte+7)&~7;
|
||||
nReserve = ROUND8(nByte);
|
||||
totalSize = nReserve + sizeof(*pHdr) + sizeof(int) +
|
||||
mem.nBacktrace*sizeof(void*) + mem.nTitle;
|
||||
p = malloc(totalSize);
|
||||
|
@ -248,6 +248,7 @@ static void *sqlite3MemMalloc(int nByte){
|
|||
void *aAddr[40];
|
||||
pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1;
|
||||
memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*));
|
||||
assert(pBt[0]);
|
||||
if( mem.xBacktrace ){
|
||||
mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]);
|
||||
}
|
||||
|
@ -371,7 +372,7 @@ void sqlite3MemdebugSettitle(const char *zTitle){
|
|||
if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1;
|
||||
memcpy(mem.zTitle, zTitle, n);
|
||||
mem.zTitle[n] = 0;
|
||||
mem.nTitle = (n+7)&~7;
|
||||
mem.nTitle = ROUND8(n);
|
||||
sqlite3_mutex_leave(mem.mutex);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
** The in-memory rollback journal is used to journal transactions for
|
||||
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
|
||||
**
|
||||
** @(#) $Id: memjournal.c,v 1.8 2008/12/20 02:14:40 drh Exp $
|
||||
** @(#) $Id: memjournal.c,v 1.11 2009/04/05 12:22:09 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -25,8 +25,13 @@ typedef struct FileChunk FileChunk;
|
|||
|
||||
/* Space to hold the rollback journal is allocated in increments of
|
||||
** this many bytes.
|
||||
**
|
||||
** The size chosen is a little less than a power of two. That way,
|
||||
** the FileChunk object will have a size that almost exactly fills
|
||||
** a power-of-two allocation. This mimimizes wasted space in power-of-two
|
||||
** memory allocators.
|
||||
*/
|
||||
#define JOURNAL_CHUNKSIZE 1024
|
||||
#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
|
||||
|
||||
/* Macro to find the minimum of two numeric values.
|
||||
*/
|
||||
|
@ -63,7 +68,8 @@ struct MemJournal {
|
|||
};
|
||||
|
||||
/*
|
||||
** Read data from the file.
|
||||
** Read data from the in-memory journal file. This is the implementation
|
||||
** of the sqlite3_vfs.xRead method.
|
||||
*/
|
||||
static int memjrnlRead(
|
||||
sqlite3_file *pJfd, /* The journal file from which to read */
|
||||
|
@ -77,12 +83,13 @@ static int memjrnlRead(
|
|||
int iChunkOffset;
|
||||
FileChunk *pChunk;
|
||||
|
||||
/* SQLite never tries to read past the end of a rollback journal file */
|
||||
assert( iOfst+iAmt<=p->endpoint.iOffset );
|
||||
|
||||
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
|
||||
sqlite3_int64 iOff = 0;
|
||||
for(pChunk=p->pFirst;
|
||||
pChunk && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
|
||||
ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
|
||||
pChunk=pChunk->pNext
|
||||
){
|
||||
iOff += JOURNAL_CHUNKSIZE;
|
||||
|
@ -185,11 +192,17 @@ static int memjrnlClose(sqlite3_file *pJfd){
|
|||
|
||||
/*
|
||||
** Sync the file.
|
||||
**
|
||||
** Syncing an in-memory journal is a no-op. And, in fact, this routine
|
||||
** is never called in a working implementation. This implementation
|
||||
** exists purely as a contingency, in case some malfunction in some other
|
||||
** part of SQLite causes Sync to be called by mistake.
|
||||
*/
|
||||
static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){ /*NO_TEST*/
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2); /*NO_TEST*/
|
||||
assert( 0 ); /*NO_TEST*/
|
||||
return SQLITE_OK; /*NO_TEST*/
|
||||
} /*NO_TEST*/
|
||||
|
||||
/*
|
||||
** Query the size of the file in bytes.
|
||||
|
@ -224,6 +237,7 @@ static struct sqlite3_io_methods MemJournalMethods = {
|
|||
*/
|
||||
void sqlite3MemJournalOpen(sqlite3_file *pJfd){
|
||||
MemJournal *p = (MemJournal *)pJfd;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(p) );
|
||||
memset(p, 0, sqlite3MemJournalSize());
|
||||
p->pMethod = &MemJournalMethods;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
** 2009 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains the implementation of the sqlite3_unlock_notify()
|
||||
** API method and its associated functionality.
|
||||
**
|
||||
** $Id: notify.c,v 1.4 2009/04/07 22:06:57 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
|
||||
/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
|
||||
/*
|
||||
** Public interfaces:
|
||||
**
|
||||
** sqlite3ConnectionBlocked()
|
||||
** sqlite3ConnectionUnlocked()
|
||||
** sqlite3ConnectionClosed()
|
||||
** sqlite3_unlock_notify()
|
||||
*/
|
||||
|
||||
#define assertMutexHeld() \
|
||||
assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) )
|
||||
|
||||
/*
|
||||
** Head of a linked list of all sqlite3 objects created by this process
|
||||
** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
|
||||
** is not NULL. This variable may only accessed while the STATIC_MASTER
|
||||
** mutex is held.
|
||||
*/
|
||||
static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
|
||||
|
||||
#ifndef NDEBUG
|
||||
/*
|
||||
** This function is a complex assert() that verifies the following
|
||||
** properties of the blocked connections list:
|
||||
**
|
||||
** 1) Each entry in the list has a non-NULL value for either
|
||||
** pUnlockConnection or pBlockingConnection, or both.
|
||||
**
|
||||
** 2) All entries in the list that share a common value for
|
||||
** xUnlockNotify are grouped together.
|
||||
**
|
||||
** 3) If the argument db is not NULL, then none of the entries in the
|
||||
** blocked connections list have pUnlockConnection or pBlockingConnection
|
||||
** set to db. This is used when closing connection db.
|
||||
*/
|
||||
static void checkListProperties(sqlite3 *db){
|
||||
sqlite3 *p;
|
||||
for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
|
||||
int seen = 0;
|
||||
sqlite3 *p2;
|
||||
|
||||
/* Verify property (1) */
|
||||
assert( p->pUnlockConnection || p->pBlockingConnection );
|
||||
|
||||
/* Verify property (2) */
|
||||
for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){
|
||||
if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1;
|
||||
assert( p2->xUnlockNotify==p->xUnlockNotify || !seen );
|
||||
assert( db==0 || p->pUnlockConnection!=db );
|
||||
assert( db==0 || p->pBlockingConnection!=db );
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define checkListProperties(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Remove connection db from the blocked connections list. If connection
|
||||
** db is not currently a part of the list, this function is a no-op.
|
||||
*/
|
||||
static void removeFromBlockedList(sqlite3 *db){
|
||||
sqlite3 **pp;
|
||||
assertMutexHeld();
|
||||
for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){
|
||||
if( *pp==db ){
|
||||
*pp = (*pp)->pNextBlocked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add connection db to the blocked connections list. It is assumed
|
||||
** that it is not already a part of the list.
|
||||
*/
|
||||
static void addToBlockedList(sqlite3 *db){
|
||||
sqlite3 **pp;
|
||||
assertMutexHeld();
|
||||
for(
|
||||
pp=&sqlite3BlockedList;
|
||||
*pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
|
||||
pp=&(*pp)->pNextBlocked
|
||||
);
|
||||
db->pNextBlocked = *pp;
|
||||
*pp = db;
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain the STATIC_MASTER mutex.
|
||||
*/
|
||||
static void enterMutex(void){
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
checkListProperties(0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Release the STATIC_MASTER mutex.
|
||||
*/
|
||||
static void leaveMutex(void){
|
||||
assertMutexHeld();
|
||||
checkListProperties(0);
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
}
|
||||
|
||||
/*
|
||||
** Register an unlock-notify callback.
|
||||
**
|
||||
** This is called after connection "db" has attempted some operation
|
||||
** but has received an SQLITE_LOCKED error because another connection
|
||||
** (call it pOther) in the same process was busy using the same shared
|
||||
** cache. pOther is found by looking at db->pBlockingConnection.
|
||||
**
|
||||
** If there is no blocking connection, the callback is invoked immediately,
|
||||
** before this routine returns.
|
||||
**
|
||||
** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
|
||||
** a deadlock.
|
||||
**
|
||||
** Otherwise, make arrangements to invoke xNotify when pOther drops
|
||||
** its locks.
|
||||
**
|
||||
** Each call to this routine overrides any prior callbacks registered
|
||||
** on the same "db". If xNotify==0 then any prior callbacks are immediately
|
||||
** cancelled.
|
||||
*/
|
||||
int sqlite3_unlock_notify(
|
||||
sqlite3 *db,
|
||||
void (*xNotify)(void **, int),
|
||||
void *pArg
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
enterMutex();
|
||||
|
||||
if( xNotify==0 ){
|
||||
removeFromBlockedList(db);
|
||||
db->pUnlockConnection = 0;
|
||||
db->xUnlockNotify = 0;
|
||||
db->pUnlockArg = 0;
|
||||
}else if( 0==db->pBlockingConnection ){
|
||||
/* The blocking transaction has been concluded. Or there never was a
|
||||
** blocking transaction. In either case, invoke the notify callback
|
||||
** immediately.
|
||||
*/
|
||||
xNotify(&pArg, 1);
|
||||
}else{
|
||||
sqlite3 *p;
|
||||
|
||||
for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
|
||||
if( p ){
|
||||
rc = SQLITE_LOCKED; /* Deadlock detected. */
|
||||
}else{
|
||||
db->pUnlockConnection = db->pBlockingConnection;
|
||||
db->xUnlockNotify = xNotify;
|
||||
db->pUnlockArg = pArg;
|
||||
removeFromBlockedList(db);
|
||||
addToBlockedList(db);
|
||||
}
|
||||
}
|
||||
|
||||
leaveMutex();
|
||||
assert( !db->mallocFailed );
|
||||
sqlite3Error(db, rc, (rc?"database is deadlocked":0));
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called while stepping or preparing a statement
|
||||
** associated with connection db. The operation will return SQLITE_LOCKED
|
||||
** to the user because it requires a lock that will not be available
|
||||
** until connection pBlocker concludes its current transaction.
|
||||
*/
|
||||
void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
|
||||
enterMutex();
|
||||
if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
|
||||
addToBlockedList(db);
|
||||
}
|
||||
db->pBlockingConnection = pBlocker;
|
||||
leaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called when
|
||||
** the transaction opened by database db has just finished. Locks held
|
||||
** by database connection db have been released.
|
||||
**
|
||||
** This function loops through each entry in the blocked connections
|
||||
** list and does the following:
|
||||
**
|
||||
** 1) If the sqlite3.pBlockingConnection member of a list entry is
|
||||
** set to db, then set pBlockingConnection=0.
|
||||
**
|
||||
** 2) If the sqlite3.pUnlockConnection member of a list entry is
|
||||
** set to db, then invoke the configured unlock-notify callback and
|
||||
** set pUnlockConnection=0.
|
||||
**
|
||||
** 3) If the two steps above mean that pBlockingConnection==0 and
|
||||
** pUnlockConnection==0, remove the entry from the blocked connections
|
||||
** list.
|
||||
*/
|
||||
void sqlite3ConnectionUnlocked(sqlite3 *db){
|
||||
void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
|
||||
int nArg = 0; /* Number of entries in aArg[] */
|
||||
sqlite3 **pp; /* Iterator variable */
|
||||
void **aArg; /* Arguments to the unlock callback */
|
||||
void **aDyn = 0; /* Dynamically allocated space for aArg[] */
|
||||
void *aStatic[16]; /* Starter space for aArg[]. No malloc required */
|
||||
|
||||
aArg = aStatic;
|
||||
enterMutex(); /* Enter STATIC_MASTER mutex */
|
||||
|
||||
/* This loop runs once for each entry in the blocked-connections list. */
|
||||
for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
|
||||
sqlite3 *p = *pp;
|
||||
|
||||
/* Step 1. */
|
||||
if( p->pBlockingConnection==db ){
|
||||
p->pBlockingConnection = 0;
|
||||
}
|
||||
|
||||
/* Step 2. */
|
||||
if( p->pUnlockConnection==db ){
|
||||
assert( p->xUnlockNotify );
|
||||
if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
|
||||
xUnlockNotify(aArg, nArg);
|
||||
nArg = 0;
|
||||
}
|
||||
|
||||
sqlite3BeginBenignMalloc();
|
||||
assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
|
||||
assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
|
||||
if( (!aDyn && nArg==(int)ArraySize(aStatic))
|
||||
|| (aDyn && nArg==(int)(sqlite3DbMallocSize(db, aDyn)/sizeof(void*)))
|
||||
){
|
||||
/* The aArg[] array needs to grow. */
|
||||
void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
|
||||
if( pNew ){
|
||||
memcpy(pNew, aArg, nArg*sizeof(void *));
|
||||
sqlite3_free(aDyn);
|
||||
aDyn = aArg = pNew;
|
||||
}else{
|
||||
/* This occurs when the array of context pointers that need to
|
||||
** be passed to the unlock-notify callback is larger than the
|
||||
** aStatic[] array allocated on the stack and the attempt to
|
||||
** allocate a larger array from the heap has failed.
|
||||
**
|
||||
** This is a difficult situation to handle. Returning an error
|
||||
** code to the caller is insufficient, as even if an error code
|
||||
** is returned the transaction on connection db will still be
|
||||
** closed and the unlock-notify callbacks on blocked connections
|
||||
** will go unissued. This might cause the application to wait
|
||||
** indefinitely for an unlock-notify callback that will never
|
||||
** arrive.
|
||||
**
|
||||
** Instead, invoke the unlock-notify callback with the context
|
||||
** array already accumulated. We can then clear the array and
|
||||
** begin accumulating any further context pointers without
|
||||
** requiring any dynamic allocation. This is sub-optimal because
|
||||
** it means that instead of one callback with a large array of
|
||||
** context pointers the application will receive two or more
|
||||
** callbacks with smaller arrays of context pointers, which will
|
||||
** reduce the applications ability to prioritize multiple
|
||||
** connections. But it is the best that can be done under the
|
||||
** circumstances.
|
||||
*/
|
||||
xUnlockNotify(aArg, nArg);
|
||||
nArg = 0;
|
||||
}
|
||||
}
|
||||
sqlite3EndBenignMalloc();
|
||||
|
||||
aArg[nArg++] = p->pUnlockArg;
|
||||
xUnlockNotify = p->xUnlockNotify;
|
||||
p->pUnlockConnection = 0;
|
||||
p->xUnlockNotify = 0;
|
||||
p->pUnlockArg = 0;
|
||||
}
|
||||
|
||||
/* Step 3. */
|
||||
if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
|
||||
/* Remove connection p from the blocked connections list. */
|
||||
*pp = p->pNextBlocked;
|
||||
p->pNextBlocked = 0;
|
||||
}else{
|
||||
pp = &p->pNextBlocked;
|
||||
}
|
||||
}
|
||||
|
||||
if( nArg!=0 ){
|
||||
xUnlockNotify(aArg, nArg);
|
||||
}
|
||||
sqlite3_free(aDyn);
|
||||
leaveMutex(); /* Leave STATIC_MASTER mutex */
|
||||
}
|
||||
|
||||
/*
|
||||
** This is called when the database connection passed as an argument is
|
||||
** being closed. The connection is removed from the blocked list.
|
||||
*/
|
||||
void sqlite3ConnectionClosed(sqlite3 *db){
|
||||
sqlite3ConnectionUnlocked(db);
|
||||
enterMutex();
|
||||
removeFromBlockedList(db);
|
||||
checkListProperties(db);
|
||||
leaveMutex();
|
||||
}
|
||||
#endif
|
7
src/os.c
7
src/os.c
|
@ -13,7 +13,7 @@
|
|||
** This file contains OS interface code that is common to all
|
||||
** architectures.
|
||||
**
|
||||
** $Id: os.c,v 1.125 2008/12/08 18:19:18 drh Exp $
|
||||
** $Id: os.c,v 1.126 2009/03/25 14:24:42 drh Exp $
|
||||
*/
|
||||
#define _SQLITE_OS_C_ 1
|
||||
#include "sqliteInt.h"
|
||||
|
@ -112,8 +112,11 @@ int sqlite3OsOpen(
|
|||
int flags,
|
||||
int *pFlagsOut
|
||||
){
|
||||
int rc;
|
||||
DO_OS_MALLOC_TEST;
|
||||
return pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut);
|
||||
rc = pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut);
|
||||
assert( rc==SQLITE_OK || pFile->pMethods==0 );
|
||||
return rc;
|
||||
}
|
||||
int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
return pVfs->xDelete(pVfs, zPath, dirSync);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
** This file should be #included by the os_*.c files only. It is not a
|
||||
** general purpose header file.
|
||||
**
|
||||
** $Id: os_common.h,v 1.37 2008/05/29 20:22:37 shane Exp $
|
||||
** $Id: os_common.h,v 1.38 2009/02/24 18:40:50 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _OS_COMMON_H_
|
||||
#define _OS_COMMON_H_
|
||||
|
@ -31,15 +31,6 @@
|
|||
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* When testing, this global variable stores the location of the
|
||||
* pending-byte in the database file.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
unsigned int sqlite3_pending_byte = 0x40000000;
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3OSTrace = 0;
|
||||
#define OSTRACE1(X) if( sqlite3OSTrace ) sqlite3DebugPrintf(X)
|
||||
|
|
140
src/os_unix.c
140
src/os_unix.c
|
@ -43,7 +43,7 @@
|
|||
** * Definitions of sqlite3_vfs objects for all locking methods
|
||||
** plus implementations of sqlite3_os_init() and sqlite3_os_end().
|
||||
**
|
||||
** $Id: os_unix.c,v 1.241 2009/02/09 17:34:07 drh Exp $
|
||||
** $Id: os_unix.c,v 1.250 2009/04/07 05:35:04 chw Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#if SQLITE_OS_UNIX /* This file is used on unix only */
|
||||
|
@ -1454,11 +1454,12 @@ static int unixUnlock(sqlite3_file *id, int locktype){
|
|||
if( IS_LOCK_ERROR(rc) ){
|
||||
pFile->lastErrno = tErrno;
|
||||
}
|
||||
goto end_unlock;
|
||||
goto end_unlock;
|
||||
}
|
||||
}
|
||||
if( locktype==NO_LOCK ){
|
||||
struct unixOpenCnt *pOpen;
|
||||
int rc2 = SQLITE_OK;
|
||||
|
||||
/* Decrement the shared lock counter. Release the lock using an
|
||||
** OS call only when all threads in this same process have released
|
||||
|
@ -1480,8 +1481,8 @@ static int unixUnlock(sqlite3_file *id, int locktype){
|
|||
if( IS_LOCK_ERROR(rc) ){
|
||||
pFile->lastErrno = tErrno;
|
||||
}
|
||||
pLock->cnt = 1;
|
||||
goto end_unlock;
|
||||
pLock->locktype = NO_LOCK;
|
||||
pFile->locktype = NO_LOCK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1489,30 +1490,31 @@ static int unixUnlock(sqlite3_file *id, int locktype){
|
|||
** count reaches zero, close any other file descriptors whose close
|
||||
** was deferred because of outstanding locks.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
pOpen = pFile->pOpen;
|
||||
pOpen->nLock--;
|
||||
assert( pOpen->nLock>=0 );
|
||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||
int i;
|
||||
for(i=0; i<pOpen->nPending; i++){
|
||||
/* close pending fds, but if closing fails don't free the array
|
||||
** assign -1 to the successfully closed descriptors and record the
|
||||
** error. The next attempt to unlock will try again. */
|
||||
if( pOpen->aPending[i] < 0 ) continue;
|
||||
if( close(pOpen->aPending[i]) ){
|
||||
pFile->lastErrno = errno;
|
||||
rc = SQLITE_IOERR_CLOSE;
|
||||
}else{
|
||||
pOpen->aPending[i] = -1;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_free(pOpen->aPending);
|
||||
pOpen->nPending = 0;
|
||||
pOpen->aPending = 0;
|
||||
pOpen = pFile->pOpen;
|
||||
pOpen->nLock--;
|
||||
assert( pOpen->nLock>=0 );
|
||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||
int i;
|
||||
for(i=0; i<pOpen->nPending; i++){
|
||||
/* close pending fds, but if closing fails don't free the array
|
||||
** assign -1 to the successfully closed descriptors and record the
|
||||
** error. The next attempt to unlock will try again. */
|
||||
if( pOpen->aPending[i] < 0 ) continue;
|
||||
if( close(pOpen->aPending[i]) ){
|
||||
pFile->lastErrno = errno;
|
||||
rc2 = SQLITE_IOERR_CLOSE;
|
||||
}else{
|
||||
pOpen->aPending[i] = -1;
|
||||
}
|
||||
}
|
||||
if( rc2==SQLITE_OK ){
|
||||
sqlite3_free(pOpen->aPending);
|
||||
pOpen->nPending = 0;
|
||||
pOpen->aPending = 0;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2211,8 +2213,8 @@ static int semClose(sqlite3_file *id) {
|
|||
unixEnterMutex();
|
||||
releaseLockInfo(pFile->pLock);
|
||||
releaseOpenCnt(pFile->pOpen);
|
||||
closeUnixFile(id);
|
||||
unixLeaveMutex();
|
||||
closeUnixFile(id);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -2824,10 +2826,12 @@ int sqlite3_fullsync_count = 0;
|
|||
#endif
|
||||
|
||||
/*
|
||||
** Use the fdatasync() API only if the HAVE_FDATASYNC macro is defined.
|
||||
** Otherwise use fsync() in its place.
|
||||
** We do not trust systems to provide a working fdatasync(). Some do.
|
||||
** Others do no. To be safe, we will stick with the (slower) fsync().
|
||||
** If you know that your system does support fdatasync() correctly,
|
||||
** then simply compile with -Dfdatasync=fdatasync
|
||||
*/
|
||||
#ifndef HAVE_FDATASYNC
|
||||
#if !defined(fdatasync) && !defined(__linux__)
|
||||
# define fdatasync fsync
|
||||
#endif
|
||||
|
||||
|
@ -2853,6 +2857,19 @@ int sqlite3_fullsync_count = 0;
|
|||
** You are strongly advised *not* to deploy with SQLITE_NO_SYNC
|
||||
** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash
|
||||
** or power failure will likely corrupt the database file.
|
||||
**
|
||||
** SQLite sets the dataOnly flag if the size of the file is unchanged.
|
||||
** The idea behind dataOnly is that it should only write the file content
|
||||
** to disk, not the inode. We only set dataOnly if the file size is
|
||||
** unchanged since the file size is part of the inode. However,
|
||||
** Ted Ts'o tells us that fdatasync() will also write the inode if the
|
||||
** file size has changed. The only real difference between fdatasync()
|
||||
** and fsync(), Ted tells us, is that fdatasync() will not flush the
|
||||
** inode if the mtime or owner or other inode attributes have changed.
|
||||
** We only care about the file size, not the other file attributes, so
|
||||
** as far as SQLite is concerned, an fdatasync() is always adequate.
|
||||
** So, we always use fdatasync() if it is available, regardless of
|
||||
** the value of the dataOnly flag.
|
||||
*/
|
||||
static int full_fsync(int fd, int fullSync, int dataOnly){
|
||||
int rc;
|
||||
|
@ -2869,6 +2886,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
|||
UNUSED_PARAMETER(dataOnly);
|
||||
#else
|
||||
UNUSED_PARAMETER(fullSync);
|
||||
UNUSED_PARAMETER(dataOnly);
|
||||
#endif
|
||||
|
||||
/* Record the number of times that we do a normal fsync() and
|
||||
|
@ -2902,16 +2920,12 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
|||
if( rc ) rc = fsync(fd);
|
||||
|
||||
#else
|
||||
if( dataOnly ){
|
||||
rc = fdatasync(fd);
|
||||
rc = fdatasync(fd);
|
||||
#if OS_VXWORKS
|
||||
if( rc==-1 && errno==ENOTSUP ){
|
||||
rc = fsync(fd);
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
if( rc==-1 && errno==ENOTSUP ){
|
||||
rc = fsync(fd);
|
||||
}
|
||||
#endif /* OS_VXWORKS */
|
||||
#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */
|
||||
|
||||
if( OS_VXWORKS && rc!= -1 ){
|
||||
|
@ -3193,7 +3207,7 @@ IOMETHODS(
|
|||
dotlockCheckReservedLock /* xCheckReservedLock method */
|
||||
)
|
||||
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
|
||||
IOMETHODS(
|
||||
flockIoFinder, /* Finder function name */
|
||||
flockIoMethods, /* sqlite3_io_methods object name */
|
||||
|
@ -3317,6 +3331,44 @@ static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int)
|
|||
|
||||
#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
||||
|
||||
#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE
|
||||
/*
|
||||
** This "finder" function attempts to determine the best locking strategy
|
||||
** for the database file "filePath". It then returns the sqlite3_io_methods
|
||||
** object that implements that strategy.
|
||||
**
|
||||
** This is for VXWorks only.
|
||||
*/
|
||||
static const sqlite3_io_methods *autolockIoFinderImpl(
|
||||
const char *filePath, /* name of the database file */
|
||||
int fd /* file descriptor open on the database file */
|
||||
){
|
||||
struct flock lockInfo;
|
||||
|
||||
if( !filePath ){
|
||||
/* If filePath==NULL that means we are dealing with a transient file
|
||||
** that does not need to be locked. */
|
||||
return &nolockIoMethods;
|
||||
}
|
||||
|
||||
/* Test if fcntl() is supported and use POSIX style locks.
|
||||
** Otherwise fall back to the named semaphore method.
|
||||
*/
|
||||
lockInfo.l_len = 1;
|
||||
lockInfo.l_start = 0;
|
||||
lockInfo.l_whence = SEEK_SET;
|
||||
lockInfo.l_type = F_RDLCK;
|
||||
if( fcntl(fd, F_GETLK, &lockInfo)!=-1 ) {
|
||||
return &posixIoMethods;
|
||||
}else{
|
||||
return &semIoMethods;
|
||||
}
|
||||
}
|
||||
static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int)
|
||||
= autolockIoFinderImpl;
|
||||
|
||||
#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */
|
||||
|
||||
/*
|
||||
** An abstract type for a pointer to a IO method finder function:
|
||||
*/
|
||||
|
@ -3599,7 +3651,7 @@ static int unixOpen(
|
|||
int flags, /* Input flags to control the opening */
|
||||
int *pOutFlags /* Output flags returned to SQLite core */
|
||||
){
|
||||
int fd = 0; /* File descriptor returned by open() */
|
||||
int fd = -1; /* File descriptor returned by open() */
|
||||
int dirfd = -1; /* Directory file descriptor */
|
||||
int openFlags = 0; /* Flags to pass to open() */
|
||||
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
||||
|
@ -3702,7 +3754,7 @@ static int unixOpen(
|
|||
}
|
||||
#endif
|
||||
|
||||
assert(fd!=0);
|
||||
assert( fd>=0 );
|
||||
if( isOpenDirectory ){
|
||||
rc = openDirectory(zPath, &dirfd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -3984,16 +4036,18 @@ static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
|
|||
sp.tv_sec = microseconds / 1000000;
|
||||
sp.tv_nsec = (microseconds % 1000000) * 1000;
|
||||
nanosleep(&sp, NULL);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return microseconds;
|
||||
#elif defined(HAVE_USLEEP) && HAVE_USLEEP
|
||||
usleep(microseconds);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return microseconds;
|
||||
#else
|
||||
int seconds = (microseconds+999999)/1000000;
|
||||
sleep(seconds);
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return seconds*1000000;
|
||||
#endif
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5045,7 +5099,7 @@ int sqlite3_os_init(void){
|
|||
** array cannot be const.
|
||||
*/
|
||||
static sqlite3_vfs aVfs[] = {
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__))
|
||||
UNIXVFS("unix", autolockIoFinder ),
|
||||
#else
|
||||
UNIXVFS("unix", posixIoFinder ),
|
||||
|
@ -5057,8 +5111,10 @@ int sqlite3_os_init(void){
|
|||
#endif
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
UNIXVFS("unix-posix", posixIoFinder ),
|
||||
#if !OS_VXWORKS
|
||||
UNIXVFS("unix-flock", flockIoFinder ),
|
||||
#endif
|
||||
#endif
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
||||
UNIXVFS("unix-afp", afpIoFinder ),
|
||||
UNIXVFS("unix-proxy", proxyIoFinder ),
|
||||
|
|
178
src/os_win.c
178
src/os_win.c
|
@ -12,7 +12,7 @@
|
|||
**
|
||||
** This file contains code that is specific to windows.
|
||||
**
|
||||
** $Id: os_win.c,v 1.148 2009/02/05 03:16:21 shane Exp $
|
||||
** $Id: os_win.c,v 1.154 2009/04/09 14:27:07 chw Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#if SQLITE_OS_WIN /* This file is used for windows only */
|
||||
|
@ -75,6 +75,7 @@
|
|||
*/
|
||||
#if SQLITE_OS_WINCE
|
||||
# define AreFileApisANSI() 1
|
||||
# define GetDiskFreeSpaceW() 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -101,6 +102,7 @@ struct winFile {
|
|||
unsigned char locktype; /* Type of lock currently held on this file */
|
||||
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
|
||||
DWORD lastErrno; /* The Windows errno from the last I/O error */
|
||||
DWORD sectorSize; /* Sector size of the device file is on */
|
||||
#if SQLITE_OS_WINCE
|
||||
WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
|
||||
HANDLE hMutex; /* Mutex used to control access to shared lock */
|
||||
|
@ -110,6 +112,13 @@ struct winFile {
|
|||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Forward prototypes.
|
||||
*/
|
||||
static int getSectorSize(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zRelative /* UTF-8 file name */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following variable is (normally) set once and never changes
|
||||
|
@ -135,7 +144,7 @@ static int sqlite3_os_type = 0;
|
|||
**
|
||||
** Here is an interesting observation: Win95, Win98, and WinME lack
|
||||
** the LockFileEx() API. But we can still statically link against that
|
||||
** API as long as we don't call it win running Win95/98/ME. A call to
|
||||
** API as long as we don't call it when running Win95/98/ME. A call to
|
||||
** this routine is used to determine if the host is Win95/98/ME or
|
||||
** WinNT/2K/XP so that we will know whether or not we can safely call
|
||||
** the LockFileEx() API.
|
||||
|
@ -610,6 +619,8 @@ static BOOL winceLockFileEx(
|
|||
static int winClose(sqlite3_file *id){
|
||||
int rc, cnt = 0;
|
||||
winFile *pFile = (winFile*)id;
|
||||
|
||||
assert( id!=0 );
|
||||
OSTRACE2("CLOSE %d\n", pFile->h);
|
||||
do{
|
||||
rc = CloseHandle(pFile->h);
|
||||
|
@ -654,9 +665,10 @@ static int winRead(
|
|||
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(offset & 0xffffffff);
|
||||
DWORD rc;
|
||||
DWORD got;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error;
|
||||
DWORD got;
|
||||
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
OSTRACE3("READ %d lock=%d\n", pFile->h, pFile->locktype);
|
||||
|
@ -691,9 +703,10 @@ static int winWrite(
|
|||
LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(offset & 0xffffffff);
|
||||
DWORD rc;
|
||||
DWORD wrote = 0;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error;
|
||||
DWORD wrote = 0;
|
||||
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_WRITE);
|
||||
SimulateDiskfullError(return SQLITE_FULL);
|
||||
|
@ -723,26 +736,26 @@ static int winWrite(
|
|||
** Truncate an open file to a specified size
|
||||
*/
|
||||
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
||||
DWORD rc;
|
||||
LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff);
|
||||
LONG lowerBits = (LONG)(nByte & 0xffffffff);
|
||||
DWORD rc;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD error = NO_ERROR;
|
||||
DWORD error;
|
||||
|
||||
assert( id!=0 );
|
||||
OSTRACE3("TRUNCATE %d %lld\n", pFile->h, nByte);
|
||||
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
||||
rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
||||
if( INVALID_SET_FILE_POINTER == rc ){
|
||||
error = GetLastError();
|
||||
if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
if( error == NO_ERROR ){
|
||||
/* SetEndOfFile will fail if nByte is negative */
|
||||
if( SetEndOfFile(pFile->h) ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
error = GetLastError();
|
||||
/* SetEndOfFile will fail if nByte is negative */
|
||||
if( !SetEndOfFile(pFile->h) ){
|
||||
pFile->lastErrno = GetLastError();
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
pFile->lastErrno = error;
|
||||
return SQLITE_IOERR_TRUNCATE;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
@ -760,6 +773,8 @@ int sqlite3_fullsync_count = 0;
|
|||
static int winSync(sqlite3_file *id, int flags){
|
||||
#ifndef SQLITE_NO_SYNC
|
||||
winFile *pFile = (winFile*)id;
|
||||
|
||||
assert( id!=0 );
|
||||
OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype);
|
||||
#else
|
||||
UNUSED_PARAMETER(id);
|
||||
|
@ -791,9 +806,12 @@ static int winSync(sqlite3_file *id, int flags){
|
|||
** Determine the current size of a file in bytes
|
||||
*/
|
||||
static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
|
||||
DWORD upperBits;
|
||||
DWORD lowerBits;
|
||||
winFile *pFile = (winFile*)id;
|
||||
DWORD upperBits, lowerBits;
|
||||
DWORD error;
|
||||
|
||||
assert( id!=0 );
|
||||
SimulateIOError(return SQLITE_IOERR_FSTAT);
|
||||
lowerBits = GetFileSize(pFile->h, &upperBits);
|
||||
if( (lowerBits == INVALID_FILE_SIZE)
|
||||
|
@ -897,7 +915,7 @@ static int winLock(sqlite3_file *id, int locktype){
|
|||
winFile *pFile = (winFile*)id;
|
||||
DWORD error = NO_ERROR;
|
||||
|
||||
assert( pFile!=0 );
|
||||
assert( id!=0 );
|
||||
OSTRACE5("LOCK %d %d was %d(%d)\n",
|
||||
pFile->h, locktype, pFile->locktype, pFile->sharedLockByte);
|
||||
|
||||
|
@ -1015,7 +1033,8 @@ static int winLock(sqlite3_file *id, int locktype){
|
|||
static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
|
||||
int rc;
|
||||
winFile *pFile = (winFile*)id;
|
||||
assert( pFile!=0 );
|
||||
|
||||
assert( id!=0 );
|
||||
if( pFile->locktype>=RESERVED_LOCK ){
|
||||
rc = 1;
|
||||
OSTRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc);
|
||||
|
@ -1100,8 +1119,8 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
|||
** same for both.
|
||||
*/
|
||||
static int winSectorSize(sqlite3_file *id){
|
||||
UNUSED_PARAMETER(id);
|
||||
return SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
assert( id!=0 );
|
||||
return (int)(((winFile*)id)->sectorSize);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1245,7 +1264,6 @@ static int getLastErrorMsg(int nBuf, char *zBuf){
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Open a file.
|
||||
*/
|
||||
|
@ -1269,6 +1287,7 @@ static int winOpen(
|
|||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||||
char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
|
||||
|
||||
assert( id!=0 );
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
|
||||
/* If the second argument to this function is NULL, generate a
|
||||
|
@ -1348,7 +1367,7 @@ static int winOpen(
|
|||
if( h==INVALID_HANDLE_VALUE ){
|
||||
free(zConverted);
|
||||
if( flags & SQLITE_OPEN_READWRITE ){
|
||||
return winOpen(0, zName, id,
|
||||
return winOpen(pVfs, zName, id,
|
||||
((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
|
||||
}else{
|
||||
return SQLITE_CANTOPEN;
|
||||
|
@ -1365,6 +1384,7 @@ static int winOpen(
|
|||
pFile->pMethod = &winIoMethod;
|
||||
pFile->h = h;
|
||||
pFile->lastErrno = NO_ERROR;
|
||||
pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
|
||||
#if SQLITE_OS_WINCE
|
||||
if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
|
||||
(SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
|
||||
|
@ -1556,6 +1576,73 @@ static int winFullPathname(
|
|||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Get the sector size of the device used to store
|
||||
** file.
|
||||
*/
|
||||
static int getSectorSize(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zRelative /* UTF-8 file name */
|
||||
){
|
||||
DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
char zFullpath[MAX_PATH+1];
|
||||
int rc;
|
||||
DWORD dwRet = 0, dwDummy;
|
||||
|
||||
/*
|
||||
** We need to get the full path name of the file
|
||||
** to get the drive letter to look up the sector
|
||||
** size.
|
||||
*/
|
||||
rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
|
||||
if( rc == SQLITE_OK )
|
||||
{
|
||||
void *zConverted = convertUtf8Filename(zFullpath);
|
||||
if( zConverted ){
|
||||
if( isNT() ){
|
||||
int i;
|
||||
/* trim path to just drive reference */
|
||||
WCHAR *p = zConverted;
|
||||
for(i=0;i<MAX_PATH;i++){
|
||||
if( p[i] == '\\' ){
|
||||
i++;
|
||||
p[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
|
||||
&dwDummy,
|
||||
&bytesPerSector,
|
||||
&dwDummy,
|
||||
&dwDummy);
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
int i;
|
||||
/* trim path to just drive reference */
|
||||
CHAR *p = (CHAR *)zConverted;
|
||||
for(i=0;i<MAX_PATH;i++){
|
||||
if( p[i] == '\\' ){
|
||||
i++;
|
||||
p[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
dwRet = GetDiskFreeSpaceA((CHAR*)zConverted,
|
||||
&dwDummy,
|
||||
&bytesPerSector,
|
||||
&dwDummy,
|
||||
&dwDummy);
|
||||
#endif
|
||||
}
|
||||
free(zConverted);
|
||||
}
|
||||
if( !dwRet ){
|
||||
bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
}
|
||||
return (int) bytesPerSector;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
/*
|
||||
** Interfaces for opening a shared library, finding entry points
|
||||
|
@ -1677,7 +1764,21 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
|||
/* FILETIME structure is a 64-bit value representing the number of
|
||||
100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
|
||||
*/
|
||||
sqlite3_int64 timeW, timeF;
|
||||
sqlite3_int64 timeW; /* Whole days */
|
||||
sqlite3_int64 timeF; /* Fractional Days */
|
||||
|
||||
/* Number of 100-nanosecond intervals in a single day */
|
||||
static const sqlite3_int64 ntuPerDay =
|
||||
10000000*(sqlite3_int64)86400;
|
||||
|
||||
/* Number of 100-nanosecond intervals in half of a day */
|
||||
static const sqlite3_int64 ntuPerHalfDay =
|
||||
10000000*(sqlite3_int64)43200;
|
||||
|
||||
/* 2^32 - to avoid use of LL and warnings in gcc */
|
||||
static const sqlite3_int64 max32BitValue =
|
||||
(sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296;
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
|
@ -1689,25 +1790,14 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
|||
GetSystemTimeAsFileTime( &ft );
|
||||
#endif
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
#if defined(_MSC_VER)
|
||||
timeW = (((sqlite3_int64)ft.dwHighDateTime)*4294967296) + ft.dwLowDateTime;
|
||||
timeF = timeW % 864000000000; /* fractional days (100-nanoseconds) */
|
||||
timeW = timeW / 864000000000; /* whole days */
|
||||
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
|
||||
timeF = timeF + 432000000000; /* add half a day (from 2305813.5) */
|
||||
timeW = timeW + (timeF / 864000000000); /* add whole day if half day made one */
|
||||
timeF = timeF % 864000000000; /* compute new fractional days */
|
||||
*prNow = (double)timeW + ((double)timeF / (double)864000000000);
|
||||
#else
|
||||
timeW = (((sqlite3_int64)ft.dwHighDateTime)*4294967296LL) + ft.dwLowDateTime;
|
||||
timeF = timeW % 864000000000LL; /* fractional days (100-nanoseconds) */
|
||||
timeW = timeW / 864000000000LL; /* whole days */
|
||||
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
|
||||
timeF = timeF + 432000000000LL; /* add half a day (from 2305813.5) */
|
||||
timeW = timeW + (timeF / 864000000000LL); /* add whole day if half day made one */
|
||||
timeF = timeF % 864000000000LL; /* compute new fractional days */
|
||||
*prNow = (double)timeW + ((double)timeF / (double)864000000000LL);
|
||||
#endif
|
||||
timeW = (((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime;
|
||||
timeF = timeW % ntuPerDay; /* fractional days (100-nanoseconds) */
|
||||
timeW = timeW / ntuPerDay; /* whole days */
|
||||
timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
|
||||
timeF = timeF + ntuPerHalfDay; /* add half a day (from 2305813.5) */
|
||||
timeW = timeW + (timeF/ntuPerDay); /* add whole day if half day made one */
|
||||
timeF = timeF % ntuPerDay; /* compute new fractional days */
|
||||
*prNow = (double)timeW + ((double)timeF / (double)ntuPerDay);
|
||||
#ifdef SQLITE_TEST
|
||||
if( sqlite3_current_time ){
|
||||
*prNow = ((double)sqlite3_current_time + (double)43200) / (double)86400 + (double)2440587;
|
||||
|
@ -1723,7 +1813,7 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
|||
** function, SQLite calls this function with zBuf pointing to
|
||||
** a buffer of nBuf bytes. The OS layer should populate the
|
||||
** buffer with a nul-terminated UTF-8 encoded error message
|
||||
** describing the last IO error to have occured within the calling
|
||||
** describing the last IO error to have occurred within the calling
|
||||
** thread.
|
||||
**
|
||||
** If the error message is too large for the supplied buffer,
|
||||
|
|
169
src/pager.c
169
src/pager.c
|
@ -18,7 +18,7 @@
|
|||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.570 2009/02/17 17:56:30 danielk1977 Exp $
|
||||
** @(#) $Id: pager.c,v 1.580 2009/04/11 16:27:50 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
#include "sqliteInt.h"
|
||||
|
@ -99,12 +99,6 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
|
|||
#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
|
||||
#define PAGER_SYNCED 5
|
||||
|
||||
/*
|
||||
** This macro rounds values up so that if the value is an address it
|
||||
** is guaranteed to be an address that is aligned to an 8-byte boundary.
|
||||
*/
|
||||
#define FORCE_ALIGNMENT(X) (((X)+7)&~7)
|
||||
|
||||
/*
|
||||
** A macro used for invoking the codec if there is one
|
||||
*/
|
||||
|
@ -762,7 +756,7 @@ static int writeJournalHdr(Pager *pPager){
|
|||
** A faster alternative is to write 0xFFFFFFFF to the nRec field. When
|
||||
** reading the journal this value tells SQLite to assume that the
|
||||
** rest of the journal file contains valid page records. This assumption
|
||||
** is dangerous, as if a failure occured whilst writing to the journal
|
||||
** is dangerous, as if a failure occurred whilst writing to the journal
|
||||
** file it may contain some garbage data. There are two scenarios
|
||||
** where this risk can be ignored:
|
||||
**
|
||||
|
@ -1143,7 +1137,7 @@ static void pager_unlock(Pager *pPager){
|
|||
|
||||
/*
|
||||
** This function should be called when an IOERR, CORRUPT or FULL error
|
||||
** may have occured. The first argument is a pointer to the pager
|
||||
** may have occurred. The first argument is a pointer to the pager
|
||||
** structure, the second the error-code about to be returned by a pager
|
||||
** API function. The value returned is a copy of the second argument
|
||||
** to this function.
|
||||
|
@ -1156,7 +1150,7 @@ static void pager_unlock(Pager *pPager){
|
|||
** A persistent error indicates that the contents of the pager-cache
|
||||
** cannot be trusted. This state can be cleared by completely discarding
|
||||
** the contents of the pager-cache. If a transaction was active when
|
||||
** the persistent error occured, then the rollback journal may need
|
||||
** the persistent error occurred, then the rollback journal may need
|
||||
** to be replayed to restore the contents of the database file (as if
|
||||
** it were a hot-journal).
|
||||
*/
|
||||
|
@ -1505,6 +1499,7 @@ static int pager_playback_one_page(
|
|||
** Do not attempt to write if database file has never been opened.
|
||||
*/
|
||||
pPg = pager_lookup(pPager, pgno);
|
||||
assert( pPg || !MEMDB );
|
||||
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
|
||||
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData),
|
||||
(isMainJrnl?"main-journal":"sub-journal")
|
||||
|
@ -2029,11 +2024,11 @@ static int pager_playback(Pager *pPager, int isHot){
|
|||
pPager->journalOff = szJ;
|
||||
break;
|
||||
}else{
|
||||
/* If we are unable to rollback, then the database is probably
|
||||
** going to end up being corrupt. It is corrupt to us, anyhow.
|
||||
** Perhaps the next process to come along can fix it....
|
||||
/* If we are unable to rollback, quit and return the error
|
||||
** code. This will cause the pager to enter the error state
|
||||
** so that no further harm will be done. Perhaps the next
|
||||
** process to come along will be able to rollback the database.
|
||||
*/
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto end_playback;
|
||||
}
|
||||
}
|
||||
|
@ -2054,7 +2049,7 @@ end_playback:
|
|||
);
|
||||
|
||||
/* If this playback is happening automatically as a result of an IO or
|
||||
** malloc error that occured after the change-counter was updated but
|
||||
** malloc error that occurred after the change-counter was updated but
|
||||
** before the transaction was committed, then the change-counter
|
||||
** modification may just have been reverted. If this happens in exclusive
|
||||
** mode, then subsequent transactions performed by the connection will not
|
||||
|
@ -3119,9 +3114,9 @@ int sqlite3PagerOpen(
|
|||
** source file journal.c).
|
||||
*/
|
||||
if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){
|
||||
journalFileSize = sqlite3JournalSize(pVfs);
|
||||
journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
|
||||
}else{
|
||||
journalFileSize = sqlite3MemJournalSize();
|
||||
journalFileSize = ROUND8(sqlite3MemJournalSize());
|
||||
}
|
||||
|
||||
/* Set the output variable to NULL in case an error occurs. */
|
||||
|
@ -3177,23 +3172,25 @@ int sqlite3PagerOpen(
|
|||
** Journal file name (nPathname+8+1 bytes)
|
||||
*/
|
||||
pPtr = (u8 *)sqlite3MallocZero(
|
||||
sizeof(*pPager) + /* Pager structure */
|
||||
pcacheSize + /* PCache object */
|
||||
pVfs->szOsFile + /* The main db file */
|
||||
journalFileSize * 2 + /* The two journal files */
|
||||
nPathname + 1 + /* zFilename */
|
||||
nPathname + 8 + 1 /* zJournal */
|
||||
ROUND8(sizeof(*pPager)) + /* Pager structure */
|
||||
ROUND8(pcacheSize) + /* PCache object */
|
||||
ROUND8(pVfs->szOsFile) + /* The main db file */
|
||||
journalFileSize * 2 + /* The two journal files */
|
||||
nPathname + 1 + /* zFilename */
|
||||
nPathname + 8 + 1 /* zJournal */
|
||||
);
|
||||
assert( EIGHT_BYTE_ALIGNMENT(journalFileSize) );
|
||||
if( !pPtr ){
|
||||
sqlite3_free(zPathname);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pPager = (Pager*)(pPtr);
|
||||
pPager->pPCache = (PCache*)(pPtr += sizeof(*pPager));
|
||||
pPager->fd = (sqlite3_file*)(pPtr += pcacheSize);
|
||||
pPager->sjfd = (sqlite3_file*)(pPtr += pVfs->szOsFile);
|
||||
pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager)));
|
||||
pPager->fd = (sqlite3_file*)(pPtr += ROUND8(pcacheSize));
|
||||
pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile));
|
||||
pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize);
|
||||
pPager->zFilename = (char*)(pPtr += journalFileSize);
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
|
||||
|
||||
/* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */
|
||||
if( zPathname ){
|
||||
|
@ -3268,7 +3265,7 @@ int sqlite3PagerOpen(
|
|||
testcase( rc!=SQLITE_OK );
|
||||
}
|
||||
|
||||
/* If an error occured in either of the blocks above, free the
|
||||
/* If an error occurred in either of the blocks above, free the
|
||||
** Pager structure and close the file.
|
||||
*/
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -3279,7 +3276,7 @@ int sqlite3PagerOpen(
|
|||
}
|
||||
|
||||
/* Initialize the PCache object. */
|
||||
nExtra = FORCE_ALIGNMENT(nExtra);
|
||||
nExtra = ROUND8(nExtra);
|
||||
sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
|
||||
!memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
|
||||
|
||||
|
@ -3335,11 +3332,12 @@ int sqlite3PagerOpen(
|
|||
** PAGER_SHARED state. It tests if there is a hot journal present in
|
||||
** the file-system for the given pager. A hot journal is one that
|
||||
** needs to be played back. According to this function, a hot-journal
|
||||
** file exists if the following three criteria are met:
|
||||
** file exists if the following criteria are met:
|
||||
**
|
||||
** * The journal file exists in the file system, and
|
||||
** * No process holds a RESERVED or greater lock on the database file, and
|
||||
** * The database file itself is greater than 0 bytes in size.
|
||||
** * The database file itself is greater than 0 bytes in size, and
|
||||
** * The first byte of the journal file exists and is not 0x00.
|
||||
**
|
||||
** If the current size of the database file is 0 but a journal file
|
||||
** exists, that is probably an old journal left over from a prior
|
||||
|
@ -3347,14 +3345,12 @@ int sqlite3PagerOpen(
|
|||
** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK
|
||||
** is returned.
|
||||
**
|
||||
** This routine does not open the journal file to examine its
|
||||
** content. Hence, the journal might contain the name of a master
|
||||
** journal file that has been deleted, and hence not be hot. Or
|
||||
** the header of the journal might be zeroed out. This routine
|
||||
** does not discover these cases of a non-hot journal - if the
|
||||
** journal file exists and is not empty this routine assumes it
|
||||
** is hot. The pager_playback() routine will discover that the
|
||||
** journal file is not really hot and will no-op.
|
||||
** This routine does not check if there is a master journal filename
|
||||
** at the end of the file. If there is, and that master journal file
|
||||
** does not exist, then the journal file is not really hot. In this
|
||||
** case this routine will return a false-positive. The pager_playback()
|
||||
** routine will discover that the journal file is not really hot and
|
||||
** will not roll it back.
|
||||
**
|
||||
** If a hot-journal file is found to exist, *pExists is set to 1 and
|
||||
** SQLITE_OK returned. If no hot-journal file is present, *pExists is
|
||||
|
@ -3365,29 +3361,52 @@ int sqlite3PagerOpen(
|
|||
static int hasHotJournal(Pager *pPager, int *pExists){
|
||||
sqlite3_vfs * const pVfs = pPager->pVfs;
|
||||
int rc; /* Return code */
|
||||
int exists = 0; /* True if a journal file is present */
|
||||
int locked = 0; /* True if some process holds a RESERVED lock */
|
||||
int exists; /* True if a journal file is present */
|
||||
|
||||
assert( pPager!=0 );
|
||||
assert( pPager->useJournal );
|
||||
assert( isOpen(pPager->fd) );
|
||||
assert( !isOpen(pPager->jfd) );
|
||||
|
||||
*pExists = 0;
|
||||
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc==SQLITE_OK && exists ){
|
||||
int locked; /* True if some process holds a RESERVED lock */
|
||||
rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
|
||||
if( rc==SQLITE_OK && !locked ){
|
||||
int nPage;
|
||||
|
||||
/* Check the size of the database file. If it consists of 0 pages,
|
||||
** then delete the journal file. See the header comment above for
|
||||
** the reasoning here.
|
||||
*/
|
||||
rc = sqlite3PagerPagecount(pPager, &nPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( nPage==0 ){
|
||||
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
|
||||
if( nPage==0 ){
|
||||
rc = sqlite3OsDelete(pVfs, pPager->zJournal, 0);
|
||||
}else{
|
||||
*pExists = 1;
|
||||
/* The journal file exists and no other connection has a reserved
|
||||
** or greater lock on the database file. Now check that there is
|
||||
** at least one non-zero bytes at the start of the journal file.
|
||||
** If there is, then we consider this journal to be hot. If not,
|
||||
** it can be ignored.
|
||||
*/
|
||||
int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
|
||||
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 first = 0;
|
||||
rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
|
||||
if( rc==SQLITE_IOERR_SHORT_READ ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
sqlite3OsClose(pPager->jfd);
|
||||
*pExists = (first!=0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -3413,10 +3432,13 @@ static int readDbPage(PgHdr *pPg){
|
|||
if( !isOpen(pPager->fd) ){
|
||||
assert( pPager->tempFile );
|
||||
memset(pPg->pData, 0, pPager->pageSize);
|
||||
return SQLITE_IOERR_SHORT_READ;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
iOffset = (pgno-1)*(i64)pPager->pageSize;
|
||||
rc = sqlite3OsRead(pPager->fd, pPg->pData, pPager->pageSize, iOffset);
|
||||
if( rc==SQLITE_IOERR_SHORT_READ ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
if( pgno==1 ){
|
||||
u8 *dbFileVers = &((u8*)pPg->pData)[24];
|
||||
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
|
||||
|
@ -3800,7 +3822,7 @@ int sqlite3PagerAcquire(
|
|||
}else{
|
||||
assert( pPg->pPager==pPager );
|
||||
rc = readDbPage(pPg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
|
||||
if( rc!=SQLITE_OK ){
|
||||
pagerDropPage(pPg);
|
||||
return rc;
|
||||
}
|
||||
|
@ -3933,7 +3955,7 @@ static int pager_open_journal(Pager *pPager){
|
|||
sqlite3MemJournalOpen(pPager->jfd);
|
||||
}else{
|
||||
const int flags = /* VFS flags to open journal file */
|
||||
SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_CREATE|
|
||||
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
|
||||
(pPager->tempFile ?
|
||||
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
|
||||
(SQLITE_OPEN_MAIN_JOURNAL)
|
||||
|
@ -4135,7 +4157,7 @@ static int pager_write(PgHdr *pPg){
|
|||
pPager->needSync = 1;
|
||||
}
|
||||
|
||||
/* An error has occured writing to the journal file. The
|
||||
/* An error has occurred writing to the journal file. The
|
||||
** transaction will be rolled back by the layer above.
|
||||
*/
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -4851,7 +4873,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
|
|||
** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE.
|
||||
** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with
|
||||
** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes
|
||||
** that have occured since the specified savepoint was created.
|
||||
** that have occurred since the specified savepoint was created.
|
||||
**
|
||||
** The savepoint to rollback or release is identified by parameter
|
||||
** iSavepoint. A value of 0 means to operate on the outermost savepoint
|
||||
|
@ -4997,6 +5019,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
|||
PgHdr *pPgOld; /* The page being overwritten. */
|
||||
Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
|
||||
int rc; /* Return code */
|
||||
Pgno origPgno; /* The original page number */
|
||||
|
||||
assert( pPg->nRef>0 );
|
||||
|
||||
|
@ -5053,13 +5076,11 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
|||
assert( !pPgOld || pPgOld->nRef==1 );
|
||||
if( pPgOld ){
|
||||
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
|
||||
}
|
||||
|
||||
sqlite3PcacheMove(pPg, pgno);
|
||||
if( pPgOld ){
|
||||
sqlite3PcacheDrop(pPgOld);
|
||||
}
|
||||
|
||||
origPgno = pPg->pgno;
|
||||
sqlite3PcacheMove(pPg, pgno);
|
||||
sqlite3PcacheMakeDirty(pPg);
|
||||
pPager->dbModified = 1;
|
||||
|
||||
|
@ -5097,6 +5118,19 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
|||
sqlite3PagerUnref(pPgHdr);
|
||||
}
|
||||
|
||||
/*
|
||||
** For an in-memory database, make sure the original page continues
|
||||
** to exist, in case the transaction needs to roll back. We allocate
|
||||
** the page now, instead of at rollback, because we can better deal
|
||||
** with an out-of-memory error now. Ticket #3761.
|
||||
*/
|
||||
if( MEMDB ){
|
||||
DbPage *pNew;
|
||||
rc = sqlite3PagerAcquire(pPager, origPgno, &pNew, 1);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3PagerUnref(pNew);
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
|
@ -5151,30 +5185,33 @@ int sqlite3PagerLockingMode(Pager *pPager, int eMode){
|
|||
** PAGER_JOURNALMODE_MEMORY
|
||||
**
|
||||
** If the parameter is not _QUERY, then the journal-mode is set to the
|
||||
** value specified.
|
||||
** value specified. Except, an in-memory database can only have its
|
||||
** journal mode set to _OFF or _MEMORY. Attempts to change the journal
|
||||
** mode of an in-memory database to something other than _OFF or _MEMORY
|
||||
** are silently ignored.
|
||||
**
|
||||
** The returned indicate the current (possibly updated) journal-mode.
|
||||
*/
|
||||
int sqlite3PagerJournalMode(Pager *pPager, int eMode){
|
||||
if( !MEMDB ){
|
||||
assert( eMode==PAGER_JOURNALMODE_QUERY
|
||||
|| eMode==PAGER_JOURNALMODE_DELETE
|
||||
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|
||||
|| eMode==PAGER_JOURNALMODE_PERSIST
|
||||
|| eMode==PAGER_JOURNALMODE_OFF
|
||||
|| eMode==PAGER_JOURNALMODE_MEMORY );
|
||||
assert( PAGER_JOURNALMODE_QUERY<0 );
|
||||
if( eMode>=0 ){
|
||||
pPager->journalMode = (u8)eMode;
|
||||
}else{
|
||||
assert( eMode==PAGER_JOURNALMODE_QUERY );
|
||||
}
|
||||
assert( eMode==PAGER_JOURNALMODE_QUERY
|
||||
|| eMode==PAGER_JOURNALMODE_DELETE
|
||||
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|
||||
|| eMode==PAGER_JOURNALMODE_PERSIST
|
||||
|| eMode==PAGER_JOURNALMODE_OFF
|
||||
|| eMode==PAGER_JOURNALMODE_MEMORY );
|
||||
assert( PAGER_JOURNALMODE_QUERY<0 );
|
||||
if( eMode>=0 && (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY
|
||||
|| eMode==PAGER_JOURNALMODE_OFF) ){
|
||||
pPager->journalMode = (u8)eMode;
|
||||
}
|
||||
return (int)pPager->journalMode;
|
||||
}
|
||||
|
||||
/*
|
||||
** Get/set the size-limit used for persistent journal files.
|
||||
**
|
||||
** Setting the size limit to -1 means no limit is enforced.
|
||||
** An attempt to set a limit smaller than -1 is a no-op.
|
||||
*/
|
||||
i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){
|
||||
if( iLimit>=-1 ){
|
||||
|
|
61
src/parse.y
61
src/parse.y
|
@ -14,7 +14,7 @@
|
|||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.268 2009/01/29 19:27:47 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.274 2009/04/06 14:16:43 drh Exp $
|
||||
*/
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
|
@ -133,9 +133,13 @@ cmd ::= ROLLBACK trans_opt TO savepoint_opt nm(X). {
|
|||
///////////////////// The CREATE TABLE statement ////////////////////////////
|
||||
//
|
||||
cmd ::= create_table create_table_args.
|
||||
create_table ::= CREATE temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). {
|
||||
create_table ::= createkw temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). {
|
||||
sqlite3StartTable(pParse,&Y,&Z,T,0,0,E);
|
||||
}
|
||||
createkw(A) ::= CREATE(X). {
|
||||
pParse->db->lookaside.bEnabled = 0;
|
||||
A = X;
|
||||
}
|
||||
%type ifnotexists {int}
|
||||
ifnotexists(A) ::= . {A = 0;}
|
||||
ifnotexists(A) ::= IF NOT EXISTS. {A = 1;}
|
||||
|
@ -174,6 +178,7 @@ columnid(A) ::= nm(X). {
|
|||
//
|
||||
%type id {Token}
|
||||
id(A) ::= ID(X). {A = X;}
|
||||
id(A) ::= INDEXED(X). {A = X;}
|
||||
|
||||
// The following directive causes tokens ABORT, AFTER, ASC, etc. to
|
||||
// fallback to ID if they will not parse as their original value.
|
||||
|
@ -224,7 +229,7 @@ ids(A) ::= ID|STRING(X). {A = X;}
|
|||
// The name of a column or table can be any of the following:
|
||||
//
|
||||
%type nm {Token}
|
||||
nm(A) ::= ID(X). {A = X;}
|
||||
nm(A) ::= id(X). {A = X;}
|
||||
nm(A) ::= STRING(X). {A = X;}
|
||||
nm(A) ::= JOIN_KW(X). {A = X;}
|
||||
|
||||
|
@ -364,7 +369,7 @@ ifexists(A) ::= . {A = 0;}
|
|||
///////////////////// The CREATE VIEW statement /////////////////////////////
|
||||
//
|
||||
%ifndef SQLITE_OMIT_VIEW
|
||||
cmd ::= CREATE(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) AS select(S). {
|
||||
cmd ::= createkw(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) AS select(S). {
|
||||
sqlite3CreateView(pParse, &X, &Y, &Z, S, T, E);
|
||||
}
|
||||
cmd ::= DROP VIEW ifexists(E) fullname(X). {
|
||||
|
@ -697,7 +702,7 @@ inscollist(A) ::= nm(Y).
|
|||
expr(A) ::= term(X). {A = X;}
|
||||
expr(A) ::= LP(B) expr(X) RP(E). {A = X; sqlite3ExprSpan(A,&B,&E); }
|
||||
term(A) ::= NULL(X). {A = sqlite3PExpr(pParse, @X, 0, 0, &X);}
|
||||
expr(A) ::= ID(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);}
|
||||
expr(A) ::= id(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);}
|
||||
expr(A) ::= JOIN_KW(X). {A = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);}
|
||||
expr(A) ::= nm(X) DOT nm(Y). {
|
||||
Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X);
|
||||
|
@ -824,7 +829,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
|||
pList = sqlite3ExprListAppend(pParse,pList, Y, 0);
|
||||
A = sqlite3PExpr(pParse, TK_BETWEEN, W, 0, 0);
|
||||
if( A ){
|
||||
A->pList = pList;
|
||||
A->x.pList = pList;
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, pList);
|
||||
}
|
||||
|
@ -838,7 +843,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
|||
expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] {
|
||||
A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
|
||||
if( A ){
|
||||
A->pList = Y;
|
||||
A->x.pList = Y;
|
||||
sqlite3ExprSetHeight(pParse, A);
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, Y);
|
||||
|
@ -849,7 +854,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
|||
expr(A) ::= LP(B) select(X) RP(E). {
|
||||
A = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
|
||||
if( A ){
|
||||
A->pSelect = X;
|
||||
A->x.pSelect = X;
|
||||
ExprSetProperty(A, EP_xIsSelect);
|
||||
sqlite3ExprSetHeight(pParse, A);
|
||||
}else{
|
||||
sqlite3SelectDelete(pParse->db, X);
|
||||
|
@ -859,7 +865,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
|||
expr(A) ::= expr(X) in_op(N) LP select(Y) RP(E). [IN] {
|
||||
A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
|
||||
if( A ){
|
||||
A->pSelect = Y;
|
||||
A->x.pSelect = Y;
|
||||
ExprSetProperty(A, EP_xIsSelect);
|
||||
sqlite3ExprSetHeight(pParse, A);
|
||||
}else{
|
||||
sqlite3SelectDelete(pParse->db, Y);
|
||||
|
@ -871,7 +878,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
|||
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z);
|
||||
A = sqlite3PExpr(pParse, TK_IN, X, 0, 0);
|
||||
if( A ){
|
||||
A->pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
|
||||
A->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
|
||||
ExprSetProperty(A, EP_xIsSelect);
|
||||
sqlite3ExprSetHeight(pParse, A);
|
||||
}else{
|
||||
sqlite3SrcListDelete(pParse->db, pSrc);
|
||||
|
@ -882,7 +890,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
|||
expr(A) ::= EXISTS(B) LP select(Y) RP(E). {
|
||||
Expr *p = A = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
|
||||
if( p ){
|
||||
p->pSelect = Y;
|
||||
p->x.pSelect = Y;
|
||||
ExprSetProperty(A, EP_xIsSelect);
|
||||
sqlite3ExprSpan(p,&B,&E);
|
||||
sqlite3ExprSetHeight(pParse, A);
|
||||
}else{
|
||||
|
@ -895,7 +904,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
|||
expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
|
||||
A = sqlite3PExpr(pParse, TK_CASE, X, Z, 0);
|
||||
if( A ){
|
||||
A->pList = Y;
|
||||
A->x.pList = Y;
|
||||
sqlite3ExprSetHeight(pParse, A);
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, Y);
|
||||
|
@ -936,7 +945,7 @@ nexprlist(A) ::= expr(Y).
|
|||
|
||||
///////////////////////////// The CREATE INDEX command ///////////////////////
|
||||
//
|
||||
cmd ::= CREATE(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
|
||||
cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
|
||||
ON nm(Y) LP idxlist(Z) RP(E). {
|
||||
sqlite3CreateIndex(pParse, &X, &D,
|
||||
sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U,
|
||||
|
@ -997,16 +1006,19 @@ cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);}
|
|||
//
|
||||
%ifndef SQLITE_OMIT_PARSER
|
||||
%ifndef SQLITE_OMIT_PRAGMA
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ nmnum(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ ON(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ DELETE(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). {
|
||||
sqlite3Pragma(pParse,&X,&Z,&Y,1);
|
||||
}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ nmnum(Y). {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) LP nmnum(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y).
|
||||
{sqlite3Pragma(pParse,&X,&Z,&Y,1);}
|
||||
cmd ::= PRAGMA nm(X) dbnm(Z) LP minus_num(Y) RP.
|
||||
{sqlite3Pragma(pParse,&X,&Z,&Y,1);}
|
||||
|
||||
nmnum(A) ::= plus_num(X). {A = X;}
|
||||
nmnum(A) ::= nm(X). {A = X;}
|
||||
nmnum(A) ::= ON(X). {A = X;}
|
||||
nmnum(A) ::= DELETE(X). {A = X;}
|
||||
nmnum(A) ::= DEFAULT(X). {A = X;}
|
||||
%endif SQLITE_OMIT_PRAGMA
|
||||
%endif SQLITE_OMIT_PARSER
|
||||
plus_num(A) ::= plus_opt number(X). {A = X;}
|
||||
|
@ -1019,7 +1031,7 @@ plus_opt ::= .
|
|||
|
||||
%ifndef SQLITE_OMIT_TRIGGER
|
||||
|
||||
cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
|
||||
cmd ::= createkw trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
|
||||
Token all;
|
||||
all.z = A.z;
|
||||
all.n = (int)(Z.z - A.z) + Z.n;
|
||||
|
@ -1100,14 +1112,14 @@ trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); }
|
|||
expr(A) ::= RAISE(X) LP IGNORE RP(Y). {
|
||||
A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
|
||||
if( A ){
|
||||
A->iColumn = OE_Ignore;
|
||||
A->affinity = OE_Ignore;
|
||||
sqlite3ExprSpan(A, &X, &Y);
|
||||
}
|
||||
}
|
||||
expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). {
|
||||
A = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &Z);
|
||||
if( A ) {
|
||||
A->iColumn = T;
|
||||
A->affinity = (char)T;
|
||||
sqlite3ExprSpan(A, &X, &Y);
|
||||
}
|
||||
}
|
||||
|
@ -1165,6 +1177,7 @@ cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). {
|
|||
sqlite3AlterFinishAddColumn(pParse, &Y);
|
||||
}
|
||||
add_column_fullname ::= fullname(X). {
|
||||
pParse->db->lookaside.bEnabled = 0;
|
||||
sqlite3AlterBeginAddColumn(pParse, X);
|
||||
}
|
||||
kwcolumn_opt ::= .
|
||||
|
@ -1175,7 +1188,7 @@ kwcolumn_opt ::= COLUMNKW.
|
|||
%ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
cmd ::= create_vtab. {sqlite3VtabFinishParse(pParse,0);}
|
||||
cmd ::= create_vtab LP vtabarglist RP(X). {sqlite3VtabFinishParse(pParse,&X);}
|
||||
create_vtab ::= CREATE VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). {
|
||||
create_vtab ::= createkw VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). {
|
||||
sqlite3VtabBeginParse(pParse, &X, &Y, &Z);
|
||||
}
|
||||
vtabarglist ::= vtabarg.
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** This file implements that page cache.
|
||||
**
|
||||
** @(#) $Id: pcache.c,v 1.43 2009/01/23 16:45:01 danielk1977 Exp $
|
||||
** @(#) $Id: pcache.c,v 1.44 2009/03/31 01:32:18 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -336,11 +336,9 @@ void sqlite3PcacheDrop(PgHdr *p){
|
|||
** make it so.
|
||||
*/
|
||||
void sqlite3PcacheMakeDirty(PgHdr *p){
|
||||
PCache *pCache;
|
||||
p->flags &= ~PGHDR_DONT_WRITE;
|
||||
assert( p->nRef>0 );
|
||||
if( 0==(p->flags & PGHDR_DIRTY) ){
|
||||
pCache = p->pCache;
|
||||
p->flags |= PGHDR_DIRTY;
|
||||
pcacheAddToDirtyList( p);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
** If the default page cache implementation is overriden, then neither of
|
||||
** these two features are available.
|
||||
**
|
||||
** @(#) $Id: pcache1.c,v 1.8 2009/01/23 16:45:01 danielk1977 Exp $
|
||||
** @(#) $Id: pcache1.c,v 1.10 2009/03/23 04:33:33 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
@ -129,7 +129,7 @@ static SQLITE_WSD struct PCacheGlobal {
|
|||
*/
|
||||
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
||||
PgFreeslot *p;
|
||||
sz &= ~7;
|
||||
sz = ROUNDDOWN8(sz);
|
||||
pcache1.szSlot = sz;
|
||||
pcache1.pStart = pBuf;
|
||||
pcache1.pFree = 0;
|
||||
|
@ -516,7 +516,7 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
|||
nPinned = pCache->nPage - pCache->nRecyclable;
|
||||
if( createFlag==1 && pCache->bPurgeable && (
|
||||
nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage)
|
||||
|| nPinned>=(pCache->nMax)
|
||||
|| nPinned>=(pCache->nMax * 9 / 10)
|
||||
)){
|
||||
goto fetch_out;
|
||||
}
|
||||
|
|
35
src/pragma.c
35
src/pragma.c
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** This file contains code used to implement the PRAGMA command.
|
||||
**
|
||||
** $Id: pragma.c,v 1.202 2009/01/20 16:53:41 danielk1977 Exp $
|
||||
** $Id: pragma.c,v 1.209 2009/04/07 22:05:43 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -144,14 +144,16 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){
|
|||
/*
|
||||
** Generate code to return a single integer value.
|
||||
*/
|
||||
static void returnSingleInt(Parse *pParse, const char *zLabel, int value){
|
||||
static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int mem = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, value, mem);
|
||||
if( pParse->explain==0 ){
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC);
|
||||
i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value));
|
||||
if( pI64 ){
|
||||
memcpy(pI64, &value, sizeof(value));
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
|
||||
}
|
||||
|
||||
|
@ -172,6 +174,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
|
|||
{ "empty_result_callbacks", SQLITE_NullCallback },
|
||||
{ "legacy_file_format", SQLITE_LegacyFileFmt },
|
||||
{ "fullfsync", SQLITE_FullFSync },
|
||||
{ "reverse_unordered_selects", SQLITE_ReverseOrder },
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ "sql_trace", SQLITE_SqlTrace },
|
||||
{ "vdbe_listing", SQLITE_VdbeListing },
|
||||
|
@ -368,7 +371,7 @@ void sqlite3Pragma(
|
|||
** buffer that the pager module resizes using sqlite3_realloc().
|
||||
*/
|
||||
db->nextPagesize = atoi(zRight);
|
||||
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1) ){
|
||||
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
}
|
||||
|
@ -530,14 +533,11 @@ void sqlite3Pragma(
|
|||
Pager *pPager = sqlite3BtreePager(pDb->pBt);
|
||||
i64 iLimit = -2;
|
||||
if( zRight ){
|
||||
int iLimit32 = atoi(zRight);
|
||||
if( iLimit32<-1 ){
|
||||
iLimit32 = -1;
|
||||
}
|
||||
iLimit = iLimit32;
|
||||
sqlite3Atoi64(zRight, &iLimit);
|
||||
if( iLimit<-1 ) iLimit = -1;
|
||||
}
|
||||
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
|
||||
returnSingleInt(pParse, "journal_size_limit", (int)iLimit);
|
||||
returnSingleInt(pParse, "journal_size_limit", iLimit);
|
||||
}else
|
||||
|
||||
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
|
||||
|
@ -831,7 +831,6 @@ void sqlite3Pragma(
|
|||
sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC);
|
||||
sqlite3ViewGetColumnNames(pParse, pTab);
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
const Token *pDflt;
|
||||
if( IsHiddenColumn(pCol) ){
|
||||
nHidden++;
|
||||
continue;
|
||||
|
@ -842,9 +841,9 @@ void sqlite3Pragma(
|
|||
pCol->zType ? pCol->zType : "", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4);
|
||||
if( pCol->pDflt ){
|
||||
pDflt = &pCol->pDflt->span;
|
||||
assert( pDflt->z );
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pDflt->z, pDflt->n);
|
||||
const Token *p = &pCol->pDflt->span;
|
||||
assert( p->z );
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)p->z, p->n);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
|
||||
}
|
||||
|
@ -1079,7 +1078,6 @@ void sqlite3Pragma(
|
|||
cnt++;
|
||||
}
|
||||
}
|
||||
if( cnt==0 ) continue;
|
||||
|
||||
/* Make sure sufficient number of registers have been allocated */
|
||||
if( pParse->nMem < cnt+4 ){
|
||||
|
@ -1152,7 +1150,6 @@ void sqlite3Pragma(
|
|||
{ OP_Concat, 3, 2, 2},
|
||||
{ OP_ResultRow, 2, 1, 0},
|
||||
};
|
||||
if( pIdx->tnum==0 ) continue;
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
** interface, and routines that contribute to loading the database schema
|
||||
** from disk.
|
||||
**
|
||||
** $Id: prepare.c,v 1.105 2009/01/20 16:53:41 danielk1977 Exp $
|
||||
** $Id: prepare.c,v 1.116 2009/04/02 18:32:27 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -77,21 +77,17 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
|||
*/
|
||||
char *zErr;
|
||||
int rc;
|
||||
u8 lookasideEnabled;
|
||||
assert( db->init.busy );
|
||||
db->init.iDb = iDb;
|
||||
db->init.newTnum = atoi(argv[1]);
|
||||
lookasideEnabled = db->lookaside.bEnabled;
|
||||
db->lookaside.bEnabled = 0;
|
||||
rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
|
||||
db->init.iDb = 0;
|
||||
db->lookaside.bEnabled = lookasideEnabled;
|
||||
assert( rc!=SQLITE_OK || zErr==0 );
|
||||
if( SQLITE_OK!=rc ){
|
||||
pData->rc = rc;
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
db->mallocFailed = 1;
|
||||
}else if( rc!=SQLITE_INTERRUPT ){
|
||||
}else if( rc!=SQLITE_INTERRUPT && (rc&0xff)!=SQLITE_LOCKED ){
|
||||
corruptSchema(pData, argv[0], zErr);
|
||||
}
|
||||
sqlite3DbFree(db, zErr);
|
||||
|
@ -351,10 +347,10 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
|||
}
|
||||
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
|
||||
/* Black magic: If the SQLITE_RecoveryMode flag is set, then consider
|
||||
** the schema loaded, even if errors occured. In this situation the
|
||||
** the schema loaded, even if errors occurred. In this situation the
|
||||
** current sqlite3_prepare() operation will fail, but the following one
|
||||
** will attempt to compile the supplied statement against whatever subset
|
||||
** of the schema was loaded before the error occured. The primary
|
||||
** of the schema was loaded before the error occurred. The primary
|
||||
** purpose of this is to allow access to the sqlite_master table
|
||||
** even when its contents have been corrupted.
|
||||
*/
|
||||
|
@ -532,26 +528,45 @@ static int sqlite3Prepare(
|
|||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
|
||||
assert( ppStmt );
|
||||
*ppStmt = 0;
|
||||
if( sqlite3SafetyOn(db) ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
if( sqlite3SafetyOn(db) ) return SQLITE_MISUSE;
|
||||
assert( ppStmt && *ppStmt==0 );
|
||||
assert( !db->mallocFailed );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
|
||||
/* If any attached database schemas are locked, do not proceed with
|
||||
** compilation. Instead return SQLITE_LOCKED immediately.
|
||||
/* Check to verify that it is possible to get a read lock on all
|
||||
** database schemas. The inability to get a read lock indicates that
|
||||
** some other database connection is holding a write-lock, which in
|
||||
** turn means that the other connection has made uncommitted changes
|
||||
** to the schema.
|
||||
**
|
||||
** Were we to proceed and prepare the statement against the uncommitted
|
||||
** schema changes and if those schema changes are subsequently rolled
|
||||
** back and different changes are made in their place, then when this
|
||||
** prepared statement goes to run the schema cookie would fail to detect
|
||||
** the schema change. Disaster would follow.
|
||||
**
|
||||
** This thread is currently holding mutexes on all Btrees (because
|
||||
** of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it
|
||||
** is not possible for another thread to start a new schema change
|
||||
** while this routine is running. Hence, we do not need to hold
|
||||
** locks on the schema, we just need to make sure nobody else is
|
||||
** holding them.
|
||||
**
|
||||
** Note that setting READ_UNCOMMITTED overrides most lock detection,
|
||||
** but it does *not* override schema lock detection, so this all still
|
||||
** works even if READ_UNCOMMITTED is set.
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++) {
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
assert( sqlite3BtreeHoldsMutex(pBt) );
|
||||
rc = sqlite3BtreeSchemaLocked(pBt);
|
||||
if( rc ){
|
||||
const char *zDb = db->aDb[i].zName;
|
||||
sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb);
|
||||
sqlite3Error(db, rc, "database schema is locked: %s", zDb);
|
||||
(void)sqlite3SafetyOff(db);
|
||||
return sqlite3ApiExit(db, SQLITE_LOCKED);
|
||||
testcase( db->flags & SQLITE_ReadUncommitted );
|
||||
return sqlite3ApiExit(db, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -621,11 +636,13 @@ static int sqlite3Prepare(
|
|||
rc = SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
if( saveSqlFlag ){
|
||||
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail - zSql));
|
||||
assert( db->init.busy==0 || saveSqlFlag==0 );
|
||||
if( db->init.busy==0 ){
|
||||
Vdbe *pVdbe = sParse.pVdbe;
|
||||
sqlite3VdbeSetSql(pVdbe, zSql, (int)(sParse.zTail-zSql), saveSqlFlag);
|
||||
}
|
||||
if( rc!=SQLITE_OK || db->mallocFailed ){
|
||||
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
|
||||
if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
assert(!(*ppStmt));
|
||||
}else{
|
||||
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
|
||||
|
@ -651,6 +668,8 @@ static int sqlite3LockAndPrepare(
|
|||
const char **pzTail /* OUT: End of parsed string */
|
||||
){
|
||||
int rc;
|
||||
assert( ppStmt!=0 );
|
||||
*ppStmt = 0;
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
@ -664,8 +683,11 @@ static int sqlite3LockAndPrepare(
|
|||
|
||||
/*
|
||||
** Rerun the compilation of a statement after a schema change.
|
||||
** Return true if the statement was recompiled successfully.
|
||||
** Return false if there is an error of some kind.
|
||||
**
|
||||
** If the statement is successfully recompiled, return SQLITE_OK. Otherwise,
|
||||
** if the statement cannot be recompiled because another connection has
|
||||
** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error
|
||||
** occurs, return SQLITE_SCHEMA.
|
||||
*/
|
||||
int sqlite3Reprepare(Vdbe *p){
|
||||
int rc;
|
||||
|
@ -684,7 +706,7 @@ int sqlite3Reprepare(Vdbe *p){
|
|||
db->mallocFailed = 1;
|
||||
}
|
||||
assert( pNew==0 );
|
||||
return 0;
|
||||
return (rc==SQLITE_LOCKED) ? SQLITE_LOCKED : SQLITE_SCHEMA;
|
||||
}else{
|
||||
assert( pNew!=0 );
|
||||
}
|
||||
|
@ -692,7 +714,7 @@ int sqlite3Reprepare(Vdbe *p){
|
|||
sqlite3TransferBindings(pNew, (sqlite3_stmt*)p);
|
||||
sqlite3VdbeResetStepResult((Vdbe*)pNew);
|
||||
sqlite3VdbeFinalize((Vdbe*)pNew);
|
||||
return 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -750,6 +772,8 @@ static int sqlite3Prepare16(
|
|||
const char *zTail8 = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( ppStmt );
|
||||
*ppStmt = 0;
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
|
27
src/printf.c
27
src/printf.c
|
@ -5,7 +5,7 @@
|
|||
** an historical reference. Most of the "enhancements" have been backed
|
||||
** out so that the functionality is now the same as standard printf().
|
||||
**
|
||||
** $Id: printf.c,v 1.99 2008/12/10 19:26:24 drh Exp $
|
||||
** $Id: printf.c,v 1.102 2009/04/08 16:10:04 drh Exp $
|
||||
**
|
||||
**************************************************************************
|
||||
**
|
||||
|
@ -77,6 +77,8 @@
|
|||
#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */
|
||||
#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
|
||||
|
||||
#define etINVALID 0 /* Any unrecognized conversion type */
|
||||
|
||||
|
||||
/*
|
||||
** An "etByte" is an 8-bit unsigned value.
|
||||
|
@ -133,6 +135,9 @@ static const et_info fmtinfo[] = {
|
|||
{ 'n', 0, 0, etSIZE, 0, 0 },
|
||||
{ '%', 0, 0, etPERCENT, 0, 0 },
|
||||
{ 'p', 16, 0, etPOINTER, 0, 1 },
|
||||
|
||||
/* All the rest have the FLAG_INTERN bit set and are thus for internal
|
||||
** use only */
|
||||
{ 'T', 0, 2, etTOKEN, 0, 0 },
|
||||
{ 'S', 0, 2, etSRCLIST, 0, 0 },
|
||||
{ 'r', 10, 3, etORDINAL, 0, 0 },
|
||||
|
@ -335,7 +340,8 @@ void sqlite3VXPrintf(
|
|||
flag_long = flag_longlong = 0;
|
||||
}
|
||||
/* Fetch the info entry for the field */
|
||||
infop = 0;
|
||||
infop = &fmtinfo[0];
|
||||
xtype = etINVALID;
|
||||
for(idx=0; idx<ArraySize(fmtinfo); idx++){
|
||||
if( c==fmtinfo[idx].fmttype ){
|
||||
infop = &fmtinfo[idx];
|
||||
|
@ -348,9 +354,6 @@ void sqlite3VXPrintf(
|
|||
}
|
||||
}
|
||||
zExtra = 0;
|
||||
if( infop==0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Limit the precision to prevent overflowing buf[] during conversion */
|
||||
|
@ -688,6 +691,10 @@ void sqlite3VXPrintf(
|
|||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( xtype==etINVALID );
|
||||
return;
|
||||
}
|
||||
}/* End switch over the format type */
|
||||
/*
|
||||
** The text of the conversion is pointed to by "bufpt" and is
|
||||
|
@ -721,13 +728,16 @@ void sqlite3VXPrintf(
|
|||
** Append N bytes of text from z to the StrAccum object.
|
||||
*/
|
||||
void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
||||
assert( z!=0 || N==0 );
|
||||
if( p->tooBig | p->mallocFailed ){
|
||||
testcase(p->tooBig);
|
||||
testcase(p->mallocFailed);
|
||||
return;
|
||||
}
|
||||
if( N<0 ){
|
||||
N = sqlite3Strlen30(z);
|
||||
}
|
||||
if( N==0 || z==0 ){
|
||||
if( N==0 || NEVER(z==0) ){
|
||||
return;
|
||||
}
|
||||
if( p->nChar+N >= p->nAlloc ){
|
||||
|
@ -816,12 +826,13 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
|
|||
char *z;
|
||||
char zBase[SQLITE_PRINT_BUF_SIZE];
|
||||
StrAccum acc;
|
||||
assert( db!=0 );
|
||||
sqlite3StrAccumInit(&acc, zBase, sizeof(zBase),
|
||||
db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH);
|
||||
db->aLimit[SQLITE_LIMIT_LENGTH]);
|
||||
acc.db = db;
|
||||
sqlite3VXPrintf(&acc, 1, zFormat, ap);
|
||||
z = sqlite3StrAccumFinish(&acc);
|
||||
if( acc.mallocFailed && db ){
|
||||
if( acc.mallocFailed ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
return z;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
** resolve all identifiers by associating them with a particular
|
||||
** table and column.
|
||||
**
|
||||
** $Id: resolve.c,v 1.15 2008/12/10 19:26:24 drh Exp $
|
||||
** $Id: resolve.c,v 1.20 2009/03/05 04:23:47 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -63,8 +63,9 @@ static void resolveAlias(
|
|||
assert( pOrig!=0 );
|
||||
assert( pOrig->flags & EP_Resolved );
|
||||
db = pParse->db;
|
||||
pDup = sqlite3ExprDup(db, pOrig);
|
||||
pDup = sqlite3ExprDup(db, pOrig, 0);
|
||||
if( pDup==0 ) return;
|
||||
sqlite3TokenCopy(db, &pDup->token, &pOrig->token);
|
||||
if( pDup->op!=TK_COLUMN && zType[0]!='G' ){
|
||||
pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
|
||||
if( pDup==0 ) return;
|
||||
|
@ -129,6 +130,7 @@ static int lookupName(
|
|||
NameContext *pTopNC = pNC; /* First namecontext in the list */
|
||||
Schema *pSchema = 0; /* Schema of the expression */
|
||||
|
||||
assert( pNC ); /* the name context cannot be NULL. */
|
||||
assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */
|
||||
|
||||
/* Dequote and zero-terminate the names */
|
||||
|
@ -282,8 +284,8 @@ static int lookupName(
|
|||
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
|
||||
Expr *pOrig;
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
|
||||
assert( pExpr->pList==0 );
|
||||
assert( pExpr->pSelect==0 );
|
||||
assert( pExpr->x.pList==0 );
|
||||
assert( pExpr->x.pSelect==0 );
|
||||
pOrig = pEList->a[j].pExpr;
|
||||
if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){
|
||||
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
|
||||
|
@ -474,8 +476,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||
*/
|
||||
case TK_CONST_FUNC:
|
||||
case TK_FUNCTION: {
|
||||
ExprList *pList = pExpr->pList; /* The argument list */
|
||||
int n = pList ? pList->nExpr : 0; /* Number of arguments */
|
||||
ExprList *pList = pExpr->x.pList; /* The argument list */
|
||||
int n = pList ? pList->nExpr : 0; /* Number of arguments */
|
||||
int no_such_func = 0; /* True if no such function exists */
|
||||
int wrong_num_args = 0; /* True if wrong number of arguments */
|
||||
int is_agg = 0; /* True if is an aggregate function */
|
||||
|
@ -485,6 +487,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||
FuncDef *pDef; /* Information about the function */
|
||||
u8 enc = ENC(pParse->db); /* The database encoding */
|
||||
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
zId = (char*)pExpr->token.z;
|
||||
nId = pExpr->token.n;
|
||||
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
|
||||
|
@ -541,14 +544,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
|||
case TK_EXISTS:
|
||||
#endif
|
||||
case TK_IN: {
|
||||
if( pExpr->pSelect ){
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
int nRef = pNC->nRef;
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
if( pNC->isCheck ){
|
||||
sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
|
||||
}
|
||||
#endif
|
||||
sqlite3WalkSelect(pWalker, pExpr->pSelect);
|
||||
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
|
||||
assert( pNC->nRef>=nRef );
|
||||
if( nRef!=pNC->nRef ){
|
||||
ExprSetProperty(pExpr, EP_VarSelect);
|
||||
|
@ -736,7 +739,7 @@ static int resolveCompoundOrderBy(
|
|||
}else{
|
||||
iCol = resolveAsName(pParse, pEList, pE);
|
||||
if( iCol==0 ){
|
||||
pDup = sqlite3ExprDup(db, pE);
|
||||
pDup = sqlite3ExprDup(db, pE, 0);
|
||||
if( !db->mallocFailed ){
|
||||
assert(pDup);
|
||||
iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
** Big chunks of rowid/next-ptr pairs are allocated at a time, to
|
||||
** reduce the malloc overhead.
|
||||
**
|
||||
** $Id: rowset.c,v 1.3 2009/01/13 20:14:16 drh Exp $
|
||||
** $Id: rowset.c,v 1.4 2009/04/01 19:35:55 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -120,7 +120,7 @@ void sqlite3RowSetClear(RowSet *p){
|
|||
void sqlite3RowSetInsert(RowSet *p, i64 rowid){
|
||||
struct RowSetEntry *pEntry;
|
||||
struct RowSetEntry *pLast;
|
||||
if( p==0 ) return; /* Must have been a malloc failure */
|
||||
assert( p!=0 );
|
||||
if( p->nFresh==0 ){
|
||||
struct RowSetChunk *pNew;
|
||||
pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
|
||||
|
|
271
src/select.c
271
src/select.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** to handle SELECT statements in SQLite.
|
||||
**
|
||||
** $Id: select.c,v 1.499 2009/02/09 13:19:28 drh Exp $
|
||||
** $Id: select.c,v 1.507 2009/04/02 16:59:47 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -800,8 +800,7 @@ static void generateSortTail(
|
|||
iTab = pOrderBy->iECursor;
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
pseudoTab = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, nColumn);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output, nColumn);
|
||||
}
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
|
||||
codeOffset(v, p, addrContinue);
|
||||
|
@ -987,8 +986,9 @@ static const char *columnType(
|
|||
** statement.
|
||||
*/
|
||||
NameContext sNC;
|
||||
Select *pS = pExpr->pSelect;
|
||||
Select *pS = pExpr->x.pSelect;
|
||||
Expr *p = pS->pEList->a[0].pExpr;
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
sNC.pSrcList = pS->pSrc;
|
||||
sNC.pNext = pNC;
|
||||
sNC.pParse = pNC->pParse;
|
||||
|
@ -1229,7 +1229,7 @@ static int selectColumnsFromExprList(
|
|||
** The column list has only names, not types or collations. This
|
||||
** routine goes through and adds the types and collations.
|
||||
**
|
||||
** This routine requires that all indentifiers in the SELECT
|
||||
** This routine requires that all identifiers in the SELECT
|
||||
** statement be resolved.
|
||||
*/
|
||||
static void selectAddColumnTypeAndCollation(
|
||||
|
@ -1284,7 +1284,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
|||
if( pTab==0 ){
|
||||
return 0;
|
||||
}
|
||||
pTab->db = db;
|
||||
pTab->dbMem = db->lookaside.bEnabled ? db : 0;
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = 0;
|
||||
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
|
@ -2141,7 +2141,7 @@ static int multiSelectOrderBy(
|
|||
/* Reattach the ORDER BY clause to the query.
|
||||
*/
|
||||
p->pOrderBy = pOrderBy;
|
||||
pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy);
|
||||
pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0);
|
||||
|
||||
/* Allocate a range of temporary registers and the KeyInfo needed
|
||||
** for the logic that removes duplicate result rows when the
|
||||
|
@ -2392,23 +2392,26 @@ static void substExpr(
|
|||
}else{
|
||||
Expr *pNew;
|
||||
assert( pEList!=0 && pExpr->iColumn<pEList->nExpr );
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 );
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
|
||||
pNew = pEList->a[pExpr->iColumn].pExpr;
|
||||
assert( pNew!=0 );
|
||||
pExpr->op = pNew->op;
|
||||
assert( pExpr->pLeft==0 );
|
||||
pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft);
|
||||
pExpr->pLeft = sqlite3ExprDup(db, pNew->pLeft, 0);
|
||||
assert( pExpr->pRight==0 );
|
||||
pExpr->pRight = sqlite3ExprDup(db, pNew->pRight);
|
||||
assert( pExpr->pList==0 );
|
||||
pExpr->pList = sqlite3ExprListDup(db, pNew->pList);
|
||||
pExpr->pRight = sqlite3ExprDup(db, pNew->pRight, 0);
|
||||
pExpr->iTable = pNew->iTable;
|
||||
pExpr->pTab = pNew->pTab;
|
||||
pExpr->iColumn = pNew->iColumn;
|
||||
pExpr->iAgg = pNew->iAgg;
|
||||
sqlite3TokenCopy(db, &pExpr->token, &pNew->token);
|
||||
sqlite3TokenCopy(db, &pExpr->span, &pNew->span);
|
||||
pExpr->pSelect = sqlite3SelectDup(db, pNew->pSelect);
|
||||
assert( pExpr->x.pList==0 && pExpr->x.pSelect==0 );
|
||||
if( ExprHasProperty(pNew, EP_xIsSelect) ){
|
||||
pExpr->x.pSelect = sqlite3SelectDup(db, pNew->x.pSelect, 0);
|
||||
}else{
|
||||
pExpr->x.pList = sqlite3ExprListDup(db, pNew->x.pList, 0);
|
||||
}
|
||||
pExpr->flags = pNew->flags;
|
||||
pExpr->pAggInfo = pNew->pAggInfo;
|
||||
pNew->pAggInfo = 0;
|
||||
|
@ -2416,8 +2419,11 @@ static void substExpr(
|
|||
}else{
|
||||
substExpr(db, pExpr->pLeft, iTable, pEList);
|
||||
substExpr(db, pExpr->pRight, iTable, pEList);
|
||||
substSelect(db, pExpr->pSelect, iTable, pEList);
|
||||
substExprList(db, pExpr->pList, iTable, pEList);
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
substSelect(db, pExpr->x.pSelect, iTable, pEList);
|
||||
}else{
|
||||
substExprList(db, pExpr->x.pList, iTable, pEList);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void substExprList(
|
||||
|
@ -2549,6 +2555,12 @@ static void substSelect(
|
|||
** (19) The subquery does not use LIMIT or the outer query does not
|
||||
** have a WHERE clause.
|
||||
**
|
||||
** (20) If the sub-query is a compound select, then it must not use
|
||||
** an ORDER BY clause. Ticket #3773. We could relax this constraint
|
||||
** somewhat by saying that the terms of the ORDER BY clause must
|
||||
** appear as unmodified result columns in the outer query. But
|
||||
** have other optimizations in mind to deal with that case.
|
||||
**
|
||||
** In this routine, the "p" parameter is a pointer to the outer query.
|
||||
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
|
||||
** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
|
||||
|
@ -2659,6 +2671,9 @@ static int flattenSubquery(
|
|||
** queries.
|
||||
*/
|
||||
if( pSub->pPrior ){
|
||||
if( pSub->pOrderBy ){
|
||||
return 0; /* Restriction 20 */
|
||||
}
|
||||
if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){
|
||||
return 0;
|
||||
}
|
||||
|
@ -2729,7 +2744,7 @@ static int flattenSubquery(
|
|||
p->pSrc = 0;
|
||||
p->pPrior = 0;
|
||||
p->pLimit = 0;
|
||||
pNew = sqlite3SelectDup(db, p);
|
||||
pNew = sqlite3SelectDup(db, p, 0);
|
||||
p->pLimit = pLimit;
|
||||
p->pOrderBy = pOrderBy;
|
||||
p->pSrc = pSrc;
|
||||
|
@ -2873,7 +2888,7 @@ static int flattenSubquery(
|
|||
substExprList(db, pParent->pOrderBy, iParent, pSub->pEList);
|
||||
}
|
||||
if( pSub->pWhere ){
|
||||
pWhere = sqlite3ExprDup(db, pSub->pWhere);
|
||||
pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
|
||||
}else{
|
||||
pWhere = 0;
|
||||
}
|
||||
|
@ -2883,9 +2898,9 @@ static int flattenSubquery(
|
|||
pParent->pWhere = pWhere;
|
||||
substExpr(db, pParent->pHaving, iParent, pSub->pEList);
|
||||
pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
|
||||
sqlite3ExprDup(db, pSub->pHaving));
|
||||
sqlite3ExprDup(db, pSub->pHaving, 0));
|
||||
assert( pParent->pGroupBy==0 );
|
||||
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy);
|
||||
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
|
||||
}else{
|
||||
substExpr(db, pParent->pWhere, iParent, pSub->pEList);
|
||||
pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
|
||||
|
@ -2934,7 +2949,8 @@ static u8 minMaxQuery(Select *p){
|
|||
|
||||
if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
|
||||
pExpr = pEList->a[0].pExpr;
|
||||
pEList = pExpr->pList;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ) return 0;
|
||||
pEList = pExpr->x.pList;
|
||||
if( pExpr->op!=TK_AGG_FUNCTION || pEList==0 || pEList->nExpr!=1 ) return 0;
|
||||
if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
|
||||
if( pExpr->token.n!=3 ) return WHERE_ORDERBY_NORMAL;
|
||||
|
@ -2946,6 +2962,40 @@ static u8 minMaxQuery(Select *p){
|
|||
return WHERE_ORDERBY_NORMAL;
|
||||
}
|
||||
|
||||
/*
|
||||
** The select statement passed as the first argument is an aggregate query.
|
||||
** The second argment is the associated aggregate-info object. This
|
||||
** function tests if the SELECT is of the form:
|
||||
**
|
||||
** SELECT count(*) FROM <tbl>
|
||||
**
|
||||
** where table is a database table, not a sub-select or view. If the query
|
||||
** does match this pattern, then a pointer to the Table object representing
|
||||
** <tbl> is returned. Otherwise, 0 is returned.
|
||||
*/
|
||||
static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
|
||||
Table *pTab;
|
||||
Expr *pExpr;
|
||||
|
||||
assert( !p->pGroupBy );
|
||||
|
||||
if( p->pWhere || p->pEList->nExpr!=1
|
||||
|| p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
pTab = p->pSrc->a[0].pTab;
|
||||
pExpr = p->pEList->a[0].pExpr;
|
||||
assert( pTab && !pTab->pSelect && pExpr );
|
||||
|
||||
if( IsVirtual(pTab) ) return 0;
|
||||
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
|
||||
if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0;
|
||||
if( pExpr->flags&EP_Distinct ) return 0;
|
||||
|
||||
return pTab;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the source-list item passed as an argument was augmented with an
|
||||
** INDEXED BY clause, then try to locate the specified index. If there
|
||||
|
@ -3039,7 +3089,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
|||
sqlite3WalkSelect(pWalker, pSel);
|
||||
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
||||
if( pTab==0 ) return WRC_Abort;
|
||||
pTab->db = db;
|
||||
pTab->dbMem = db->lookaside.bEnabled ? db : 0;
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
|
||||
while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
||||
|
@ -3065,7 +3115,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
|||
** in the inner view.
|
||||
*/
|
||||
if( pFrom->pSelect==0 ){
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect);
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
|
||||
sqlite3WalkSelect(pWalker, pFrom->pSelect);
|
||||
}
|
||||
}
|
||||
|
@ -3364,12 +3414,13 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
|||
sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem);
|
||||
if( pFunc->iDistinct>=0 ){
|
||||
Expr *pE = pFunc->pExpr;
|
||||
if( pE->pList==0 || pE->pList->nExpr!=1 ){
|
||||
assert( !ExprHasProperty(pE, EP_xIsSelect) );
|
||||
if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){
|
||||
sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one "
|
||||
"argument");
|
||||
pFunc->iDistinct = -1;
|
||||
}else{
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->pList);
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
}
|
||||
|
@ -3386,7 +3437,8 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
|
|||
int i;
|
||||
struct AggInfo_func *pF;
|
||||
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
|
||||
ExprList *pList = pF->pExpr->pList;
|
||||
ExprList *pList = pF->pExpr->x.pList;
|
||||
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
|
||||
sqlite3VdbeAddOp4(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0, 0,
|
||||
(void*)pF->pFunc, P4_FUNCDEF);
|
||||
}
|
||||
|
@ -3407,7 +3459,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
|||
int nArg;
|
||||
int addrNext = 0;
|
||||
int regAgg;
|
||||
ExprList *pList = pF->pExpr->pList;
|
||||
ExprList *pList = pF->pExpr->x.pList;
|
||||
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
|
||||
if( pList ){
|
||||
nArg = pList->nExpr;
|
||||
regAgg = sqlite3GetTempRange(pParse, nArg);
|
||||
|
@ -3657,7 +3710,7 @@ int sqlite3Select(
|
|||
** GROUP BY might use an index, DISTINCT never does.
|
||||
*/
|
||||
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct && !p->pGroupBy ){
|
||||
p->pGroupBy = sqlite3ExprListDup(db, p->pEList);
|
||||
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
|
||||
pGroupBy = p->pGroupBy;
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
isDistinct = 0;
|
||||
|
@ -3780,7 +3833,8 @@ int sqlite3Select(
|
|||
}
|
||||
sAggInfo.nAccumulator = sAggInfo.nColumn;
|
||||
for(i=0; i<sAggInfo.nFunc; i++){
|
||||
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList);
|
||||
assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
|
||||
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
|
||||
}
|
||||
if( db->mallocFailed ) goto select_end;
|
||||
|
||||
|
@ -3987,68 +4041,127 @@ int sqlite3Select(
|
|||
|
||||
} /* endif pGroupBy */
|
||||
else {
|
||||
ExprList *pMinMax = 0;
|
||||
ExprList *pDel = 0;
|
||||
u8 flag;
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
Table *pTab;
|
||||
if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
|
||||
/* If isSimpleCount() returns a pointer to a Table structure, then
|
||||
** the SQL statement is of the form:
|
||||
**
|
||||
** SELECT count(*) FROM <tbl>
|
||||
**
|
||||
** where the Table structure returned represents table <tbl>.
|
||||
**
|
||||
** This statement is so common that it is optimized specially. The
|
||||
** OP_Count instruction is executed either on the intkey table that
|
||||
** contains the data for table <tbl> or on one of its indexes. It
|
||||
** is better to execute the op on an index, as indexes are almost
|
||||
** always spread across less pages than their corresponding tables.
|
||||
*/
|
||||
const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */
|
||||
Index *pIdx; /* Iterator variable */
|
||||
KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */
|
||||
Index *pBest = 0; /* Best index found so far */
|
||||
int iRoot = pTab->tnum; /* Root page of scanned b-tree */
|
||||
|
||||
/* Check if the query is of one of the following forms:
|
||||
**
|
||||
** SELECT min(x) FROM ...
|
||||
** SELECT max(x) FROM ...
|
||||
**
|
||||
** If it is, then ask the code in where.c to attempt to sort results
|
||||
** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause.
|
||||
** If where.c is able to produce results sorted in this order, then
|
||||
** add vdbe code to break out of the processing loop after the
|
||||
** first iteration (since the first iteration of the loop is
|
||||
** guaranteed to operate on the row with the minimum or maximum
|
||||
** value of x, the only row required).
|
||||
**
|
||||
** A special flag must be passed to sqlite3WhereBegin() to slightly
|
||||
** modify behaviour as follows:
|
||||
**
|
||||
** + If the query is a "SELECT min(x)", then the loop coded by
|
||||
** where.c should not iterate over any values with a NULL value
|
||||
** for x.
|
||||
**
|
||||
** + The optimizer code in where.c (the thing that decides which
|
||||
** index or indices to use) should place a different priority on
|
||||
** satisfying the 'ORDER BY' clause than it does in other cases.
|
||||
** Refer to code and comments in where.c for details.
|
||||
*/
|
||||
flag = minMaxQuery(p);
|
||||
if( flag ){
|
||||
pDel = pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->pList);
|
||||
if( pMinMax && !db->mallocFailed ){
|
||||
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
|
||||
pMinMax->a[0].pExpr->op = TK_COLUMN;
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
/* Search for the index that has the least amount of columns. If
|
||||
** there is such an index, and it has less columns than the table
|
||||
** does, then we can assume that it consumes less space on disk and
|
||||
** will therefore be cheaper to scan to determine the query result.
|
||||
** In this case set iRoot to the root page number of the index b-tree
|
||||
** and pKeyInfo to the KeyInfo structure required to navigate the
|
||||
** index.
|
||||
**
|
||||
** In practice the KeyInfo structure will not be used. It is only
|
||||
** passed to keep OP_OpenRead happy.
|
||||
*/
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( !pBest || pIdx->nColumn<pBest->nColumn ){
|
||||
pBest = pIdx;
|
||||
}
|
||||
}
|
||||
if( pBest && pBest->nColumn<pTab->nCol ){
|
||||
iRoot = pBest->tnum;
|
||||
pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest);
|
||||
}
|
||||
|
||||
/* Open a read-only cursor, execute the OP_Count, close the cursor. */
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb);
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_BTREECOUNT */
|
||||
{
|
||||
/* Check if the query is of one of the following forms:
|
||||
**
|
||||
** SELECT min(x) FROM ...
|
||||
** SELECT max(x) FROM ...
|
||||
**
|
||||
** If it is, then ask the code in where.c to attempt to sort results
|
||||
** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause.
|
||||
** If where.c is able to produce results sorted in this order, then
|
||||
** add vdbe code to break out of the processing loop after the
|
||||
** first iteration (since the first iteration of the loop is
|
||||
** guaranteed to operate on the row with the minimum or maximum
|
||||
** value of x, the only row required).
|
||||
**
|
||||
** A special flag must be passed to sqlite3WhereBegin() to slightly
|
||||
** modify behaviour as follows:
|
||||
**
|
||||
** + If the query is a "SELECT min(x)", then the loop coded by
|
||||
** where.c should not iterate over any values with a NULL value
|
||||
** for x.
|
||||
**
|
||||
** + The optimizer code in where.c (the thing that decides which
|
||||
** index or indices to use) should place a different priority on
|
||||
** satisfying the 'ORDER BY' clause than it does in other cases.
|
||||
** Refer to code and comments in where.c for details.
|
||||
*/
|
||||
ExprList *pMinMax = 0;
|
||||
u8 flag = minMaxQuery(p);
|
||||
if( flag ){
|
||||
assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) );
|
||||
pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0);
|
||||
pDel = pMinMax;
|
||||
if( pMinMax && !db->mallocFailed ){
|
||||
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
|
||||
pMinMax->a[0].pExpr->op = TK_COLUMN;
|
||||
}
|
||||
}
|
||||
|
||||
/* This case runs if the aggregate has no GROUP BY clause. The
|
||||
** processing is much simpler since there is only a single row
|
||||
** of output.
|
||||
*/
|
||||
resetAccumulator(pParse, &sAggInfo);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0);
|
||||
if( pWInfo==0 ){
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
goto select_end;
|
||||
}
|
||||
updateAccumulator(pParse, &sAggInfo);
|
||||
if( !pMinMax && flag ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
|
||||
VdbeComment((v, "%s() by index",
|
||||
(flag==WHERE_ORDERBY_MIN?"min":"max")));
|
||||
}
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
finalizeAggFunctions(pParse, &sAggInfo);
|
||||
}
|
||||
|
||||
/* This case runs if the aggregate has no GROUP BY clause. The
|
||||
** processing is much simpler since there is only a single row
|
||||
** of output.
|
||||
*/
|
||||
resetAccumulator(pParse, &sAggInfo);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0);
|
||||
if( pWInfo==0 ){
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
goto select_end;
|
||||
}
|
||||
updateAccumulator(pParse, &sAggInfo);
|
||||
if( !pMinMax && flag ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
|
||||
VdbeComment((v, "%s() by index",(flag==WHERE_ORDERBY_MIN?"min":"max")));
|
||||
}
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
finalizeAggFunctions(pParse, &sAggInfo);
|
||||
pOrderBy = 0;
|
||||
if( pHaving ){
|
||||
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
|
||||
pDest, addrEnd, addrEnd);
|
||||
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, addrEnd);
|
||||
|
|
932
src/shell.c
932
src/shell.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains code to implement the "sqlite" command line
|
||||
** utility for accessing SQLite databases.
|
||||
**
|
||||
** $Id: shell.c,v 1.201 2009/02/04 22:46:47 drh Exp $
|
||||
** $Id: shell.c,v 1.207 2009/03/16 10:59:44 drh Exp $
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
/* This needs to come before any includes for MSVC compiler */
|
||||
|
@ -120,6 +120,861 @@ static void endTimer(void){
|
|||
*/
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
***************************************************************************
|
||||
** Begin genfkey logic.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined SQLITE_OMIT_SUBQUERY
|
||||
|
||||
#define GENFKEY_ERROR 1
|
||||
#define GENFKEY_DROPTRIGGER 2
|
||||
#define GENFKEY_CREATETRIGGER 3
|
||||
static int genfkey_create_triggers(sqlite3 *, const char *, void *,
|
||||
int (*)(void *, int, const char *)
|
||||
);
|
||||
|
||||
struct GenfkeyCb {
|
||||
void *pCtx;
|
||||
int eType;
|
||||
int (*xData)(void *, int, const char *);
|
||||
};
|
||||
typedef struct GenfkeyCb GenfkeyCb;
|
||||
|
||||
/* The code in this file defines a sqlite3 virtual-table module that
|
||||
** provides a read-only view of the current database schema. There is one
|
||||
** row in the schema table for each column in the database schema.
|
||||
*/
|
||||
#define SCHEMA \
|
||||
"CREATE TABLE x(" \
|
||||
"database," /* Name of database (i.e. main, temp etc.) */ \
|
||||
"tablename," /* Name of table */ \
|
||||
"cid," /* Column number (from left-to-right, 0 upward) */ \
|
||||
"name," /* Column name */ \
|
||||
"type," /* Specified type (i.e. VARCHAR(32)) */ \
|
||||
"not_null," /* Boolean. True if NOT NULL was specified */ \
|
||||
"dflt_value," /* Default value for this column */ \
|
||||
"pk" /* True if this column is part of the primary key */ \
|
||||
")"
|
||||
|
||||
#define SCHEMA2 \
|
||||
"CREATE TABLE x(" \
|
||||
"database," /* Name of database (i.e. main, temp etc.) */ \
|
||||
"from_tbl," /* Name of table */ \
|
||||
"fkid," \
|
||||
"seq," \
|
||||
"to_tbl," \
|
||||
"from_col," \
|
||||
"to_col," \
|
||||
"on_update," \
|
||||
"on_delete," \
|
||||
"match" \
|
||||
")"
|
||||
|
||||
#define SCHEMA3 \
|
||||
"CREATE TABLE x(" \
|
||||
"database," /* Name of database (i.e. main, temp etc.) */ \
|
||||
"tablename," /* Name of table */ \
|
||||
"seq," \
|
||||
"name," \
|
||||
"isunique" \
|
||||
")"
|
||||
|
||||
#define SCHEMA4 \
|
||||
"CREATE TABLE x(" \
|
||||
"database," /* Name of database (i.e. main, temp etc.) */ \
|
||||
"indexname," /* Name of table */ \
|
||||
"seqno," \
|
||||
"cid," \
|
||||
"name" \
|
||||
")"
|
||||
|
||||
#define SCHEMA5 \
|
||||
"CREATE TABLE x(" \
|
||||
"database," /* Name of database (i.e. main, temp etc.) */ \
|
||||
"triggername," /* Name of trigger */ \
|
||||
"dummy" /* Unused */ \
|
||||
")"
|
||||
|
||||
typedef struct SchemaTable SchemaTable;
|
||||
struct SchemaTable {
|
||||
const char *zName;
|
||||
const char *zObject;
|
||||
const char *zPragma;
|
||||
const char *zSchema;
|
||||
} aSchemaTable[] = {
|
||||
{ "table_info", "table", "PRAGMA %Q.table_info(%Q)", SCHEMA },
|
||||
{ "foreign_key_list", "table", "PRAGMA %Q.foreign_key_list(%Q)", SCHEMA2 },
|
||||
{ "index_list", "table", "PRAGMA %Q.index_list(%Q)", SCHEMA3 },
|
||||
{ "index_info", "index", "PRAGMA %Q.index_info(%Q)", SCHEMA4 },
|
||||
{ "trigger_list", "trigger", "SELECT 1", SCHEMA5 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
typedef struct schema_vtab schema_vtab;
|
||||
typedef struct schema_cursor schema_cursor;
|
||||
|
||||
/* A schema table object */
|
||||
struct schema_vtab {
|
||||
sqlite3_vtab base;
|
||||
sqlite3 *db;
|
||||
SchemaTable *pType;
|
||||
};
|
||||
|
||||
/* A schema table cursor object */
|
||||
struct schema_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_stmt *pDbList;
|
||||
sqlite3_stmt *pTableList;
|
||||
sqlite3_stmt *pColumnList;
|
||||
int rowid;
|
||||
};
|
||||
|
||||
/*
|
||||
** Table destructor for the schema module.
|
||||
*/
|
||||
static int schemaDestroy(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table constructor for the schema module.
|
||||
*/
|
||||
static int schemaCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
int rc = SQLITE_NOMEM;
|
||||
schema_vtab *pVtab;
|
||||
SchemaTable *pType = &aSchemaTable[0];
|
||||
|
||||
UNUSED_PARAMETER(pzErr);
|
||||
if( argc>3 ){
|
||||
int i;
|
||||
pType = 0;
|
||||
for(i=0; aSchemaTable[i].zName; i++){
|
||||
if( 0==strcmp(argv[3], aSchemaTable[i].zName) ){
|
||||
pType = &aSchemaTable[i];
|
||||
}
|
||||
}
|
||||
if( !pType ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
pVtab = sqlite3_malloc(sizeof(schema_vtab));
|
||||
if( pVtab ){
|
||||
memset(pVtab, 0, sizeof(schema_vtab));
|
||||
pVtab->db = (sqlite3 *)pAux;
|
||||
pVtab->pType = pType;
|
||||
rc = sqlite3_declare_vtab(db, pType->zSchema);
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab *)pVtab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new cursor on the schema table.
|
||||
*/
|
||||
static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
int rc = SQLITE_NOMEM;
|
||||
schema_cursor *pCur;
|
||||
UNUSED_PARAMETER(pVTab);
|
||||
pCur = sqlite3_malloc(sizeof(schema_cursor));
|
||||
if( pCur ){
|
||||
memset(pCur, 0, sizeof(schema_cursor));
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCur;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a schema table cursor.
|
||||
*/
|
||||
static int schemaClose(sqlite3_vtab_cursor *cur){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
sqlite3_finalize(pCur->pDbList);
|
||||
sqlite3_finalize(pCur->pTableList);
|
||||
sqlite3_finalize(pCur->pColumnList);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void columnToResult(sqlite3_context *ctx, sqlite3_stmt *pStmt, int iCol){
|
||||
switch( sqlite3_column_type(pStmt, iCol) ){
|
||||
case SQLITE_NULL:
|
||||
sqlite3_result_null(ctx);
|
||||
break;
|
||||
case SQLITE_INTEGER:
|
||||
sqlite3_result_int64(ctx, sqlite3_column_int64(pStmt, iCol));
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
sqlite3_result_double(ctx, sqlite3_column_double(pStmt, iCol));
|
||||
break;
|
||||
case SQLITE_TEXT: {
|
||||
const char *z = (const char *)sqlite3_column_text(pStmt, iCol);
|
||||
sqlite3_result_text(ctx, z, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve a column of data.
|
||||
*/
|
||||
static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
switch( i ){
|
||||
case 0:
|
||||
columnToResult(ctx, pCur->pDbList, 1);
|
||||
break;
|
||||
case 1:
|
||||
columnToResult(ctx, pCur->pTableList, 0);
|
||||
break;
|
||||
default:
|
||||
columnToResult(ctx, pCur->pColumnList, i-2);
|
||||
break;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve the current rowid.
|
||||
*/
|
||||
static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
*pRowid = pCur->rowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int finalize(sqlite3_stmt **ppStmt){
|
||||
int rc = sqlite3_finalize(*ppStmt);
|
||||
*ppStmt = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int schemaEof(sqlite3_vtab_cursor *cur){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
return (pCur->pDbList ? 0 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance the cursor to the next row.
|
||||
*/
|
||||
static int schemaNext(sqlite3_vtab_cursor *cur){
|
||||
int rc = SQLITE_OK;
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
schema_vtab *pVtab = (schema_vtab *)(cur->pVtab);
|
||||
char *zSql = 0;
|
||||
|
||||
while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){
|
||||
if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit;
|
||||
|
||||
while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){
|
||||
if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit;
|
||||
|
||||
assert(pCur->pDbList);
|
||||
while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){
|
||||
rc = finalize(&pCur->pDbList);
|
||||
goto next_exit;
|
||||
}
|
||||
|
||||
/* Set zSql to the SQL to pull the list of tables from the
|
||||
** sqlite_master (or sqlite_temp_master) table of the database
|
||||
** identfied by the row pointed to by the SQL statement pCur->pDbList
|
||||
** (iterating through a "PRAGMA database_list;" statement).
|
||||
*/
|
||||
if( sqlite3_column_int(pCur->pDbList, 0)==1 ){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT name FROM sqlite_temp_master WHERE type=%Q",
|
||||
pVtab->pType->zObject
|
||||
);
|
||||
}else{
|
||||
sqlite3_stmt *pDbList = pCur->pDbList;
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT name FROM %Q.sqlite_master WHERE type=%Q",
|
||||
sqlite3_column_text(pDbList, 1), pVtab->pType->zObject
|
||||
);
|
||||
}
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto next_exit;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK ) goto next_exit;
|
||||
}
|
||||
|
||||
/* Set zSql to the SQL to the table_info pragma for the table currently
|
||||
** identified by the rows pointed to by statements pCur->pDbList and
|
||||
** pCur->pTableList.
|
||||
*/
|
||||
zSql = sqlite3_mprintf(pVtab->pType->zPragma,
|
||||
sqlite3_column_text(pCur->pDbList, 1),
|
||||
sqlite3_column_text(pCur->pTableList, 0)
|
||||
);
|
||||
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto next_exit;
|
||||
}
|
||||
rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK ) goto next_exit;
|
||||
}
|
||||
pCur->rowid++;
|
||||
|
||||
next_exit:
|
||||
/* TODO: Handle rc */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset a schema table cursor.
|
||||
*/
|
||||
static int schemaFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
int rc;
|
||||
schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab);
|
||||
schema_cursor *pCur = (schema_cursor *)pVtabCursor;
|
||||
UNUSED_PARAMETER(idxNum);
|
||||
UNUSED_PARAMETER(idxStr);
|
||||
UNUSED_PARAMETER(argc);
|
||||
UNUSED_PARAMETER(argv);
|
||||
pCur->rowid = 0;
|
||||
finalize(&pCur->pTableList);
|
||||
finalize(&pCur->pColumnList);
|
||||
finalize(&pCur->pDbList);
|
||||
rc = sqlite3_prepare(pVtab->db,"SELECT 0, 'main'", -1, &pCur->pDbList, 0);
|
||||
return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyse the WHERE condition.
|
||||
*/
|
||||
static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
UNUSED_PARAMETER(tab);
|
||||
UNUSED_PARAMETER(pIdxInfo);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that merely echos method calls into TCL
|
||||
** variables.
|
||||
*/
|
||||
static sqlite3_module schemaModule = {
|
||||
0, /* iVersion */
|
||||
schemaCreate,
|
||||
schemaCreate,
|
||||
schemaBestIndex,
|
||||
schemaDestroy,
|
||||
schemaDestroy,
|
||||
schemaOpen, /* xOpen - open a cursor */
|
||||
schemaClose, /* xClose - close a cursor */
|
||||
schemaFilter, /* xFilter - configure scan constraints */
|
||||
schemaNext, /* xNext - advance a cursor */
|
||||
schemaEof, /* xEof */
|
||||
schemaColumn, /* xColumn - read data */
|
||||
schemaRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
/*
|
||||
** Extension load function.
|
||||
*/
|
||||
static int installSchemaModule(sqlite3 *db, sqlite3 *sdb){
|
||||
sqlite3_create_module(db, "schema", &schemaModule, (void *)sdb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** sj(zValue, zJoin)
|
||||
**
|
||||
** The following block contains the implementation of an aggregate
|
||||
** function that returns a string. Each time the function is stepped,
|
||||
** it appends data to an internal buffer. When the aggregate is finalized,
|
||||
** the contents of the buffer are returned.
|
||||
**
|
||||
** The first time the aggregate is stepped the buffer is set to a copy
|
||||
** of the first argument. The second time and subsequent times it is
|
||||
** stepped a copy of the second argument is appended to the buffer, then
|
||||
** a copy of the first.
|
||||
**
|
||||
** Example:
|
||||
**
|
||||
** INSERT INTO t1(a) VALUES('1');
|
||||
** INSERT INTO t1(a) VALUES('2');
|
||||
** INSERT INTO t1(a) VALUES('3');
|
||||
** SELECT sj(a, ', ') FROM t1;
|
||||
**
|
||||
** => "1, 2, 3"
|
||||
**
|
||||
*/
|
||||
struct StrBuffer {
|
||||
char *zBuf;
|
||||
};
|
||||
typedef struct StrBuffer StrBuffer;
|
||||
static void joinFinalize(sqlite3_context *context){
|
||||
StrBuffer *p;
|
||||
p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer));
|
||||
sqlite3_result_text(context, p->zBuf, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_free(p->zBuf);
|
||||
}
|
||||
static void joinStep(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
StrBuffer *p;
|
||||
UNUSED_PARAMETER(argc);
|
||||
p = (StrBuffer *)sqlite3_aggregate_context(context, sizeof(StrBuffer));
|
||||
if( p->zBuf==0 ){
|
||||
p->zBuf = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
||||
}else{
|
||||
char *zTmp = p->zBuf;
|
||||
p->zBuf = sqlite3_mprintf("%s%s%s",
|
||||
zTmp, sqlite3_value_text(argv[1]), sqlite3_value_text(argv[0])
|
||||
);
|
||||
sqlite3_free(zTmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** dq(zString)
|
||||
**
|
||||
** This scalar function accepts a single argument and interprets it as
|
||||
** a text value. The return value is the argument enclosed in double
|
||||
** quotes. If any double quote characters are present in the argument,
|
||||
** these are escaped.
|
||||
**
|
||||
** dq('the raven "Nevermore."') == '"the raven ""Nevermore."""'
|
||||
*/
|
||||
static void doublequote(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int ii;
|
||||
char *zOut;
|
||||
char *zCsr;
|
||||
const char *zIn = (const char *)sqlite3_value_text(argv[0]);
|
||||
int nIn = sqlite3_value_bytes(argv[0]);
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
zOut = sqlite3_malloc(nIn*2+3);
|
||||
zCsr = zOut;
|
||||
*zCsr++ = '"';
|
||||
for(ii=0; ii<nIn; ii++){
|
||||
*zCsr++ = zIn[ii];
|
||||
if( zIn[ii]=='"' ){
|
||||
*zCsr++ = '"';
|
||||
}
|
||||
}
|
||||
*zCsr++ = '"';
|
||||
*zCsr++ = '\0';
|
||||
|
||||
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_free(zOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** multireplace(zString, zSearch1, zReplace1, ...)
|
||||
*/
|
||||
static void multireplace(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int i = 0;
|
||||
char *zOut = 0;
|
||||
int nOut = 0;
|
||||
int nMalloc = 0;
|
||||
const char *zIn = (const char *)sqlite3_value_text(argv[0]);
|
||||
int nIn = sqlite3_value_bytes(argv[0]);
|
||||
|
||||
while( i<nIn ){
|
||||
const char *zCopy = &zIn[i];
|
||||
int nCopy = 1;
|
||||
int nReplace = 1;
|
||||
int j;
|
||||
for(j=1; j<(argc-1); j+=2){
|
||||
const char *z = (const char *)sqlite3_value_text(argv[j]);
|
||||
int n = sqlite3_value_bytes(argv[j]);
|
||||
if( n<=(nIn-i) && 0==strncmp(z, zCopy, n) ){
|
||||
zCopy = (const char *)sqlite3_value_text(argv[j+1]);
|
||||
nCopy = sqlite3_value_bytes(argv[j+1]);
|
||||
nReplace = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( (nOut+nCopy)>nMalloc ){
|
||||
nMalloc = 16 + (nOut+nCopy)*2;
|
||||
zOut = (char *)sqlite3_realloc(zOut, nMalloc);
|
||||
}
|
||||
assert( nMalloc>=(nOut+nCopy) );
|
||||
memcpy(&zOut[nOut], zCopy, nCopy);
|
||||
i += nReplace;
|
||||
nOut += nCopy;
|
||||
}
|
||||
|
||||
sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT);
|
||||
sqlite3_free(zOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** A callback for sqlite3_exec() invokes the callback specified by the
|
||||
** GenfkeyCb structure pointed to by the void* passed as the first argument.
|
||||
*/
|
||||
static int invokeCallback(void *p, int nArg, char **azArg, char **azCol){
|
||||
GenfkeyCb *pCb = (GenfkeyCb *)p;
|
||||
UNUSED_PARAMETER(nArg);
|
||||
UNUSED_PARAMETER(azCol);
|
||||
return pCb->xData(pCb->pCtx, pCb->eType, azArg[0]);
|
||||
}
|
||||
|
||||
int detectSchemaProblem(
|
||||
sqlite3 *db, /* Database connection */
|
||||
const char *zMessage, /* English language error message */
|
||||
const char *zSql, /* SQL statement to run */
|
||||
GenfkeyCb *pCb
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
char *zDel;
|
||||
int iFk = sqlite3_column_int(pStmt, 0);
|
||||
const char *zTab = (const char *)sqlite3_column_text(pStmt, 1);
|
||||
zDel = sqlite3_mprintf("Error in table %s: %s", zTab, zMessage);
|
||||
rc = pCb->xData(pCb->pCtx, pCb->eType, zDel);
|
||||
sqlite3_free(zDel);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
zDel = sqlite3_mprintf(
|
||||
"DELETE FROM temp.fkey WHERE from_tbl = %Q AND fkid = %d"
|
||||
, zTab, iFk
|
||||
);
|
||||
sqlite3_exec(db, zDel, 0, 0, 0);
|
||||
sqlite3_free(zDel);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create and populate temporary table "fkey".
|
||||
*/
|
||||
static int populateTempTable(sqlite3 *db, GenfkeyCb *pCallback){
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE VIRTUAL TABLE temp.v_fkey USING schema(foreign_key_list);"
|
||||
"CREATE VIRTUAL TABLE temp.v_col USING schema(table_info);"
|
||||
"CREATE VIRTUAL TABLE temp.v_idxlist USING schema(index_list);"
|
||||
"CREATE VIRTUAL TABLE temp.v_idxinfo USING schema(index_info);"
|
||||
"CREATE VIRTUAL TABLE temp.v_triggers USING schema(trigger_list);"
|
||||
"CREATE TABLE temp.fkey AS "
|
||||
"SELECT from_tbl, to_tbl, fkid, from_col, to_col, on_update, on_delete "
|
||||
"FROM temp.v_fkey WHERE database = 'main';"
|
||||
, 0, 0, 0
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
rc = detectSchemaProblem(db, "foreign key columns do not exist",
|
||||
"SELECT fkid, from_tbl "
|
||||
"FROM temp.fkey "
|
||||
"WHERE to_col IS NOT NULL AND NOT EXISTS (SELECT 1 "
|
||||
"FROM temp.v_col WHERE tablename=to_tbl AND name==to_col"
|
||||
")", pCallback
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* At this point the temp.fkey table is mostly populated. If any foreign
|
||||
** keys were specified so that they implicitly refer to they primary
|
||||
** key of the parent table, the "to_col" values of the temp.fkey rows
|
||||
** are still set to NULL.
|
||||
**
|
||||
** This is easily fixed for single column primary keys, but not for
|
||||
** composites. With a composite primary key, there is no way to reliably
|
||||
** query sqlite for the order in which the columns that make up the
|
||||
** composite key were declared i.e. there is no way to tell if the
|
||||
** schema actually contains "PRIMARY KEY(a, b)" or "PRIMARY KEY(b, a)".
|
||||
** Therefore, this case is not handled. The following function call
|
||||
** detects instances of this case.
|
||||
*/
|
||||
rc = detectSchemaProblem(db, "implicit mapping to composite primary key",
|
||||
"SELECT fkid, from_tbl "
|
||||
"FROM temp.fkey "
|
||||
"WHERE to_col IS NULL "
|
||||
"GROUP BY fkid, from_tbl HAVING count(*) > 1", pCallback
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Detect attempts to implicitly map to the primary key of a table
|
||||
** that has no primary key column.
|
||||
*/
|
||||
rc = detectSchemaProblem(db, "implicit mapping to non-existant primary key",
|
||||
"SELECT fkid, from_tbl "
|
||||
"FROM temp.fkey "
|
||||
"WHERE to_col IS NULL AND NOT EXISTS "
|
||||
"(SELECT 1 FROM temp.v_col WHERE pk AND tablename = temp.fkey.to_tbl)"
|
||||
, pCallback
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Fix all the implicit primary key mappings in the temp.fkey table. */
|
||||
rc = sqlite3_exec(db,
|
||||
"UPDATE temp.fkey SET to_col = "
|
||||
"(SELECT name FROM temp.v_col WHERE pk AND tablename=temp.fkey.to_tbl)"
|
||||
" WHERE to_col IS NULL;"
|
||||
, 0, 0, 0
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Now check that all all parent keys are either primary keys or
|
||||
** subject to a unique constraint.
|
||||
*/
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE temp.idx2 AS SELECT "
|
||||
"il.tablename AS tablename,"
|
||||
"ii.indexname AS indexname,"
|
||||
"ii.name AS col "
|
||||
"FROM temp.v_idxlist AS il, temp.v_idxinfo AS ii "
|
||||
"WHERE il.isunique AND il.database='main' AND ii.indexname = il.name;"
|
||||
"INSERT INTO temp.idx2 "
|
||||
"SELECT tablename, 'pk', name FROM temp.v_col WHERE pk;"
|
||||
|
||||
"CREATE TABLE temp.idx AS SELECT "
|
||||
"tablename, indexname, sj(dq(col),',') AS cols "
|
||||
"FROM (SELECT * FROM temp.idx2 ORDER BY col) "
|
||||
"GROUP BY tablename, indexname;"
|
||||
|
||||
"CREATE TABLE temp.fkey2 AS SELECT "
|
||||
"fkid, from_tbl, to_tbl, sj(dq(to_col),',') AS cols "
|
||||
"FROM (SELECT * FROM temp.fkey ORDER BY to_col) "
|
||||
"GROUP BY fkid, from_tbl;"
|
||||
|
||||
"CREATE TABLE temp.triggers AS SELECT "
|
||||
"triggername FROM temp.v_triggers WHERE database='main' AND "
|
||||
"triggername LIKE 'genfkey%';"
|
||||
, 0, 0, 0
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = detectSchemaProblem(db, "foreign key is not unique",
|
||||
"SELECT fkid, from_tbl "
|
||||
"FROM temp.fkey2 "
|
||||
"WHERE NOT EXISTS (SELECT 1 "
|
||||
"FROM temp.idx WHERE tablename=to_tbl AND fkey2.cols==idx.cols"
|
||||
")", pCallback
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define GENFKEY_ERROR 1
|
||||
#define GENFKEY_DROPTRIGGER 2
|
||||
#define GENFKEY_CREATETRIGGER 3
|
||||
static int genfkey_create_triggers(
|
||||
sqlite3 *sdb, /* Connection to read schema from */
|
||||
const char *zDb, /* Name of db to read ("main", "temp") */
|
||||
void *pCtx, /* Context pointer to pass to xData */
|
||||
int (*xData)(void *, int, const char *)
|
||||
){
|
||||
const char *zSql =
|
||||
"SELECT multireplace('"
|
||||
|
||||
"-- Triggers for foreign key mapping:\n"
|
||||
"--\n"
|
||||
"-- /from_readable/ REFERENCES /to_readable/\n"
|
||||
"-- on delete /on_delete/\n"
|
||||
"-- on update /on_update/\n"
|
||||
"--\n"
|
||||
|
||||
/* The "BEFORE INSERT ON <referencing>" trigger. This trigger's job is to
|
||||
** throw an exception if the user tries to insert a row into the
|
||||
** referencing table for which there is no corresponding row in
|
||||
** the referenced table.
|
||||
*/
|
||||
"CREATE TRIGGER /name/_insert_referencing BEFORE INSERT ON /tbl/ WHEN \n"
|
||||
" /key_notnull/ AND NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n"
|
||||
"BEGIN\n"
|
||||
" SELECT RAISE(ABORT, ''constraint failed'');\n"
|
||||
"END;\n"
|
||||
|
||||
/* The "BEFORE UPDATE ON <referencing>" trigger. This trigger's job
|
||||
** is to throw an exception if the user tries to update a row in the
|
||||
** referencing table causing it to correspond to no row in the
|
||||
** referenced table.
|
||||
*/
|
||||
"CREATE TRIGGER /name/_update_referencing BEFORE\n"
|
||||
" UPDATE OF /rkey_list/ ON /tbl/ WHEN \n"
|
||||
" /key_notnull/ AND \n"
|
||||
" NOT EXISTS (SELECT 1 FROM /ref/ WHERE /cond1/)\n"
|
||||
"BEGIN\n"
|
||||
" SELECT RAISE(ABORT, ''constraint failed'');\n"
|
||||
"END;\n"
|
||||
|
||||
|
||||
/* The "BEFORE DELETE ON <referenced>" trigger. This trigger's job
|
||||
** is to detect when a row is deleted from the referenced table to
|
||||
** which rows in the referencing table correspond. The action taken
|
||||
** depends on the value of the 'ON DELETE' clause.
|
||||
*/
|
||||
"CREATE TRIGGER /name/_delete_referenced BEFORE DELETE ON /ref/ WHEN\n"
|
||||
" EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n"
|
||||
"BEGIN\n"
|
||||
" /delete_action/\n"
|
||||
"END;\n"
|
||||
|
||||
/* The "BEFORE DELETE ON <referenced>" trigger. This trigger's job
|
||||
** is to detect when the key columns of a row in the referenced table
|
||||
** to which one or more rows in the referencing table correspond are
|
||||
** updated. The action taken depends on the value of the 'ON UPDATE'
|
||||
** clause.
|
||||
*/
|
||||
"CREATE TRIGGER /name/_update_referenced AFTER\n"
|
||||
" UPDATE OF /fkey_list/ ON /ref/ WHEN \n"
|
||||
" EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n"
|
||||
"BEGIN\n"
|
||||
" /update_action/\n"
|
||||
"END;\n"
|
||||
"'"
|
||||
|
||||
/* These are used in the SQL comment written above each set of triggers */
|
||||
", '/from_readable/', from_tbl || '(' || sj(from_col, ', ') || ')'"
|
||||
", '/to_readable/', to_tbl || '(' || sj(to_col, ', ') || ')'"
|
||||
", '/on_delete/', on_delete"
|
||||
", '/on_update/', on_update"
|
||||
|
||||
", '/name/', 'genfkey' || min(rowid)"
|
||||
", '/tbl/', dq(from_tbl)"
|
||||
", '/ref/', dq(to_tbl)"
|
||||
", '/key_notnull/', sj('new.' || dq(from_col) || ' IS NOT NULL', ' AND ')"
|
||||
|
||||
", '/fkey_list/', sj(to_col, ', ')"
|
||||
", '/rkey_list/', sj(from_col, ', ')"
|
||||
|
||||
", '/cond1/', sj(multireplace('new./from/ == /to/'"
|
||||
", '/from/', dq(from_col)"
|
||||
", '/to/', dq(to_col)"
|
||||
"), ' AND ')"
|
||||
", '/cond2/', sj(multireplace('old./to/ == /from/'"
|
||||
", '/from/', dq(from_col)"
|
||||
", '/to/', dq(to_col)"
|
||||
"), ' AND ')"
|
||||
|
||||
", '/update_action/', CASE on_update "
|
||||
"WHEN 'SET NULL' THEN "
|
||||
"multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' "
|
||||
", '/setlist/', sj(from_col||' = NULL',', ')"
|
||||
", '/tbl/', dq(from_tbl)"
|
||||
", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')"
|
||||
")"
|
||||
"WHEN 'CASCADE' THEN "
|
||||
"multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' "
|
||||
", '/setlist/', sj(dq(from_col)||' = new.'||dq(to_col),', ')"
|
||||
", '/tbl/', dq(from_tbl)"
|
||||
", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')"
|
||||
")"
|
||||
"ELSE "
|
||||
" 'SELECT RAISE(ABORT, ''constraint failed'');'"
|
||||
"END "
|
||||
|
||||
", '/delete_action/', CASE on_delete "
|
||||
"WHEN 'SET NULL' THEN "
|
||||
"multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' "
|
||||
", '/setlist/', sj(from_col||' = NULL',', ')"
|
||||
", '/tbl/', dq(from_tbl)"
|
||||
", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')"
|
||||
")"
|
||||
"WHEN 'CASCADE' THEN "
|
||||
"multireplace('DELETE FROM /tbl/ WHERE /where/;' "
|
||||
", '/tbl/', dq(from_tbl)"
|
||||
", '/where/', sj(dq(from_col)||' = old.'||dq(to_col),' AND ')"
|
||||
")"
|
||||
"ELSE "
|
||||
" 'SELECT RAISE(ABORT, ''constraint failed'');'"
|
||||
"END "
|
||||
|
||||
") FROM temp.fkey "
|
||||
"GROUP BY from_tbl, fkid"
|
||||
;
|
||||
|
||||
int rc;
|
||||
const int enc = SQLITE_UTF8;
|
||||
sqlite3 *db = 0;
|
||||
|
||||
GenfkeyCb cb;
|
||||
cb.xData = xData;
|
||||
cb.pCtx = pCtx;
|
||||
|
||||
UNUSED_PARAMETER(zDb);
|
||||
|
||||
/* Open the working database handle. */
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
if( rc!=SQLITE_OK ) goto genfkey_exit;
|
||||
|
||||
/* Create the special scalar and aggregate functions used by this program. */
|
||||
sqlite3_create_function(db, "dq", 1, enc, 0, doublequote, 0, 0);
|
||||
sqlite3_create_function(db, "multireplace", -1, enc, db, multireplace, 0, 0);
|
||||
sqlite3_create_function(db, "sj", 2, enc, 0, 0, joinStep, joinFinalize);
|
||||
|
||||
/* Install the "schema" virtual table module */
|
||||
installSchemaModule(db, sdb);
|
||||
|
||||
/* Create and populate a temp table with the information required to
|
||||
** build the foreign key triggers. See function populateTempTable()
|
||||
** for details.
|
||||
*/
|
||||
cb.eType = GENFKEY_ERROR;
|
||||
rc = populateTempTable(db, &cb);
|
||||
if( rc!=SQLITE_OK ) goto genfkey_exit;
|
||||
|
||||
/* Unless the --no-drop option was specified, generate DROP TRIGGER
|
||||
** statements to drop any triggers in the database generated by a
|
||||
** previous run of this program.
|
||||
*/
|
||||
cb.eType = GENFKEY_DROPTRIGGER;
|
||||
rc = sqlite3_exec(db,
|
||||
"SELECT 'DROP TRIGGER main.' || dq(triggername) || ';' FROM triggers"
|
||||
,invokeCallback, (void *)&cb, 0
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto genfkey_exit;
|
||||
|
||||
/* Run the main query to create the trigger definitions. */
|
||||
cb.eType = GENFKEY_CREATETRIGGER;
|
||||
rc = sqlite3_exec(db, zSql, invokeCallback, (void *)&cb, 0);
|
||||
if( rc!=SQLITE_OK ) goto genfkey_exit;
|
||||
|
||||
genfkey_exit:
|
||||
sqlite3_close(db);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/* End genfkey logic. */
|
||||
/*************************************************************************/
|
||||
/*************************************************************************/
|
||||
|
||||
/*
|
||||
** If the following flag is set, then command execution stops
|
||||
** at an error if we are not interactive.
|
||||
|
@ -926,6 +1781,62 @@ static int run_schema_dump_query(
|
|||
return rc;
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY)
|
||||
struct GenfkeyCmd {
|
||||
sqlite3 *db; /* Database handle */
|
||||
struct callback_data *pCb; /* Callback data */
|
||||
int isIgnoreErrors; /* True for --ignore-errors */
|
||||
int isExec; /* True for --exec */
|
||||
int isNoDrop; /* True for --no-drop */
|
||||
int nErr; /* Number of errors seen so far */
|
||||
};
|
||||
typedef struct GenfkeyCmd GenfkeyCmd;
|
||||
|
||||
static int genfkeyParseArgs(GenfkeyCmd *p, char **azArg, int nArg){
|
||||
int ii;
|
||||
memset(p, 0, sizeof(GenfkeyCmd));
|
||||
|
||||
for(ii=0; ii<nArg; ii++){
|
||||
int n = strlen30(azArg[ii]);
|
||||
|
||||
if( n>2 && n<10 && 0==strncmp(azArg[ii], "--no-drop", n) ){
|
||||
p->isNoDrop = 1;
|
||||
}else if( n>2 && n<16 && 0==strncmp(azArg[ii], "--ignore-errors", n) ){
|
||||
p->isIgnoreErrors = 1;
|
||||
}else if( n>2 && n<7 && 0==strncmp(azArg[ii], "--exec", n) ){
|
||||
p->isExec = 1;
|
||||
}else{
|
||||
fprintf(stderr, "unknown option: %s\n", azArg[ii]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int genfkeyCmdCb(void *pCtx, int eType, const char *z){
|
||||
GenfkeyCmd *p = (GenfkeyCmd *)pCtx;
|
||||
if( eType==GENFKEY_ERROR && !p->isIgnoreErrors ){
|
||||
p->nErr++;
|
||||
fprintf(stderr, "%s\n", z);
|
||||
}
|
||||
|
||||
if( p->nErr==0 && (
|
||||
(eType==GENFKEY_CREATETRIGGER)
|
||||
|| (eType==GENFKEY_DROPTRIGGER && !p->isNoDrop)
|
||||
)){
|
||||
if( p->isExec ){
|
||||
sqlite3_exec(p->db, z, 0, 0, 0);
|
||||
}else{
|
||||
char *zCol = "sql";
|
||||
callback((void *)p->pCb, 1, (char **)&z, (char **)&zCol);
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Text of a help message
|
||||
*/
|
||||
|
@ -937,6 +1848,14 @@ static char zHelp[] =
|
|||
".echo ON|OFF Turn command echo on or off\n"
|
||||
".exit Exit this program\n"
|
||||
".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n"
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY)
|
||||
".genfkey ?OPTIONS? Options are:\n"
|
||||
" --no-drop: Do not drop old fkey triggers.\n"
|
||||
" --ignore-errors: Ignore tables with fkey errors\n"
|
||||
" --exec: Execute generated SQL immediately\n"
|
||||
" See file tool/genfkey.README in the source \n"
|
||||
" distribution for further information.\n"
|
||||
#endif
|
||||
".header(s) ON|OFF Turn display of headers on or off\n"
|
||||
".help Show this message\n"
|
||||
".import FILE TABLE Import data from FILE into TABLE\n"
|
||||
|
@ -1240,6 +2159,17 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
|||
}
|
||||
}else
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_SUBQUERY)
|
||||
if( c=='g' && strncmp(azArg[0], "genfkey", n)==0 ){
|
||||
GenfkeyCmd cmd;
|
||||
if( 0==genfkeyParseArgs(&cmd, &azArg[1], nArg-1) ){
|
||||
cmd.db = p->db;
|
||||
cmd.pCb = p;
|
||||
genfkey_create_triggers(p->db, "main", (void *)&cmd, genfkeyCmdCb);
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
|
||||
if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
|
||||
strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
|
||||
p->showHeader = booleanValue(azArg[1]);
|
||||
|
|
2119
src/sqlite.h.in
2119
src/sqlite.h.in
File diff suppressed because it is too large
Load Diff
207
src/sqliteInt.h
207
src/sqliteInt.h
|
@ -11,7 +11,7 @@
|
|||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.833 2009/02/05 16:53:43 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.854 2009/04/08 13:51:51 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
|
@ -444,6 +444,22 @@ extern const int sqlite3one;
|
|||
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
|
||||
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
|
||||
|
||||
/*
|
||||
** Round up a number to the next larger multiple of 8. This is used
|
||||
** to force 8-byte alignment on 64-bit architectures.
|
||||
*/
|
||||
#define ROUND8(x) (((x)+7)&~7)
|
||||
|
||||
/*
|
||||
** Round down to the nearest multiple of 8
|
||||
*/
|
||||
#define ROUNDDOWN8(x) ((x)&~7)
|
||||
|
||||
/*
|
||||
** Assert that the pointer X is aligned to an 8-byte boundary.
|
||||
*/
|
||||
#define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0)
|
||||
|
||||
/*
|
||||
** An instance of the following structure is used to store the busy-handler
|
||||
** callback for a given sqlite handle.
|
||||
|
@ -675,10 +691,17 @@ struct Schema {
|
|||
** lookaside malloc subsystem. Each available memory allocation in
|
||||
** the lookaside subsystem is stored on a linked list of LookasideSlot
|
||||
** objects.
|
||||
**
|
||||
** Lookaside allocations are only allowed for objects that are associated
|
||||
** with a particular database connection. Hence, schema information cannot
|
||||
** be stored in lookaside because in shared cache mode the schema information
|
||||
** is shared by multiple database connections. Therefore, while parsing
|
||||
** schema information, the Lookaside.bEnabled flag is cleared so that
|
||||
** lookaside allocations are not used to construct the schema objects.
|
||||
*/
|
||||
struct Lookaside {
|
||||
u16 sz; /* Size of each buffer in bytes */
|
||||
u8 bEnabled; /* True if use lookaside. False to ignore it */
|
||||
u8 bEnabled; /* False to disable new lookaside allocations */
|
||||
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
|
||||
int nOut; /* Number of buffers currently checked out */
|
||||
int mxOut; /* Highwater mark for nOut */
|
||||
|
@ -807,7 +830,26 @@ struct sqlite3 {
|
|||
#endif
|
||||
Savepoint *pSavepoint; /* List of active savepoints */
|
||||
int nSavepoint; /* Number of non-transaction savepoints */
|
||||
int nStatement; /* Number of nested statement-transactions */
|
||||
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
/* The following variables are all protected by the STATIC_MASTER
|
||||
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
|
||||
**
|
||||
** When X.pUnlockConnection==Y, that means that X is waiting for Y to
|
||||
** unlock so that it can proceed.
|
||||
**
|
||||
** When X.pBlockingConnection==Y, that means that something that X tried
|
||||
** tried to do recently failed with an SQLITE_LOCKED error due to locks
|
||||
** held by Y.
|
||||
*/
|
||||
sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */
|
||||
sqlite3 *pUnlockConnection; /* Connection to watch for unlock */
|
||||
void *pUnlockArg; /* Argument to xUnlockNotify */
|
||||
void (*xUnlockNotify)(void **, int); /* Unlock notify callback */
|
||||
sqlite3 *pNextBlocked; /* Next in list of all blocked connections */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -845,8 +887,8 @@ struct sqlite3 {
|
|||
|
||||
#define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */
|
||||
#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */
|
||||
#define SQLITE_Vtab 0x00100000 /* There exists a virtual table */
|
||||
#define SQLITE_CommitBusy 0x00200000 /* In the process of committing */
|
||||
#define SQLITE_ReverseOrder 0x00400000 /* Reverse unordered SELECTs */
|
||||
|
||||
/*
|
||||
** Possible values for the sqlite.magic field.
|
||||
|
@ -886,6 +928,7 @@ struct FuncDef {
|
|||
#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
|
||||
#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
|
||||
#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */
|
||||
#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */
|
||||
|
||||
/*
|
||||
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
|
||||
|
@ -1080,7 +1123,7 @@ struct CollSeq {
|
|||
** of a SELECT statement.
|
||||
*/
|
||||
struct Table {
|
||||
sqlite3 *db; /* Associated database connection. Might be NULL. */
|
||||
sqlite3 *dbMem; /* DB connection used for lookaside allocations. */
|
||||
char *zName; /* Name of the table or view */
|
||||
int iPKey; /* If not negative, use aCol[iPKey] as the primary key */
|
||||
int nCol; /* Number of columns in this table */
|
||||
|
@ -1091,7 +1134,6 @@ struct Table {
|
|||
u16 nRef; /* Number of pointers to this Table */
|
||||
u8 tabFlags; /* Mask of TF_* values */
|
||||
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
|
||||
Trigger *pTrigger; /* List of SQL triggers on this table */
|
||||
FKey *pFKey; /* Linked list of all foreign keys in this table */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
|
@ -1106,6 +1148,7 @@ struct Table {
|
|||
int nModuleArg; /* Number of arguments to the module */
|
||||
char **azModuleArg; /* Text of all module args. [0] is module name */
|
||||
#endif
|
||||
Trigger *pTrigger; /* List of triggers stored in pSchema */
|
||||
Schema *pSchema; /* Schema that contains this table */
|
||||
Table *pNextZombie; /* Next on the Parse.pZombieTab list */
|
||||
};
|
||||
|
@ -1363,19 +1406,27 @@ struct AggInfo {
|
|||
** Each node of an expression in the parse tree is an instance
|
||||
** of this structure.
|
||||
**
|
||||
** Expr.op is the opcode. The integer parser token codes are reused
|
||||
** as opcodes here. For example, the parser defines TK_GE to be an integer
|
||||
** code representing the ">=" operator. This same integer code is reused
|
||||
** Expr.op is the opcode. The integer parser token codes are reused
|
||||
** as opcodes here. For example, the parser defines TK_GE to be an integer
|
||||
** code representing the ">=" operator. This same integer code is reused
|
||||
** to represent the greater-than-or-equal-to operator in the expression
|
||||
** tree.
|
||||
**
|
||||
** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list
|
||||
** of argument if the expression is a function.
|
||||
** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
|
||||
** or TK_STRING), then Expr.token contains the text of the SQL literal. If
|
||||
** the expression is a variable (TK_VARIABLE), then Expr.token contains the
|
||||
** variable name. Finally, if the expression is an SQL function (TK_FUNCTION),
|
||||
** then Expr.token contains the name of the function.
|
||||
**
|
||||
** Expr.token is the operator token for this node. For some expressions
|
||||
** that have subexpressions, Expr.token can be the complete text that gave
|
||||
** rise to the Expr. In the latter case, the token is marked as being
|
||||
** a compound token.
|
||||
** Expr.pRight and Expr.pLeft are the left and right subexpressions of a
|
||||
** binary operator. Either or both may be NULL.
|
||||
**
|
||||
** Expr.x.pList is a list of arguments if the expression is an SQL function,
|
||||
** a CASE expression or an IN expression of the form "<lhs> IN (<y>, <z>...)".
|
||||
** Expr.x.pSelect is used if the expression is a sub-select or an expression of
|
||||
** the form "<lhs> IN (SELECT ...)". If the EP_xIsSelect bit is set in the
|
||||
** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is
|
||||
** valid.
|
||||
**
|
||||
** An expression of the form ID or ID.ID refers to a column in a table.
|
||||
** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is
|
||||
|
@ -1385,10 +1436,9 @@ struct AggInfo {
|
|||
** value is also stored in the Expr.iAgg column in the aggregate so that
|
||||
** it can be accessed after all aggregates are computed.
|
||||
**
|
||||
** If the expression is a function, the Expr.iTable is an integer code
|
||||
** representing which function. If the expression is an unbound variable
|
||||
** marker (a question mark character '?' in the original SQL) then the
|
||||
** Expr.iTable holds the index number for that variable.
|
||||
** If the expression is an unbound variable marker (a question mark
|
||||
** character '?' in the original SQL) then the Expr.iTable holds the index
|
||||
** number for that variable.
|
||||
**
|
||||
** If the expression is a subquery then Expr.iColumn holds an integer
|
||||
** register number containing the result of the subquery. If the
|
||||
|
@ -1396,32 +1446,62 @@ struct AggInfo {
|
|||
** gives a different answer at different times during statement processing
|
||||
** then iTable is the address of a subroutine that computes the subquery.
|
||||
**
|
||||
** The Expr.pSelect field points to a SELECT statement. The SELECT might
|
||||
** be the right operand of an IN operator. Or, if a scalar SELECT appears
|
||||
** in an expression the opcode is TK_SELECT and Expr.pSelect is the only
|
||||
** operand.
|
||||
**
|
||||
** If the Expr is of type OP_Column, and the table it is selecting from
|
||||
** is a disk table or the "old.*" pseudo-table, then pTab points to the
|
||||
** corresponding table definition.
|
||||
**
|
||||
** ALLOCATION NOTES:
|
||||
**
|
||||
** Expr objects can use a lot of memory space in database schema. To
|
||||
** help reduce memory requirements, sometimes an Expr object will be
|
||||
** truncated. And to reduce the number of memory allocations, sometimes
|
||||
** two or more Expr objects will be stored in a single memory allocation,
|
||||
** together with Expr.token and/or Expr.span strings.
|
||||
**
|
||||
** If the EP_Reduced, EP_SpanToken, and EP_TokenOnly flags are set when
|
||||
** an Expr object is truncated. When EP_Reduced is set, then all
|
||||
** the child Expr objects in the Expr.pLeft and Expr.pRight subtrees
|
||||
** are contained within the same memory allocation. Note, however, that
|
||||
** the subtrees in Expr.x.pList or Expr.x.pSelect are always separately
|
||||
** allocated, regardless of whether or not EP_Reduced is set.
|
||||
*/
|
||||
struct Expr {
|
||||
u8 op; /* Operation performed by this node */
|
||||
char affinity; /* The affinity of the column or 0 if not a column */
|
||||
u16 flags; /* Various flags. See below */
|
||||
CollSeq *pColl; /* The collation type of the column or 0 */
|
||||
Expr *pLeft, *pRight; /* Left and right subnodes */
|
||||
ExprList *pList; /* A list of expressions used as function arguments
|
||||
** or in "<expr> IN (<expr-list)" */
|
||||
VVA_ONLY(u8 vvaFlags;) /* Flags used for VV&A only. EVVA_* below. */
|
||||
u16 flags; /* Various flags. EP_* See below */
|
||||
Token token; /* An operand token */
|
||||
|
||||
/* If the EP_TokenOnly flag is set in the Expr.flags mask, then no
|
||||
** space is allocated for the fields below this point. An attempt to
|
||||
** access them will result in a segfault or malfunction.
|
||||
*********************************************************************/
|
||||
|
||||
Token span; /* Complete text of the expression */
|
||||
|
||||
/* If the EP_SpanToken flag is set in the Expr.flags mask, then no
|
||||
** space is allocated for the fields below this point. An attempt to
|
||||
** access them will result in a segfault or malfunction.
|
||||
*********************************************************************/
|
||||
|
||||
Expr *pLeft; /* Left subnode */
|
||||
Expr *pRight; /* Right subnode */
|
||||
union {
|
||||
ExprList *pList; /* Function arguments or in "<expr> IN (<expr-list)" */
|
||||
Select *pSelect; /* Used for sub-selects and "<expr> IN (<select>)" */
|
||||
} x;
|
||||
CollSeq *pColl; /* The collation type of the column or 0 */
|
||||
|
||||
/* If the EP_Reduced flag is set in the Expr.flags mask, then no
|
||||
** space is allocated for the fields below this point. An attempt to
|
||||
** access them will result in a segfault or malfunction.
|
||||
*********************************************************************/
|
||||
|
||||
int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the
|
||||
** iColumn-th field of the iTable-th table. */
|
||||
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
|
||||
int iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
|
||||
int iRightJoinTable; /* If EP_FromJoin, the right table of the join */
|
||||
Select *pSelect; /* When the expression is a sub-select. Also the
|
||||
** right side of "<expr> IN (<select>)" */
|
||||
Table *pTab; /* Table for TK_COLUMN expressions. */
|
||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
int nHeight; /* Height of the tree headed by this node */
|
||||
|
@ -1443,6 +1523,21 @@ struct Expr {
|
|||
#define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */
|
||||
#define EP_FixedDest 0x0400 /* Result needed in a specific register */
|
||||
#define EP_IntValue 0x0800 /* Integer value contained in iTable */
|
||||
#define EP_xIsSelect 0x1000 /* x.pSelect is valid (otherwise x.pList is) */
|
||||
|
||||
#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_SpanToken 0x8000 /* Expr size is EXPR_SPANTOKENSIZE bytes */
|
||||
|
||||
/*
|
||||
** The following are the meanings of bits in the Expr.vvaFlags field.
|
||||
** This information is only used when SQLite is compiled with
|
||||
** SQLITE_DEBUG defined.
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
#define EVVA_ReadOnlyToken 0x01 /* Expr.token.z is read-only */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** These macros can be used to test, set, or clear bits in the
|
||||
** Expr.flags field.
|
||||
|
@ -1452,6 +1547,23 @@ struct Expr {
|
|||
#define ExprSetProperty(E,P) (E)->flags|=(P)
|
||||
#define ExprClearProperty(E,P) (E)->flags&=~(P)
|
||||
|
||||
/*
|
||||
** Macros to determine the number of bytes required by a normal Expr
|
||||
** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
|
||||
** and an Expr struct with the EP_TokenOnly flag set.
|
||||
*/
|
||||
#define EXPR_FULLSIZE sizeof(Expr) /* Full size */
|
||||
#define EXPR_REDUCEDSIZE offsetof(Expr,iTable) /* Common features */
|
||||
#define EXPR_SPANTOKENSIZE offsetof(Expr,pLeft) /* Fewer features */
|
||||
#define EXPR_TOKENONLYSIZE offsetof(Expr,span) /* Smallest possible */
|
||||
|
||||
/*
|
||||
** Flags passed to the sqlite3ExprDup() function. See the header comment
|
||||
** above sqlite3ExprDup() for details.
|
||||
*/
|
||||
#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */
|
||||
#define EXPRDUP_SPAN 0x0002 /* Make a copy of Expr.span */
|
||||
|
||||
/*
|
||||
** A list of expressions. Each expression may optionally have a
|
||||
** name. An expr/name combination can be used in several ways, such
|
||||
|
@ -2237,7 +2349,7 @@ void sqlite3SetString(char **, sqlite3*, const char*, ...);
|
|||
void sqlite3ErrorMsg(Parse*, const char*, ...);
|
||||
void sqlite3ErrorClear(Parse*);
|
||||
void sqlite3Dequote(char*);
|
||||
void sqlite3DequoteExpr(sqlite3*, Expr*);
|
||||
void sqlite3DequoteExpr(Expr*);
|
||||
int sqlite3KeywordCode(const unsigned char*, int);
|
||||
int sqlite3RunParser(Parse*, const char*, char **);
|
||||
void sqlite3FinishCoding(Parse*);
|
||||
|
@ -2379,12 +2491,12 @@ void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
|
|||
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
|
||||
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
|
||||
void sqlite3BeginWriteOperation(Parse*, int, int);
|
||||
Expr *sqlite3ExprDup(sqlite3*,Expr*);
|
||||
void sqlite3TokenCopy(sqlite3*,Token*, Token*);
|
||||
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*);
|
||||
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*);
|
||||
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
|
||||
void sqlite3TokenCopy(sqlite3*,Token*,const Token*);
|
||||
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
|
||||
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
|
||||
IdList *sqlite3IdListDup(sqlite3*,IdList*);
|
||||
Select *sqlite3SelectDup(sqlite3*,Select*);
|
||||
Select *sqlite3SelectDup(sqlite3*,Select*,int);
|
||||
void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
|
||||
FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
|
||||
void sqlite3RegisterBuiltinFunctions(sqlite3*);
|
||||
|
@ -2411,9 +2523,10 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
|
|||
void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*);
|
||||
void sqlite3DropTrigger(Parse*, SrcList*, int);
|
||||
void sqlite3DropTriggerPtr(Parse*, Trigger*);
|
||||
int sqlite3TriggersExist(Table*, int, ExprList*);
|
||||
int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int,
|
||||
int, int, u32*, u32*);
|
||||
Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask);
|
||||
Trigger *sqlite3TriggerList(Parse *, Table *);
|
||||
int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
|
||||
int, int, int, int, u32*, u32*);
|
||||
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
|
||||
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
|
||||
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
|
||||
|
@ -2428,7 +2541,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
|
|||
# define sqlite3DeleteTrigger(A,B)
|
||||
# define sqlite3DropTriggerPtr(A,B)
|
||||
# define sqlite3UnlinkAndDeleteTrigger(A,B,C)
|
||||
# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K) 0
|
||||
# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K,L) 0
|
||||
# define sqlite3TriggerList(X, Y) 0
|
||||
#endif
|
||||
|
||||
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
|
||||
|
@ -2460,7 +2574,7 @@ int sqlite3GetInt32(const char *, int*);
|
|||
int sqlite3FitsIn64Bits(const char *, int);
|
||||
int sqlite3Utf16ByteLen(const void *pData, int nChar);
|
||||
int sqlite3Utf8CharLen(const char *pData, int nByte);
|
||||
int sqlite3Utf8Read(const u8*, const u8*, const u8**);
|
||||
int sqlite3Utf8Read(const u8*, const u8**);
|
||||
|
||||
/*
|
||||
** Routines to read and write variable-length integers. These used to
|
||||
|
@ -2686,6 +2800,17 @@ int sqlite3IsMemJournal(sqlite3_file *);
|
|||
u32 sqlite3Get4byte(const u8*);
|
||||
void sqlite3Put4byte(u8*, u32);
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
void sqlite3ConnectionBlocked(sqlite3 *, sqlite3 *);
|
||||
void sqlite3ConnectionUnlocked(sqlite3 *db);
|
||||
void sqlite3ConnectionClosed(sqlite3 *db);
|
||||
#else
|
||||
#define sqlite3ConnectionBlocked(x,y)
|
||||
#define sqlite3ConnectionUnlocked(x)
|
||||
#define sqlite3ConnectionClosed(x)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef SQLITE_SSE
|
||||
#include "sseInt.h"
|
||||
#endif
|
||||
|
|
33
src/table.c
33
src/table.c
|
@ -16,7 +16,7 @@
|
|||
** These routines are in a separate files so that they will not be linked
|
||||
** if they are not used.
|
||||
**
|
||||
** $Id: table.c,v 1.39 2009/01/19 20:49:10 drh Exp $
|
||||
** $Id: table.c,v 1.40 2009/04/10 14:28:00 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -29,14 +29,13 @@
|
|||
** to the callback function is uses to build the result.
|
||||
*/
|
||||
typedef struct TabResult {
|
||||
char **azResult;
|
||||
char *zErrMsg;
|
||||
int nResult;
|
||||
int nAlloc;
|
||||
int nRow;
|
||||
int nColumn;
|
||||
int nData;
|
||||
int rc;
|
||||
char **azResult; /* Accumulated output */
|
||||
char *zErrMsg; /* Error message text, if an error occurs */
|
||||
int nAlloc; /* Slots allocated for azResult[] */
|
||||
int nRow; /* Number of rows in the result */
|
||||
int nColumn; /* Number of columns in the result */
|
||||
int nData; /* Slots used in azResult[]. (nRow+1)*nColumn */
|
||||
int rc; /* Return code from sqlite3_exec() */
|
||||
} TabResult;
|
||||
|
||||
/*
|
||||
|
@ -45,10 +44,10 @@ typedef struct TabResult {
|
|||
** memory as necessary.
|
||||
*/
|
||||
static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
|
||||
TabResult *p = (TabResult*)pArg;
|
||||
int need;
|
||||
int i;
|
||||
char *z;
|
||||
TabResult *p = (TabResult*)pArg; /* Result accumulator */
|
||||
int need; /* Slots needed in p->azResult[] */
|
||||
int i; /* Loop counter */
|
||||
char *z; /* A single column of result */
|
||||
|
||||
/* Make sure there is enough space in p->azResult to hold everything
|
||||
** we need to remember from this invocation of the callback.
|
||||
|
@ -58,9 +57,9 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
|
|||
}else{
|
||||
need = nCol;
|
||||
}
|
||||
if( p->nData + need >= p->nAlloc ){
|
||||
if( p->nData + need > p->nAlloc ){
|
||||
char **azNew;
|
||||
p->nAlloc = p->nAlloc*2 + need + 1;
|
||||
p->nAlloc = p->nAlloc*2 + need;
|
||||
azNew = sqlite3_realloc( p->azResult, sizeof(char*)*p->nAlloc );
|
||||
if( azNew==0 ) goto malloc_failed;
|
||||
p->azResult = azNew;
|
||||
|
@ -134,7 +133,6 @@ int sqlite3_get_table(
|
|||
if( pnRow ) *pnRow = 0;
|
||||
if( pzErrMsg ) *pzErrMsg = 0;
|
||||
res.zErrMsg = 0;
|
||||
res.nResult = 0;
|
||||
res.nRow = 0;
|
||||
res.nColumn = 0;
|
||||
res.nData = 1;
|
||||
|
@ -168,13 +166,12 @@ int sqlite3_get_table(
|
|||
}
|
||||
if( res.nAlloc>res.nData ){
|
||||
char **azNew;
|
||||
azNew = sqlite3_realloc( res.azResult, sizeof(char*)*(res.nData+1) );
|
||||
azNew = sqlite3_realloc( res.azResult, sizeof(char*)*res.nData );
|
||||
if( azNew==0 ){
|
||||
sqlite3_free_table(&res.azResult[1]);
|
||||
db->errCode = SQLITE_NOMEM;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
res.nAlloc = res.nData+1;
|
||||
res.azResult = azNew;
|
||||
}
|
||||
*pazResult = &res.azResult[1];
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
** A TCL Interface to SQLite. Append this file to sqlite3.c and
|
||||
** compile the whole thing to build a TCL-enabled version of SQLite.
|
||||
**
|
||||
** $Id: tclsqlite.c,v 1.237 2009/02/17 16:29:11 danielk1977 Exp $
|
||||
** $Id: tclsqlite.c,v 1.241 2009/03/27 12:44:35 drh Exp $
|
||||
*/
|
||||
#include "tcl.h"
|
||||
#include <errno.h>
|
||||
|
@ -109,6 +109,7 @@ struct SqliteDb {
|
|||
SqlFunc *pFunc; /* List of SQL functions */
|
||||
Tcl_Obj *pUpdateHook; /* Update hook script (if any) */
|
||||
Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */
|
||||
Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */
|
||||
SqlCollate *pCollate; /* List of SQL collation functions */
|
||||
int rc; /* Return code of most recent sqlite3_exec() */
|
||||
Tcl_Obj *pCollateNeeded; /* Collation needed script */
|
||||
|
@ -574,6 +575,33 @@ static void DbRollbackHandler(void *clientData){
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
|
||||
static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){
|
||||
char zBuf[64];
|
||||
sprintf(zBuf, "%d", iArg);
|
||||
Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY);
|
||||
sprintf(zBuf, "%d", nArg);
|
||||
Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY);
|
||||
}
|
||||
#else
|
||||
# define setTestUnlockNotifyVars(x,y,z)
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
static void DbUnlockNotify(void **apArg, int nArg){
|
||||
int i;
|
||||
for(i=0; i<nArg; i++){
|
||||
const int flags = (TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT);
|
||||
SqliteDb *pDb = (SqliteDb *)apArg[i];
|
||||
setTestUnlockNotifyVars(pDb->interp, i, nArg);
|
||||
assert( pDb->pUnlockNotify);
|
||||
Tcl_EvalObjEx(pDb->interp, pDb->pUnlockNotify, flags);
|
||||
Tcl_DecrRefCount(pDb->pUnlockNotify);
|
||||
pDb->pUnlockNotify = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void DbUpdateHandler(
|
||||
void *p,
|
||||
int op,
|
||||
|
@ -993,8 +1021,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
|||
"profile", "progress", "rekey",
|
||||
"restore", "rollback_hook", "status",
|
||||
"timeout", "total_changes", "trace",
|
||||
"transaction", "update_hook", "version",
|
||||
0
|
||||
"transaction", "unlock_notify", "update_hook",
|
||||
"version", 0
|
||||
};
|
||||
enum DB_enum {
|
||||
DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
|
||||
|
@ -1007,7 +1035,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
|||
DB_PROFILE, DB_PROGRESS, DB_REKEY,
|
||||
DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS,
|
||||
DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
|
||||
DB_TRANSACTION, DB_UPDATE_HOOK, DB_VERSION,
|
||||
DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK,
|
||||
DB_VERSION,
|
||||
};
|
||||
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
|
||||
|
||||
|
@ -2450,6 +2479,42 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
|||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** $db unlock_notify ?script?
|
||||
*/
|
||||
case DB_UNLOCK_NOTIFY: {
|
||||
#ifndef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
Tcl_AppendResult(interp, "unlock_notify not available in this build", 0);
|
||||
rc = TCL_ERROR;
|
||||
#else
|
||||
if( objc!=2 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
void (*xNotify)(void **, int) = 0;
|
||||
void *pNotifyArg = 0;
|
||||
|
||||
if( pDb->pUnlockNotify ){
|
||||
Tcl_DecrRefCount(pDb->pUnlockNotify);
|
||||
pDb->pUnlockNotify = 0;
|
||||
}
|
||||
|
||||
if( objc==3 ){
|
||||
xNotify = DbUnlockNotify;
|
||||
pNotifyArg = (void *)pDb;
|
||||
pDb->pUnlockNotify = objv[2];
|
||||
Tcl_IncrRefCount(pDb->pUnlockNotify);
|
||||
}
|
||||
|
||||
if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
|
||||
rc = TCL_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** $db update_hook ?script?
|
||||
** $db rollback_hook ?script?
|
||||
|
@ -2530,8 +2595,21 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
|||
int i;
|
||||
const char *zFile;
|
||||
const char *zVfs = 0;
|
||||
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX;
|
||||
int flags;
|
||||
Tcl_DString translatedFilename;
|
||||
|
||||
/* In normal use, each TCL interpreter runs in a single thread. So
|
||||
** by default, we can turn of mutexing on SQLite database connections.
|
||||
** However, for testing purposes it is useful to have mutexes turned
|
||||
** on. So, by default, mutexes default off. But if compiled with
|
||||
** SQLITE_TCL_DEFAULT_FULLMUTEX then mutexes default on.
|
||||
*/
|
||||
#ifdef SQLITE_TCL_DEFAULT_FULLMUTEX
|
||||
flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
||||
#else
|
||||
flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX;
|
||||
#endif
|
||||
|
||||
if( objc==2 ){
|
||||
zArg = Tcl_GetStringFromObj(objv[1], 0);
|
||||
if( strcmp(zArg,"-version")==0 ){
|
||||
|
|
78
src/test1.c
78
src/test1.c
|
@ -13,7 +13,7 @@
|
|||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.347 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** $Id: test1.c,v 1.351 2009/04/08 15:45:32 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
|
@ -125,6 +125,7 @@ const char *sqlite3TestErrorName(int rc){
|
|||
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
|
||||
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
|
||||
case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
|
||||
case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break;
|
||||
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
|
||||
case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
|
||||
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
||||
|
@ -495,7 +496,7 @@ static int test_exec_nr(
|
|||
/*
|
||||
** Usage: sqlite3_mprintf_z_test SEPARATOR ARG0 ARG1 ...
|
||||
**
|
||||
** Test the %z format of sqliteMPrintf(). Use multiple mprintf() calls to
|
||||
** Test the %z format of sqlite_mprintf(). Use multiple mprintf() calls to
|
||||
** concatenate arg0 through argn using separator as the separator.
|
||||
** Return the result.
|
||||
*/
|
||||
|
@ -509,7 +510,7 @@ static int test_mprintf_z(
|
|||
int i;
|
||||
|
||||
for(i=2; i<argc && (i==2 || zResult); i++){
|
||||
zResult = sqlite3MPrintf(0, "%z%s%s", zResult, argv[1], argv[i]);
|
||||
zResult = sqlite3_mprintf("%z%s%s", zResult, argv[1], argv[i]);
|
||||
}
|
||||
Tcl_AppendResult(interp, zResult, 0);
|
||||
sqlite3_free(zResult);
|
||||
|
@ -519,7 +520,7 @@ static int test_mprintf_z(
|
|||
/*
|
||||
** Usage: sqlite3_mprintf_n_test STRING
|
||||
**
|
||||
** Test the %n format of sqliteMPrintf(). Return the length of the
|
||||
** Test the %n format of sqlite_mprintf(). Return the length of the
|
||||
** input string.
|
||||
*/
|
||||
static int test_mprintf_n(
|
||||
|
@ -530,7 +531,7 @@ static int test_mprintf_n(
|
|||
){
|
||||
char *zStr;
|
||||
int n = 0;
|
||||
zStr = sqlite3MPrintf(0, "%s%n", argv[1], &n);
|
||||
zStr = sqlite3_mprintf("%s%n", argv[1], &n);
|
||||
sqlite3_free(zStr);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
|
||||
return TCL_OK;
|
||||
|
@ -3260,6 +3261,7 @@ static int test_errmsg16(
|
|||
#ifndef SQLITE_OMIT_UTF16
|
||||
sqlite3 *db;
|
||||
const void *zErr;
|
||||
const char *z;
|
||||
int bytes = 0;
|
||||
|
||||
if( objc!=2 ){
|
||||
|
@ -3271,7 +3273,8 @@ static int test_errmsg16(
|
|||
|
||||
zErr = sqlite3_errmsg16(db);
|
||||
if( zErr ){
|
||||
bytes = sqlite3Utf16ByteLen(zErr, -1);
|
||||
z = zErr;
|
||||
for(bytes=0; z[bytes] || z[bytes+1]; bytes+=2){}
|
||||
}
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zErr, bytes));
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
||||
|
@ -3661,6 +3664,24 @@ static int test_step(
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_sql(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "STMT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
Tcl_SetResult(interp, (char *)sqlite3_sql(pStmt), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_column_count STMT
|
||||
**
|
||||
|
@ -3934,7 +3955,10 @@ static int test_stmt_utf16(
|
|||
|
||||
zName16 = xFunc(pStmt, col);
|
||||
if( zName16 ){
|
||||
pRet = Tcl_NewByteArrayObj(zName16, sqlite3Utf16ByteLen(zName16, -1)+2);
|
||||
int n;
|
||||
const char *z = zName16;
|
||||
for(n=0; z[n] || z[n+1]; n+=2){}
|
||||
pRet = Tcl_NewByteArrayObj(zName16, n+2);
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_UTF16 */
|
||||
|
@ -4816,6 +4840,42 @@ static int test_pcache_stats(
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
static void test_unlock_notify_cb(void **aArg, int nArg){
|
||||
int ii;
|
||||
for(ii=0; ii<nArg; ii++){
|
||||
Tcl_EvalEx((Tcl_Interp *)aArg[ii], "unlock_notify", -1, TCL_EVAL_GLOBAL);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_UNLOCK_NOTIFY */
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_unlock_notify db
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
static int test_unlock_notify(
|
||||
ClientData clientData, /* Unused */
|
||||
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;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_unlock_notify(db, test_unlock_notify_cb, (void *)interp);
|
||||
Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
|
@ -4916,6 +4976,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
|||
{ "sqlite3_transfer_bindings", test_transfer_bind ,0 },
|
||||
{ "sqlite3_changes", test_changes ,0 },
|
||||
{ "sqlite3_step", test_step ,0 },
|
||||
{ "sqlite3_sql", test_sql ,0 },
|
||||
{ "sqlite3_next_stmt", test_next_stmt ,0 },
|
||||
|
||||
{ "sqlite3_release_memory", test_release_memory, 0},
|
||||
|
@ -5000,6 +5061,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
|||
{ "sqlite3_blob_write", test_blob_write, 0 },
|
||||
#endif
|
||||
{ "pcache_stats", test_pcache_stats, 0 },
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
{ "sqlite3_unlock_notify", test_unlock_notify, 0 },
|
||||
#endif
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
int i;
|
||||
|
|
11
src/test3.c
11
src/test3.c
|
@ -13,7 +13,7 @@
|
|||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test3.c,v 1.102 2008/10/27 13:59:34 danielk1977 Exp $
|
||||
** $Id: test3.c,v 1.103 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
|
@ -235,7 +235,7 @@ static int btree_begin_statement(
|
|||
}
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeBeginStmt(pBt);
|
||||
rc = sqlite3BtreeBeginStmt(pBt, 1);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
|
@ -264,7 +264,10 @@ static int btree_rollback_statement(
|
|||
}
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeRollbackStmt(pBt);
|
||||
rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0);
|
||||
}
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
|
@ -293,7 +296,7 @@ static int btree_commit_statement(
|
|||
}
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeCommitStmt(pBt);
|
||||
rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
|
|
20
src/test8.c
20
src/test8.c
|
@ -13,7 +13,7 @@
|
|||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test8.c,v 1.75 2008/08/31 00:29:08 shane Exp $
|
||||
** $Id: test8.c,v 1.76 2009/04/08 15:45:32 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
|
@ -253,7 +253,7 @@ static int getIndexArray(
|
|||
}
|
||||
|
||||
/* Compile an sqlite pragma to loop through all indices on table zTab */
|
||||
zSql = sqlite3MPrintf(0, "PRAGMA index_list(%s)", zTab);
|
||||
zSql = sqlite3_mprintf("PRAGMA index_list(%s)", zTab);
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto get_index_array_out;
|
||||
|
@ -267,7 +267,7 @@ static int getIndexArray(
|
|||
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
const char *zIdx = (const char *)sqlite3_column_text(pStmt, 1);
|
||||
sqlite3_stmt *pStmt2 = 0;
|
||||
zSql = sqlite3MPrintf(0, "PRAGMA index_info(%s)", zIdx);
|
||||
zSql = sqlite3_mprintf("PRAGMA index_info(%s)", zIdx);
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto get_index_array_out;
|
||||
|
@ -413,7 +413,7 @@ static int echoConstructor(
|
|||
pVtab->db = db;
|
||||
|
||||
/* Allocate echo_vtab.zThis */
|
||||
pVtab->zThis = sqlite3MPrintf(0, "%s", argv[2]);
|
||||
pVtab->zThis = sqlite3_mprintf("%s", argv[2]);
|
||||
if( !pVtab->zThis ){
|
||||
echoDestructor((sqlite3_vtab *)pVtab);
|
||||
return SQLITE_NOMEM;
|
||||
|
@ -421,10 +421,10 @@ static int echoConstructor(
|
|||
|
||||
/* Allocate echo_vtab.zTableName */
|
||||
if( argc>3 ){
|
||||
pVtab->zTableName = sqlite3MPrintf(0, "%s", argv[3]);
|
||||
pVtab->zTableName = sqlite3_mprintf("%s", argv[3]);
|
||||
dequoteString(pVtab->zTableName);
|
||||
if( pVtab->zTableName && pVtab->zTableName[0]=='*' ){
|
||||
char *z = sqlite3MPrintf(0, "%s%s", argv[2], &(pVtab->zTableName[1]));
|
||||
char *z = sqlite3_mprintf("%s%s", argv[2], &(pVtab->zTableName[1]));
|
||||
sqlite3_free(pVtab->zTableName);
|
||||
pVtab->zTableName = z;
|
||||
pVtab->isPattern = 1;
|
||||
|
@ -482,8 +482,8 @@ static int echoCreate(
|
|||
if( rc==SQLITE_OK && argc==5 ){
|
||||
char *zSql;
|
||||
echo_vtab *pVtab = *(echo_vtab **)ppVtab;
|
||||
pVtab->zLogName = sqlite3MPrintf(0, "%s", argv[4]);
|
||||
zSql = sqlite3MPrintf(0, "CREATE TABLE %Q(logmsg)", pVtab->zLogName);
|
||||
pVtab->zLogName = sqlite3_mprintf("%s", argv[4]);
|
||||
zSql = sqlite3_mprintf("CREATE TABLE %Q(logmsg)", pVtab->zLogName);
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -536,7 +536,7 @@ static int echoDestroy(sqlite3_vtab *pVtab){
|
|||
/* Drop the "log" table, if one exists (see echoCreate() for details) */
|
||||
if( p && p->zLogName ){
|
||||
char *zSql;
|
||||
zSql = sqlite3MPrintf(0, "DROP TABLE %Q", p->zLogName);
|
||||
zSql = sqlite3_mprintf("DROP TABLE %Q", p->zLogName);
|
||||
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
@ -1224,7 +1224,7 @@ static int echoRename(sqlite3_vtab *vtab, const char *zNewName){
|
|||
|
||||
if( p->isPattern ){
|
||||
int nThis = strlen(p->zThis);
|
||||
char *zSql = sqlite3MPrintf(0, "ALTER TABLE %s RENAME TO %s%s",
|
||||
char *zSql = sqlite3_mprintf("ALTER TABLE %s RENAME TO %s%s",
|
||||
p->zTableName, zNewName, &p->zTableName[nThis]
|
||||
);
|
||||
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
|
||||
|
|
19
src/test9.c
19
src/test9.c
|
@ -14,7 +14,7 @@
|
|||
** for completeness. Test code is written in C for these cases
|
||||
** as there is not much point in binding to Tcl.
|
||||
**
|
||||
** $Id: test9.c,v 1.6 2008/07/11 13:53:55 drh Exp $
|
||||
** $Id: test9.c,v 1.7 2009/04/02 18:32:27 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
|
@ -114,6 +114,7 @@ static int c_misuse_test(
|
|||
){
|
||||
const char *zErrFunction = "N/A";
|
||||
sqlite3 *db = 0;
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
if( objc!=1 ){
|
||||
|
@ -138,29 +139,37 @@ static int c_misuse_test(
|
|||
goto error_out;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare(db, 0, 0, 0, 0);
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare";
|
||||
goto error_out;
|
||||
}
|
||||
assert( pStmt==0 ); /* Verify that pStmt is zeroed even on a MISUSE error */
|
||||
|
||||
rc = sqlite3_prepare_v2(db, 0, 0, 0, 0);
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare_v2(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare_v2";
|
||||
goto error_out;
|
||||
}
|
||||
assert( pStmt==0 );
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
rc = sqlite3_prepare16(db, 0, 0, 0, 0);
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare16(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare16";
|
||||
goto error_out;
|
||||
}
|
||||
rc = sqlite3_prepare16_v2(db, 0, 0, 0, 0);
|
||||
assert( pStmt==0 );
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare16_v2(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare16_v2";
|
||||
goto error_out;
|
||||
}
|
||||
assert( pStmt==0 );
|
||||
#endif
|
||||
|
||||
return TCL_OK;
|
||||
|
|
181
src/test_async.c
181
src/test_async.c
|
@ -10,7 +10,7 @@
|
|||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: test_async.c,v 1.48 2008/09/26 20:02:50 drh Exp $
|
||||
** $Id: test_async.c,v 1.57 2009/04/07 11:21:29 danielk1977 Exp $
|
||||
**
|
||||
** This file contains an example implementation of an asynchronous IO
|
||||
** backend for SQLite.
|
||||
|
@ -109,7 +109,7 @@
|
|||
#define ENABLE_FILE_LOCKING
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# include "sqlite3.h"
|
||||
# include "sqliteInt.h"
|
||||
# include <assert.h>
|
||||
# include <string.h>
|
||||
#endif
|
||||
|
@ -280,7 +280,7 @@ static struct TestAsyncStaticData {
|
|||
volatile int ioDelay; /* Extra delay between write operations */
|
||||
volatile int writerHaltWhenIdle; /* Writer thread halts when queue empty */
|
||||
volatile int writerHaltNow; /* Writer thread halts after next op */
|
||||
int ioError; /* True if an IO error has occured */
|
||||
int ioError; /* True if an IO error has occurred */
|
||||
int nFile; /* Number of open files (from sqlite pov) */
|
||||
} async = {
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
|
@ -419,7 +419,7 @@ struct AsyncFileData {
|
|||
sqlite3_file *pBaseWrite; /* Write handle to the underlying Os file */
|
||||
AsyncFileLock lock; /* Lock state for this handle */
|
||||
AsyncLock *pLock; /* AsyncLock object for this file system entry */
|
||||
AsyncWrite close;
|
||||
AsyncWrite closeOp; /* Preallocated close operation */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -483,7 +483,6 @@ static int async_mutex_lock(pthread_mutex_t *pMutex){
|
|||
assert(&(aHolder[2])==&asyncdebug.writerMutexHolder);
|
||||
|
||||
assert( pthread_self()!=0 );
|
||||
|
||||
for(iIdx=0; iIdx<3; iIdx++){
|
||||
if( pMutex==&aMutex[iIdx] ) break;
|
||||
|
||||
|
@ -610,7 +609,9 @@ static void assert_mutex_is_held(pthread_mutex_t *pMutex){
|
|||
*/
|
||||
static void addAsyncWrite(AsyncWrite *pWrite){
|
||||
/* We must hold the queue mutex in order to modify the queue pointers */
|
||||
pthread_mutex_lock(&async.queueMutex);
|
||||
if( pWrite->op!=ASYNC_UNLOCK ){
|
||||
pthread_mutex_lock(&async.queueMutex);
|
||||
}
|
||||
|
||||
/* Add the record to the end of the write-op queue */
|
||||
assert( !pWrite->pNext );
|
||||
|
@ -629,7 +630,9 @@ static void addAsyncWrite(AsyncWrite *pWrite){
|
|||
}
|
||||
|
||||
/* Drop the queue mutex */
|
||||
pthread_mutex_unlock(&async.queueMutex);
|
||||
if( pWrite->op!=ASYNC_UNLOCK ){
|
||||
pthread_mutex_unlock(&async.queueMutex);
|
||||
}
|
||||
|
||||
/* The writer thread might have been idle because there was nothing
|
||||
** on the write-op queue for it to do. So wake it up. */
|
||||
|
@ -639,7 +642,7 @@ static void addAsyncWrite(AsyncWrite *pWrite){
|
|||
/*
|
||||
** Increment async.nFile in a thread-safe manner.
|
||||
*/
|
||||
static void incrOpenFileCount(){
|
||||
static void incrOpenFileCount(void){
|
||||
/* We must hold the queue mutex in order to modify async.nFile */
|
||||
pthread_mutex_lock(&async.queueMutex);
|
||||
if( async.nFile==0 ){
|
||||
|
@ -701,7 +704,7 @@ static int asyncClose(sqlite3_file *pFile){
|
|||
p->lock.eLock = 0;
|
||||
pthread_mutex_unlock(&async.lockMutex);
|
||||
|
||||
addAsyncWrite(&p->close);
|
||||
addAsyncWrite(&p->closeOp);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -956,10 +959,12 @@ static int asyncUnlock(sqlite3_file *pFile, int eLock){
|
|||
AsyncFileData *p = ((AsyncFile *)pFile)->pData;
|
||||
if( p->zName ){
|
||||
AsyncFileLock *pLock = &p->lock;
|
||||
pthread_mutex_lock(&async.queueMutex);
|
||||
pthread_mutex_lock(&async.lockMutex);
|
||||
pLock->eLock = MIN(pLock->eLock, eLock);
|
||||
pthread_mutex_unlock(&async.lockMutex);
|
||||
rc = addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0);
|
||||
pthread_mutex_unlock(&async.lockMutex);
|
||||
pthread_mutex_unlock(&async.queueMutex);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -1041,6 +1046,25 @@ static int unlinkAsyncFile(AsyncFileData *pData){
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The parameter passed to this function is a copy of a 'flags' parameter
|
||||
** passed to this modules xOpen() method. This function returns true
|
||||
** if the file should be opened asynchronously, or false if it should
|
||||
** be opened immediately.
|
||||
**
|
||||
** If the file is to be opened asynchronously, then asyncOpen() will add
|
||||
** an entry to the event queue and the file will not actually be opened
|
||||
** until the event is processed. Otherwise, the file is opened directly
|
||||
** by the caller.
|
||||
*/
|
||||
static int doAsynchronousOpen(int flags){
|
||||
return (flags&SQLITE_OPEN_CREATE) && (
|
||||
(flags&SQLITE_OPEN_MAIN_JOURNAL) ||
|
||||
(flags&SQLITE_OPEN_TEMP_JOURNAL) ||
|
||||
(flags&SQLITE_OPEN_DELETEONCLOSE)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a file.
|
||||
*/
|
||||
|
@ -1075,7 +1099,7 @@ static int asyncOpen(
|
|||
AsyncFileData *pData;
|
||||
AsyncLock *pLock = 0;
|
||||
char *z;
|
||||
int isExclusive = (flags&SQLITE_OPEN_EXCLUSIVE);
|
||||
int isAsyncOpen = doAsynchronousOpen(flags);
|
||||
|
||||
/* If zName is NULL, then the upper layer is requesting an anonymous file */
|
||||
if( zName ){
|
||||
|
@ -1097,8 +1121,8 @@ static int asyncOpen(
|
|||
pData->pBaseRead = (sqlite3_file*)z;
|
||||
z += pVfs->szOsFile;
|
||||
pData->pBaseWrite = (sqlite3_file*)z;
|
||||
pData->close.pFileData = pData;
|
||||
pData->close.op = ASYNC_CLOSE;
|
||||
pData->closeOp.pFileData = pData;
|
||||
pData->closeOp.op = ASYNC_CLOSE;
|
||||
|
||||
if( zName ){
|
||||
z += pVfs->szOsFile;
|
||||
|
@ -1107,10 +1131,14 @@ static int asyncOpen(
|
|||
memcpy(pData->zName, zName, nName);
|
||||
}
|
||||
|
||||
if( !isExclusive ){
|
||||
rc = pVfs->xOpen(pVfs, zName, pData->pBaseRead, flags, pOutFlags);
|
||||
if( rc==SQLITE_OK && ((*pOutFlags)&SQLITE_OPEN_READWRITE) ){
|
||||
rc = pVfs->xOpen(pVfs, zName, pData->pBaseWrite, flags, 0);
|
||||
if( !isAsyncOpen ){
|
||||
int flagsout;
|
||||
rc = pVfs->xOpen(pVfs, pData->zName, pData->pBaseRead, flags, &flagsout);
|
||||
if( rc==SQLITE_OK && (flagsout&SQLITE_OPEN_READWRITE) ){
|
||||
rc = pVfs->xOpen(pVfs, pData->zName, pData->pBaseWrite, flags, 0);
|
||||
}
|
||||
if( pOutFlags ){
|
||||
*pOutFlags = flagsout;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1126,7 +1154,7 @@ static int asyncOpen(
|
|||
#ifdef ENABLE_FILE_LOCKING
|
||||
if( flags&SQLITE_OPEN_MAIN_DB ){
|
||||
pLock->pFile = (sqlite3_file *)&pLock[1];
|
||||
rc = pVfs->xOpen(pVfs, zName, pLock->pFile, flags, 0);
|
||||
rc = pVfs->xOpen(pVfs, pData->zName, pLock->pFile, flags, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(pLock);
|
||||
pLock = 0;
|
||||
|
@ -1175,7 +1203,7 @@ static int asyncOpen(
|
|||
pData->pLock = pLock;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && isExclusive ){
|
||||
if( rc==SQLITE_OK && isAsyncOpen ){
|
||||
rc = addNewAsyncWrite(pData, ASYNC_OPENEXCLUSIVE, (sqlite3_int64)flags,0,0);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pOutFlags ) *pOutFlags = flags;
|
||||
|
@ -1186,6 +1214,9 @@ static int asyncOpen(
|
|||
sqlite3_free(pData);
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
p->pMethod = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1259,40 +1290,27 @@ static int asyncFullPathname(
|
|||
** file-system uses unix style paths.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
int iIn;
|
||||
int iOut = 0;
|
||||
int nPathOut = strlen(zPathOut);
|
||||
|
||||
for(iIn=0; iIn<nPathOut; iIn++){
|
||||
|
||||
/* Replace any occurences of "//" with "/" */
|
||||
if( iIn<=(nPathOut-2) && zPathOut[iIn]=='/' && zPathOut[iIn+1]=='/'
|
||||
){
|
||||
continue;
|
||||
int i, j;
|
||||
int n = nPathOut;
|
||||
char *z = zPathOut;
|
||||
while( n>1 && z[n-1]=='/' ){ n--; }
|
||||
for(i=j=0; i<n; i++){
|
||||
if( z[i]=='/' ){
|
||||
if( z[i+1]=='/' ) continue;
|
||||
if( z[i+1]=='.' && i+2<n && z[i+2]=='/' ){
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if( z[i+1]=='.' && i+3<n && z[i+2]=='.' && z[i+3]=='/' ){
|
||||
while( j>0 && z[j-1]!='/' ){ j--; }
|
||||
if( j>0 ){ j--; }
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace any occurences of "/./" with "/" */
|
||||
if( iIn<=(nPathOut-3)
|
||||
&& zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.' && zPathOut[iIn+2]=='/'
|
||||
){
|
||||
iIn++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Replace any occurences of "<path-component>/../" with "" */
|
||||
if( iOut>0 && iIn<=(nPathOut-4)
|
||||
&& zPathOut[iIn]=='/' && zPathOut[iIn+1]=='.'
|
||||
&& zPathOut[iIn+2]=='.' && zPathOut[iIn+3]=='/'
|
||||
){
|
||||
iIn += 3;
|
||||
iOut--;
|
||||
for( ; iOut>0 && zPathOut[iOut-1]!='/'; iOut--);
|
||||
continue;
|
||||
}
|
||||
|
||||
zPathOut[iOut++] = zPathOut[iIn];
|
||||
z[j++] = z[i];
|
||||
}
|
||||
zPathOut[iOut] = '\0';
|
||||
z[j] = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -1305,11 +1323,11 @@ static void asyncDlError(sqlite3_vfs *pAsyncVfs, int nByte, char *zErrMsg){
|
|||
sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
|
||||
pVfs->xDlError(pVfs, nByte, zErrMsg);
|
||||
}
|
||||
static void *asyncDlSym(
|
||||
static void (*asyncDlSym(
|
||||
sqlite3_vfs *pAsyncVfs,
|
||||
void *pHandle,
|
||||
const char *zSymbol
|
||||
){
|
||||
))(void){
|
||||
sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
|
||||
return pVfs->xDlSym(pVfs, pHandle, zSymbol);
|
||||
}
|
||||
|
@ -1513,15 +1531,54 @@ static void *asyncWriterThread(void *pIsStarted){
|
|||
}
|
||||
|
||||
case ASYNC_UNLOCK: {
|
||||
AsyncWrite *pIter;
|
||||
AsyncFileData *pData = p->pFileData;
|
||||
int eLock = p->nByte;
|
||||
pthread_mutex_lock(&async.lockMutex);
|
||||
pData->lock.eAsyncLock = MIN(
|
||||
pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock)
|
||||
);
|
||||
assert(pData->lock.eAsyncLock>=pData->lock.eLock);
|
||||
rc = getFileLock(pData->pLock);
|
||||
pthread_mutex_unlock(&async.lockMutex);
|
||||
|
||||
/* When a file is locked by SQLite using the async backend, it is
|
||||
** locked within the 'real' file-system synchronously. When it is
|
||||
** unlocked, an ASYNC_UNLOCK event is added to the write-queue to
|
||||
** unlock the file asynchronously. The design of the async backend
|
||||
** requires that the 'real' file-system file be locked from the
|
||||
** time that SQLite first locks it (and probably reads from it)
|
||||
** until all asynchronous write events that were scheduled before
|
||||
** SQLite unlocked the file have been processed.
|
||||
**
|
||||
** This is more complex if SQLite locks and unlocks the file multiple
|
||||
** times in quick succession. For example, if SQLite does:
|
||||
**
|
||||
** lock, write, unlock, lock, write, unlock
|
||||
**
|
||||
** Each "lock" operation locks the file immediately. Each "write"
|
||||
** and "unlock" operation adds an event to the event queue. If the
|
||||
** second "lock" operation is performed before the first "unlock"
|
||||
** operation has been processed asynchronously, then the first
|
||||
** "unlock" cannot be safely processed as is, since this would mean
|
||||
** the file was unlocked when the second "write" operation is
|
||||
** processed. To work around this, when processing an ASYNC_UNLOCK
|
||||
** operation, SQLite:
|
||||
**
|
||||
** 1) Unlocks the file to the minimum of the argument passed to
|
||||
** the xUnlock() call and the current lock from SQLite's point
|
||||
** of view, and
|
||||
**
|
||||
** 2) Only unlocks the file at all if this event is the last
|
||||
** ASYNC_UNLOCK event on this file in the write-queue.
|
||||
*/
|
||||
assert( holdingMutex==1 );
|
||||
assert( async.pQueueFirst==p );
|
||||
for(pIter=async.pQueueFirst->pNext; pIter; pIter=pIter->pNext){
|
||||
if( pIter->pFileData==pData && pIter->op==ASYNC_UNLOCK ) break;
|
||||
}
|
||||
if( !pIter ){
|
||||
pthread_mutex_lock(&async.lockMutex);
|
||||
pData->lock.eAsyncLock = MIN(
|
||||
pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock)
|
||||
);
|
||||
assert(pData->lock.eAsyncLock>=pData->lock.eLock);
|
||||
rc = getFileLock(pData->pLock);
|
||||
pthread_mutex_unlock(&async.lockMutex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1564,12 +1621,12 @@ static void *asyncWriterThread(void *pIsStarted){
|
|||
}
|
||||
assert( holdingMutex );
|
||||
|
||||
/* An IO error has occured. We cannot report the error back to the
|
||||
/* An IO error has occurred. We cannot report the error back to the
|
||||
** connection that requested the I/O since the error happened
|
||||
** asynchronously. The connection has already moved on. There
|
||||
** really is nobody to report the error to.
|
||||
**
|
||||
** The file for which the error occured may have been a database or
|
||||
** The file for which the error occurred may have been a database or
|
||||
** journal file. Regardless, none of the currently queued operations
|
||||
** associated with the same database should now be performed. Nor should
|
||||
** any subsequently requested IO on either a database or journal file
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: test_backup.c,v 1.1 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** $Id: test_backup.c,v 1.3 2009/03/30 12:56:52 drh Exp $
|
||||
*/
|
||||
|
||||
#include "tcl.h"
|
||||
|
@ -61,12 +61,13 @@ static int backupTestCmd(
|
|||
switch( aSub[iCmd].eCmd ){
|
||||
|
||||
case BACKUP_FINISH: {
|
||||
const char *zCmdName;
|
||||
Tcl_CmdInfo cmdInfo;
|
||||
Tcl_Command cmd = Tcl_GetCommandFromObj(interp, objv[0]);
|
||||
Tcl_GetCommandInfoFromToken(cmd, &cmdInfo);
|
||||
zCmdName = Tcl_GetString(objv[0]);
|
||||
Tcl_GetCommandInfo(interp, zCmdName, &cmdInfo);
|
||||
cmdInfo.deleteProc = 0;
|
||||
Tcl_SetCommandInfoFromToken(cmd, &cmdInfo);
|
||||
Tcl_DeleteCommandFromToken(interp, cmd);
|
||||
Tcl_SetCommandInfo(interp, zCmdName, &cmdInfo);
|
||||
Tcl_DeleteCommand(interp, zCmdName);
|
||||
|
||||
rc = sqlite3_backup_finish(p);
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
|
||||
|
@ -145,4 +146,3 @@ int Sqlitetestbackup_Init(Tcl_Interp *interp){
|
|||
Tcl_CreateObjCommand(interp, "sqlite3_backup", backupTestInit, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
** The focus of this file is providing the TCL testing layer
|
||||
** access to compile-time constants.
|
||||
**
|
||||
** $Id: test_config.c,v 1.47 2009/01/12 14:01:45 danielk1977 Exp $
|
||||
** $Id: test_config.c,v 1.48 2009/03/16 13:19:36 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteLimit.h"
|
||||
|
@ -493,6 +493,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
|||
Tcl_SetVar2(interp, "sqlite_options", "update_delete_limit", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
|
||||
Tcl_SetVar2(interp, "sqlite_options", "unlock_notify", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "unlock_notify", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_SECURE_DELETE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
** Code for testing all sorts of SQLite interfaces. This code
|
||||
** implements new SQL functions used by the test scripts.
|
||||
**
|
||||
** $Id: test_func.c,v 1.13 2008/08/28 02:26:07 drh Exp $
|
||||
** $Id: test_func.c,v 1.14 2009/03/19 18:51:07 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include "tcl.h"
|
||||
|
@ -146,6 +146,25 @@ static void test_destructor_count(
|
|||
sqlite3_result_int(pCtx, test_destructor_count_var);
|
||||
}
|
||||
|
||||
/*
|
||||
** The following aggregate function, test_agg_errmsg16(), takes zero
|
||||
** arguments. It returns the text value returned by the sqlite3_errmsg16()
|
||||
** API function.
|
||||
*/
|
||||
void sqlite3BeginBenignMalloc(void);
|
||||
void sqlite3EndBenignMalloc(void);
|
||||
static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){
|
||||
}
|
||||
static void test_agg_errmsg16_final(sqlite3_context *ctx){
|
||||
const void *z;
|
||||
sqlite3 * db = sqlite3_context_db_handle(ctx);
|
||||
sqlite3_aggregate_context(ctx, 2048);
|
||||
sqlite3BeginBenignMalloc();
|
||||
z = sqlite3_errmsg16(db);
|
||||
sqlite3EndBenignMalloc();
|
||||
sqlite3_result_text16(ctx, z, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata()
|
||||
** interface.
|
||||
|
@ -318,6 +337,10 @@ static int registerTestFunctions(sqlite3 *db){
|
|||
sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg,
|
||||
aFuncs[i].eTextRep, 0, aFuncs[i].xFunc, 0, 0);
|
||||
}
|
||||
|
||||
sqlite3_create_function(db, "test_agg_errmsg16", 0, SQLITE_ANY, 0, 0,
|
||||
test_agg_errmsg16_step, test_agg_errmsg16_final);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
** correctly populates and syncs a journal file before writing to a
|
||||
** corresponding database file.
|
||||
**
|
||||
** $Id: test_journal.c,v 1.11 2009/02/12 09:11:56 danielk1977 Exp $
|
||||
** $Id: test_journal.c,v 1.15 2009/04/07 11:21:29 danielk1977 Exp $
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for testing only */
|
||||
|
||||
|
@ -50,7 +50,7 @@
|
|||
** c) The set of page numbers corresponding to free-list leaf pages.
|
||||
** d) A check-sum for every page in the database file.
|
||||
**
|
||||
** The start of a write-transaction is deemed to have occured when a
|
||||
** The start of a write-transaction is deemed to have occurred when a
|
||||
** 28-byte journal header is written to byte offset 0 of the journal
|
||||
** file.
|
||||
**
|
||||
|
@ -206,6 +206,17 @@ struct JtGlobal {
|
|||
};
|
||||
static struct JtGlobal g = {0, 0};
|
||||
|
||||
/*
|
||||
** Functions to obtain and relinquish a mutex to protect g.pList. The
|
||||
** STATIC_PRNG mutex is reused, purely for the sake of convenience.
|
||||
*/
|
||||
static void enterJtMutex(void){
|
||||
sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
|
||||
}
|
||||
static void leaveJtMutex(void){
|
||||
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
|
||||
}
|
||||
|
||||
extern int sqlite3_io_error_pending;
|
||||
static void stop_ioerr_simulation(int *piSave){
|
||||
*piSave = sqlite3_io_error_pending;
|
||||
|
@ -236,11 +247,12 @@ static int jtClose(sqlite3_file *pFile){
|
|||
jt_file *p = (jt_file *)pFile;
|
||||
|
||||
closeTransaction(p);
|
||||
enterJtMutex();
|
||||
if( p->zName ){
|
||||
for(pp=&g.pList; *pp!=p; pp=&(*pp)->pNext);
|
||||
*pp = p->pNext;
|
||||
}
|
||||
|
||||
leaveJtMutex();
|
||||
return sqlite3OsClose(p->pReal);
|
||||
}
|
||||
|
||||
|
@ -257,7 +269,6 @@ static int jtRead(
|
|||
return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Parameter zJournal is the name of a journal file that is currently
|
||||
** open. This function locates and returns the handle opened on the
|
||||
|
@ -274,6 +285,7 @@ static int jtRead(
|
|||
**/
|
||||
static jt_file *locateDatabaseHandle(const char *zJournal){
|
||||
jt_file *pMain = 0;
|
||||
enterJtMutex();
|
||||
for(pMain=g.pList; pMain; pMain=pMain->pNext){
|
||||
int nName = strlen(zJournal) - strlen("-journal");
|
||||
if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
|
||||
|
@ -284,6 +296,7 @@ static jt_file *locateDatabaseHandle(const char *zJournal){
|
|||
break;
|
||||
}
|
||||
}
|
||||
leaveJtMutex();
|
||||
return pMain;
|
||||
}
|
||||
|
||||
|
@ -656,6 +669,7 @@ static int jtOpen(
|
|||
){
|
||||
int rc;
|
||||
jt_file *p = (jt_file *)pFile;
|
||||
pFile->pMethods = 0;
|
||||
p->pReal = (sqlite3_file *)&p[1];
|
||||
p->pReal->pMethods = 0;
|
||||
rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
|
||||
|
@ -668,10 +682,12 @@ static int jtOpen(
|
|||
p->pNext = 0;
|
||||
p->pWritable = 0;
|
||||
p->aCksum = 0;
|
||||
enterJtMutex();
|
||||
if( zName ){
|
||||
p->pNext = g.pList;
|
||||
g.pList = p;
|
||||
}
|
||||
leaveJtMutex();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -798,7 +814,7 @@ int jt_register(char *zWrap, int isDefault){
|
|||
/*
|
||||
** Uninstall the jt VFS, if it is installed.
|
||||
*/
|
||||
void jt_unregister(){
|
||||
void jt_unregister(void){
|
||||
sqlite3_vfs_unregister(&jt_vfs);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
** This file contains code used to implement test interfaces to the
|
||||
** memory allocation subsystem.
|
||||
**
|
||||
** $Id: test_malloc.c,v 1.53 2009/02/04 15:27:40 danielk1977 Exp $
|
||||
** $Id: test_malloc.c,v 1.54 2009/04/07 11:21:29 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
|
@ -49,7 +49,7 @@ static void sqlite3Fault(void){
|
|||
** Check to see if a fault should be simulated. Return true to simulate
|
||||
** the fault. Return false if the fault should not be simulated.
|
||||
*/
|
||||
static int faultsimStep(){
|
||||
static int faultsimStep(void){
|
||||
if( likely(!memfault.enable) ){
|
||||
return 0;
|
||||
}
|
||||
|
@ -771,7 +771,7 @@ static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
|
|||
}
|
||||
#endif /* SQLITE_MEMDEBUG */
|
||||
|
||||
static void test_memdebug_log_clear(){
|
||||
static void test_memdebug_log_clear(void){
|
||||
Tcl_HashSearch search;
|
||||
Tcl_HashEntry *pEntry;
|
||||
for(
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: test_mutex.c,v 1.14 2009/02/11 05:18:07 danielk1977 Exp $
|
||||
** $Id: test_mutex.c,v 1.15 2009/03/20 13:15:30 drh Exp $
|
||||
*/
|
||||
|
||||
#include "tcl.h"
|
||||
|
@ -248,7 +248,7 @@ static int test_read_mutex_counters(
|
|||
int ii;
|
||||
char *aName[8] = {
|
||||
"fast", "recursive", "static_master", "static_mem",
|
||||
"static_mem2", "static_prng", "static_lru", "static_lru2"
|
||||
"static_open", "static_prng", "static_lru", "static_lru2"
|
||||
};
|
||||
|
||||
if( objc!=1 ){
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: test_onefile.c,v 1.11 2009/02/10 18:54:03 danielk1977 Exp $
|
||||
** $Id: test_onefile.c,v 1.12 2009/04/07 11:21:29 danielk1977 Exp $
|
||||
**
|
||||
** OVERVIEW:
|
||||
**
|
||||
|
@ -810,7 +810,7 @@ static int fsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|||
** true, the fs vfs becomes the new default vfs. It is the only publicly
|
||||
** available function in this file.
|
||||
*/
|
||||
int fs_register(){
|
||||
int fs_register(void){
|
||||
if( fs_vfs.pParent ) return SQLITE_OK;
|
||||
fs_vfs.pParent = sqlite3_vfs_find(0);
|
||||
fs_vfs.base.mxPathname = fs_vfs.pParent->mxPathname;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
** This pagecache implementation is designed for simplicity
|
||||
** not speed.
|
||||
**
|
||||
** $Id: test_pcache.c,v 1.2 2009/01/07 03:59:47 drh Exp $
|
||||
** $Id: test_pcache.c,v 1.3 2009/04/11 11:38:54 drh Exp $
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <string.h>
|
||||
|
@ -75,9 +75,19 @@ static void testpcacheShutdown(void *pArg){
|
|||
}
|
||||
|
||||
/*
|
||||
** Number of pages in a cache
|
||||
** Number of pages in a cache.
|
||||
**
|
||||
** The number of pages is a hard upper bound in this test module.
|
||||
** If more pages are requested, sqlite3PcacheFetch() returns NULL.
|
||||
**
|
||||
** If testing with in-memory temp tables, provide a larger pcache.
|
||||
** Some of the test cases need this.
|
||||
*/
|
||||
#define TESTPCACHE_NPAGE 217
|
||||
#if defined(SQLITE_TEMP_STORE) && SQLITE_TEMP_STORE>=2
|
||||
# define TESTPCACHE_NPAGE 499
|
||||
#else
|
||||
# define TESTPCACHE_NPAGE 217
|
||||
#endif
|
||||
#define TESTPCACHE_RESERVE 17
|
||||
|
||||
/*
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
** test that sqlite3 database handles may be concurrently accessed by
|
||||
** multiple threads. Right now this only works on unix.
|
||||
**
|
||||
** $Id: test_thread.c,v 1.10 2009/02/03 19:55:20 shane Exp $
|
||||
** $Id: test_thread.c,v 1.15 2009/03/27 12:32:56 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
@ -55,8 +55,19 @@ struct EvalEvent {
|
|||
|
||||
static Tcl_ObjCmdProc sqlthread_proc;
|
||||
static Tcl_ObjCmdProc clock_seconds_proc;
|
||||
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
|
||||
static Tcl_ObjCmdProc blocking_step_proc;
|
||||
static Tcl_ObjCmdProc blocking_prepare_v2_proc;
|
||||
#endif
|
||||
int Sqlitetest1_Init(Tcl_Interp *);
|
||||
|
||||
/* Functions from test1.c */
|
||||
void *sqlite3TestTextToPtr(const char *);
|
||||
const char *sqlite3TestErrorName(int);
|
||||
int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
|
||||
int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
|
||||
int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
|
||||
|
||||
/*
|
||||
** Handler for events of type EvalEvent.
|
||||
*/
|
||||
|
@ -106,6 +117,13 @@ static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
|
|||
interp = Tcl_CreateInterp();
|
||||
Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
|
||||
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
|
||||
Tcl_CreateObjCommand(interp,
|
||||
"sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
|
||||
Tcl_CreateObjCommand(interp,
|
||||
"sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
|
||||
#endif
|
||||
Sqlitetest1_Init(interp);
|
||||
Sqlitetest_mutex_Init(interp);
|
||||
|
||||
|
@ -359,12 +377,248 @@ static int clock_seconds_proc(
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** This block contains the implementation of the [sqlite3_blocking_step]
|
||||
** command available to threads created by [sqlthread spawn] commands. It
|
||||
** is only available on UNIX for now. This is because pthread condition
|
||||
** variables are used.
|
||||
**
|
||||
** The source code for the C functions sqlite3_blocking_step(),
|
||||
** blocking_step_notify() and the structure UnlockNotification is
|
||||
** automatically extracted from this file and used as part of the
|
||||
** documentation for the sqlite3_unlock_notify() API function. This
|
||||
** should be considered if these functions are to be extended (i.e. to
|
||||
** support windows) in the future.
|
||||
*/
|
||||
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
|
||||
|
||||
/* BEGIN_SQLITE_BLOCKING_STEP */
|
||||
/* This example uses the pthreads API */
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
** A pointer to an instance of this structure is passed as the user-context
|
||||
** pointer when registering for an unlock-notify callback.
|
||||
*/
|
||||
typedef struct UnlockNotification UnlockNotification;
|
||||
struct UnlockNotification {
|
||||
int fired; /* True after unlock event has occured */
|
||||
pthread_cond_t cond; /* Condition variable to wait on */
|
||||
pthread_mutex_t mutex; /* Mutex to protect structure */
|
||||
};
|
||||
|
||||
/*
|
||||
** This function is an unlock-notify callback registered with SQLite.
|
||||
*/
|
||||
static void unlock_notify_cb(void **apArg, int nArg){
|
||||
int i;
|
||||
for(i=0; i<nArg; i++){
|
||||
UnlockNotification *p = (UnlockNotification *)apArg[i];
|
||||
pthread_mutex_lock(&p->mutex);
|
||||
p->fired = 1;
|
||||
pthread_cond_signal(&p->cond);
|
||||
pthread_mutex_unlock(&p->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
|
||||
** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
|
||||
** associated database connection.
|
||||
**
|
||||
** This function calls sqlite3_unlock_notify() to register for an
|
||||
** unlock-notify callback, then blocks until that callback is delivered
|
||||
** and returns SQLITE_OK. The caller should then retry the failed operation.
|
||||
**
|
||||
** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
|
||||
** the system, then this function returns SQLITE_LOCKED immediately. In
|
||||
** this case the caller should not retry the operation and should roll
|
||||
** back the current transaction (if any).
|
||||
*/
|
||||
static int wait_for_unlock_notify(sqlite3 *db){
|
||||
int rc;
|
||||
UnlockNotification un;
|
||||
|
||||
/* Initialize the UnlockNotification structure. */
|
||||
un.fired = 0;
|
||||
pthread_mutex_init(&un.mutex, 0);
|
||||
pthread_cond_init(&un.cond, 0);
|
||||
|
||||
/* Register for an unlock-notify callback. */
|
||||
rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
|
||||
assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
|
||||
|
||||
/* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
|
||||
** or SQLITE_OK.
|
||||
**
|
||||
** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
|
||||
** case this function needs to return SQLITE_LOCKED to the caller so
|
||||
** that the current transaction can be rolled back. Otherwise, block
|
||||
** until the unlock-notify callback is invoked, then return SQLITE_OK.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
pthread_mutex_lock(&un.mutex);
|
||||
if( !un.fired ){
|
||||
pthread_cond_wait(&un.cond, &un.mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&un.mutex);
|
||||
}
|
||||
|
||||
/* Destroy the mutex and condition variables. */
|
||||
pthread_cond_destroy(&un.cond);
|
||||
pthread_mutex_destroy(&un.mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a wrapper around the SQLite function sqlite3_step().
|
||||
** It functions in the same way as step(), except that if a required
|
||||
** shared-cache lock cannot be obtained, this function may block waiting for
|
||||
** the lock to become available. In this scenario the normal API step()
|
||||
** function always returns SQLITE_LOCKED.
|
||||
**
|
||||
** If this function returns SQLITE_LOCKED, the caller should rollback
|
||||
** the current transaction (if any) and try again later. Otherwise, the
|
||||
** system may become deadlocked.
|
||||
*/
|
||||
int sqlite3_blocking_step(sqlite3_stmt *pStmt){
|
||||
int rc;
|
||||
while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
|
||||
rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
sqlite3_reset(pStmt);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
|
||||
** It functions in the same way as prepare_v2(), except that if a required
|
||||
** shared-cache lock cannot be obtained, this function may block waiting for
|
||||
** the lock to become available. In this scenario the normal API prepare_v2()
|
||||
** function always returns SQLITE_LOCKED.
|
||||
**
|
||||
** If this function returns SQLITE_LOCKED, the caller should rollback
|
||||
** the current transaction (if any) and try again later. Otherwise, the
|
||||
** system may become deadlocked.
|
||||
*/
|
||||
int sqlite3_blocking_prepare_v2(
|
||||
sqlite3 *db, /* Database handle. */
|
||||
const char *zSql, /* UTF-8 encoded SQL statement. */
|
||||
int nSql, /* Length of zSql in bytes. */
|
||||
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
|
||||
const char **pz /* OUT: End of parsed string */
|
||||
){
|
||||
int rc;
|
||||
while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
|
||||
rc = wait_for_unlock_notify(db);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
/* END_SQLITE_BLOCKING_STEP */
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_blocking_step STMT
|
||||
**
|
||||
** Advance the statement to the next row.
|
||||
*/
|
||||
static int blocking_step_proc(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "STMT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
rc = sqlite3_blocking_step(pStmt);
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
|
||||
** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
|
||||
*/
|
||||
static int blocking_prepare_v2_proc(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zSql;
|
||||
int bytes;
|
||||
const char *zTail = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char zBuf[50];
|
||||
int rc;
|
||||
int isBlocking = !(clientData==0);
|
||||
|
||||
if( objc!=5 && objc!=4 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
|
||||
|
||||
if( isBlocking ){
|
||||
rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
|
||||
}
|
||||
|
||||
assert(rc==SQLITE_OK || pStmt==0);
|
||||
if( zTail && objc>=5 ){
|
||||
if( bytes>=0 ){
|
||||
bytes = bytes - (zTail-zSql);
|
||||
}
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( pStmt==0 );
|
||||
sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc));
|
||||
Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( pStmt ){
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
|
||||
/*
|
||||
** End of implementation of [sqlite3_blocking_step].
|
||||
************************************************************************/
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int SqlitetestThread_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
|
||||
#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
|
||||
Tcl_CreateObjCommand(interp,
|
||||
"sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
|
||||
Tcl_CreateObjCommand(interp,
|
||||
"sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
** sqlite3_wsd_init() and sqlite3_wsd_find() functions required if the
|
||||
** SQLITE_OMIT_WSD symbol is defined at build time.
|
||||
**
|
||||
** $Id: test_wsd.c,v 1.3 2008/10/07 15:25:49 drh Exp $
|
||||
** $Id: test_wsd.c,v 1.4 2009/03/23 04:33:33 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
#if defined(SQLITE_OMIT_WSD) && defined(SQLITE_TEST)
|
||||
|
@ -69,7 +69,7 @@ void *sqlite3_wsd_find(void *K, int L){
|
|||
|
||||
/* If no entry for K was found, create and populate a new one. */
|
||||
if( !pVar ){
|
||||
int nByte = (sizeof(ProcessLocalVar) + L + 7)&~7;
|
||||
int nByte = ROUND8(sizeof(ProcessLocalVar) + L);
|
||||
assert( pGlobal->nFree>=nByte );
|
||||
pVar = (ProcessLocalVar *)pGlobal->pFree;
|
||||
pVar->pKey = K;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
**
|
||||
** $Id: tokenize.c,v 1.153 2009/01/20 16:53:41 danielk1977 Exp $
|
||||
** $Id: tokenize.c,v 1.155 2009/03/31 03:41:57 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -381,14 +381,17 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
|||
** error message.
|
||||
*/
|
||||
int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
int nErr = 0;
|
||||
int i;
|
||||
void *pEngine;
|
||||
int tokenType;
|
||||
int lastTokenParsed = -1;
|
||||
sqlite3 *db = pParse->db;
|
||||
int mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
|
||||
int nErr = 0; /* Number of errors encountered */
|
||||
int i; /* Loop counter */
|
||||
void *pEngine; /* The LEMON-generated LALR(1) parser */
|
||||
int tokenType; /* type of the next token */
|
||||
int lastTokenParsed = -1; /* type of the previous token */
|
||||
u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */
|
||||
sqlite3 *db = pParse->db; /* The database connection */
|
||||
int mxSqlLen; /* Max length of an SQL string */
|
||||
|
||||
|
||||
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
|
||||
if( db->activeVdbeCnt==0 ){
|
||||
db->u1.isInterrupted = 0;
|
||||
}
|
||||
|
@ -408,6 +411,8 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
|||
assert( pParse->nVarExpr==0 );
|
||||
assert( pParse->nVarExprAlloc==0 );
|
||||
assert( pParse->apVarExpr==0 );
|
||||
enableLookaside = db->lookaside.bEnabled;
|
||||
if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
|
||||
while( !db->mallocFailed && zSql[i]!=0 ){
|
||||
assert( i>=0 );
|
||||
pParse->sLastToken.z = (u8*)&zSql[i];
|
||||
|
@ -462,6 +467,7 @@ abort_parse:
|
|||
);
|
||||
#endif /* YYDEBUG */
|
||||
sqlite3ParserFree(pEngine, sqlite3_free);
|
||||
db->lookaside.bEnabled = enableLookaside;
|
||||
if( db->mallocFailed ){
|
||||
pParse->rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
|
150
src/trigger.c
150
src/trigger.c
|
@ -10,7 +10,7 @@
|
|||
*************************************************************************
|
||||
**
|
||||
**
|
||||
** $Id: trigger.c,v 1.133 2008/12/26 07:56:39 danielk1977 Exp $
|
||||
** $Id: trigger.c,v 1.135 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -33,6 +33,30 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Given table pTab, return a list of all the triggers attached to
|
||||
** the table. The list is connected by Trigger.pNext pointers.
|
||||
*/
|
||||
Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
|
||||
Schema * const pTmpSchema = pParse->db->aDb[1].pSchema;
|
||||
Trigger *pList = 0; /* List of triggers to return */
|
||||
|
||||
if( pTmpSchema!=pTab->pSchema ){
|
||||
HashElem *p;
|
||||
for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
|
||||
Trigger *pTrig = (Trigger *)sqliteHashData(p);
|
||||
if( pTrig->pTabSchema==pTab->pSchema
|
||||
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
|
||||
){
|
||||
pTrig->pNext = (pList ? pList : pTab->pTrigger);
|
||||
pList = pTrig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (pList ? pList : pTab->pTrigger);
|
||||
}
|
||||
|
||||
/*
|
||||
** This is called by the parser when it sees a CREATE TRIGGER statement
|
||||
** up to the point of the BEGIN before the trigger actions. A Trigger
|
||||
|
@ -182,7 +206,7 @@ void sqlite3BeginTrigger(
|
|||
pTrigger->pTabSchema = pTab->pSchema;
|
||||
pTrigger->op = (u8)op;
|
||||
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
|
||||
pTrigger->pWhen = sqlite3ExprDup(db, pWhen);
|
||||
pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
|
||||
pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
|
||||
sqlite3TokenCopy(db, &pTrigger->nameToken,pName);
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
|
@ -209,14 +233,16 @@ void sqlite3FinishTrigger(
|
|||
TriggerStep *pStepList, /* The triggered program */
|
||||
Token *pAll /* Token that describes the complete CREATE TRIGGER */
|
||||
){
|
||||
Trigger *pTrig = 0; /* The trigger whose construction is finishing up */
|
||||
sqlite3 *db = pParse->db; /* The database */
|
||||
Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */
|
||||
char *zName; /* Name of trigger */
|
||||
sqlite3 *db = pParse->db; /* The database */
|
||||
DbFixer sFix;
|
||||
int iDb; /* Database containing the trigger */
|
||||
int iDb; /* Database containing the trigger */
|
||||
|
||||
pTrig = pParse->pNewTrigger;
|
||||
pParse->pNewTrigger = 0;
|
||||
if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
|
||||
zName = pTrig->name;
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
|
||||
pTrig->step_list = pStepList;
|
||||
while( pStepList ){
|
||||
|
@ -242,32 +268,29 @@ void sqlite3FinishTrigger(
|
|||
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
|
||||
sqlite3NestedParse(pParse,
|
||||
"INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrig->name,
|
||||
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName,
|
||||
pTrig->table, z);
|
||||
sqlite3DbFree(db, z);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf(
|
||||
db, "type='trigger' AND name='%q'", pTrig->name), P4_DYNAMIC
|
||||
db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC
|
||||
);
|
||||
}
|
||||
|
||||
if( db->init.busy ){
|
||||
int n;
|
||||
Table *pTab;
|
||||
Trigger *pDel;
|
||||
pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash,
|
||||
pTrig->name, sqlite3Strlen30(pTrig->name), pTrig);
|
||||
if( pDel ){
|
||||
assert( pDel==pTrig );
|
||||
Trigger *pLink = pTrig;
|
||||
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
|
||||
pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig);
|
||||
if( pTrig ){
|
||||
db->mallocFailed = 1;
|
||||
goto triggerfinish_cleanup;
|
||||
}else if( pLink->pSchema==pLink->pTabSchema ){
|
||||
Table *pTab;
|
||||
int n = sqlite3Strlen30(pLink->table) + 1;
|
||||
pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table, n);
|
||||
assert( pTab!=0 );
|
||||
pLink->pNext = pTab->pTrigger;
|
||||
pTab->pTrigger = pLink;
|
||||
}
|
||||
n = sqlite3Strlen30(pTrig->table) + 1;
|
||||
pTab = sqlite3HashFind(&pTrig->pTabSchema->tblHash, pTrig->table, n);
|
||||
assert( pTab!=0 );
|
||||
pTrig->pNext = pTab->pTrigger;
|
||||
pTab->pTrigger = pTrig;
|
||||
pTrig = 0;
|
||||
}
|
||||
|
||||
triggerfinish_cleanup:
|
||||
|
@ -292,17 +315,17 @@ static void sqlitePersistTriggerStep(sqlite3 *db, TriggerStep *p){
|
|||
p->target.dyn = 1;
|
||||
}
|
||||
if( p->pSelect ){
|
||||
Select *pNew = sqlite3SelectDup(db, p->pSelect);
|
||||
Select *pNew = sqlite3SelectDup(db, p->pSelect, 1);
|
||||
sqlite3SelectDelete(db, p->pSelect);
|
||||
p->pSelect = pNew;
|
||||
}
|
||||
if( p->pWhere ){
|
||||
Expr *pNew = sqlite3ExprDup(db, p->pWhere);
|
||||
Expr *pNew = sqlite3ExprDup(db, p->pWhere, EXPRDUP_REDUCE);
|
||||
sqlite3ExprDelete(db, p->pWhere);
|
||||
p->pWhere = pNew;
|
||||
}
|
||||
if( p->pExprList ){
|
||||
ExprList *pNew = sqlite3ExprListDup(db, p->pExprList);
|
||||
ExprList *pNew = sqlite3ExprListDup(db, p->pExprList, 1);
|
||||
sqlite3ExprListDelete(db, p->pExprList);
|
||||
p->pExprList = pNew;
|
||||
}
|
||||
|
@ -546,6 +569,9 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
|||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0);
|
||||
if( pParse->nMem<3 ){
|
||||
pParse->nMem = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -553,25 +579,15 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
|
|||
** Remove a trigger from the hash tables of the sqlite* pointer.
|
||||
*/
|
||||
void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
|
||||
Hash *pHash = &(db->aDb[iDb].pSchema->trigHash);
|
||||
Trigger *pTrigger;
|
||||
int nName = sqlite3Strlen30(zName);
|
||||
pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash),
|
||||
zName, nName, 0);
|
||||
pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0);
|
||||
if( pTrigger ){
|
||||
Table *pTable = tableOfTrigger(pTrigger);
|
||||
assert( pTable!=0 );
|
||||
if( pTable->pTrigger == pTrigger ){
|
||||
pTable->pTrigger = pTrigger->pNext;
|
||||
}else{
|
||||
Trigger *cc = pTable->pTrigger;
|
||||
while( cc ){
|
||||
if( cc->pNext == pTrigger ){
|
||||
cc->pNext = cc->pNext->pNext;
|
||||
break;
|
||||
}
|
||||
cc = cc->pNext;
|
||||
}
|
||||
assert(cc);
|
||||
if( pTrigger->pSchema==pTrigger->pTabSchema ){
|
||||
Table *pTab = tableOfTrigger(pTrigger);
|
||||
Trigger **pp;
|
||||
for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
|
||||
*pp = (*pp)->pNext;
|
||||
}
|
||||
sqlite3DeleteTrigger(db, pTrigger);
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
|
@ -597,30 +613,31 @@ static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
|
|||
}
|
||||
|
||||
/*
|
||||
** Return a bit vector to indicate what kind of triggers exist for operation
|
||||
** "op" on table pTab. If pChanges is not NULL then it is a list of columns
|
||||
** that are being updated. Triggers only match if the ON clause of the
|
||||
** trigger definition overlaps the set of columns being updated.
|
||||
**
|
||||
** The returned bit vector is some combination of TRIGGER_BEFORE and
|
||||
** TRIGGER_AFTER.
|
||||
** Return a list of all triggers on table pTab if there exists at least
|
||||
** one trigger that must be fired when an operation of type 'op' is
|
||||
** performed on the table, and, if that operation is an UPDATE, if at
|
||||
** least one of the columns in pChanges is being modified.
|
||||
*/
|
||||
int sqlite3TriggersExist(
|
||||
Trigger *sqlite3TriggersExist(
|
||||
Parse *pParse, /* Parse context */
|
||||
Table *pTab, /* The table the contains the triggers */
|
||||
int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
|
||||
ExprList *pChanges /* Columns that change in an UPDATE statement */
|
||||
ExprList *pChanges, /* Columns that change in an UPDATE statement */
|
||||
int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
|
||||
){
|
||||
Trigger *pTrigger;
|
||||
int mask = 0;
|
||||
|
||||
pTrigger = IsVirtual(pTab) ? 0 : pTab->pTrigger;
|
||||
while( pTrigger ){
|
||||
if( pTrigger->op==op && checkColumnOverLap(pTrigger->pColumns, pChanges) ){
|
||||
mask |= pTrigger->tr_tm;
|
||||
Trigger *pList = sqlite3TriggerList(pParse, pTab);
|
||||
Trigger *p;
|
||||
assert( pList==0 || IsVirtual(pTab)==0 );
|
||||
for(p=pList; p; p=p->pNext){
|
||||
if( p->op==op && checkColumnOverLap(p->pColumns, pChanges) ){
|
||||
mask |= p->tr_tm;
|
||||
}
|
||||
pTrigger = pTrigger->pNext;
|
||||
}
|
||||
return mask;
|
||||
if( pMask ){
|
||||
*pMask = mask;
|
||||
}
|
||||
return (mask ? pList : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -677,7 +694,7 @@ static int codeTriggerProgram(
|
|||
pParse->trigStack->orconf = orconf;
|
||||
switch( pTriggerStep->op ){
|
||||
case TK_SELECT: {
|
||||
Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect);
|
||||
Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0);
|
||||
if( ss ){
|
||||
SelectDest dest;
|
||||
|
||||
|
@ -692,8 +709,8 @@ static int codeTriggerProgram(
|
|||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
|
||||
sqlite3Update(pParse, pSrc,
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList),
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere), orconf);
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -702,8 +719,8 @@ static int codeTriggerProgram(
|
|||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
|
||||
sqlite3Insert(pParse, pSrc,
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList),
|
||||
sqlite3SelectDup(db, pTriggerStep->pSelect),
|
||||
sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
|
||||
sqlite3SelectDup(db, pTriggerStep->pSelect, 0),
|
||||
sqlite3IdListDup(db, pTriggerStep->pIdList), orconf);
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
|
||||
break;
|
||||
|
@ -713,7 +730,7 @@ static int codeTriggerProgram(
|
|||
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqlite3DeleteFrom(pParse, pSrc,
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere));
|
||||
sqlite3ExprDup(db, pTriggerStep->pWhere, 0));
|
||||
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -757,6 +774,7 @@ static int codeTriggerProgram(
|
|||
*/
|
||||
int sqlite3CodeRowTrigger(
|
||||
Parse *pParse, /* Parse context */
|
||||
Trigger *pTrigger, /* List of triggers on table pTab */
|
||||
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
|
||||
ExprList *pChanges, /* Changes list for any UPDATE OF triggers */
|
||||
int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
|
||||
|
@ -780,7 +798,7 @@ int sqlite3CodeRowTrigger(
|
|||
|
||||
assert(newIdx != -1 || oldIdx != -1);
|
||||
|
||||
for(p=pTab->pTrigger; p; p=p->pNext){
|
||||
for(p=pTrigger; p; p=p->pNext){
|
||||
int fire_this = 0;
|
||||
|
||||
/* Determine whether we should code this trigger */
|
||||
|
@ -830,7 +848,7 @@ int sqlite3CodeRowTrigger(
|
|||
|
||||
/* code the WHEN clause */
|
||||
endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
|
||||
whenExpr = sqlite3ExprDup(db, p->pWhen);
|
||||
whenExpr = sqlite3ExprDup(db, p->pWhen, 0);
|
||||
if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){
|
||||
pParse->trigStack = trigStackEntry.pNext;
|
||||
sqlite3ExprDelete(db, whenExpr);
|
||||
|
|
42
src/update.c
42
src/update.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.191 2008/12/23 23:56:22 drh Exp $
|
||||
** $Id: update.c,v 1.196 2009/02/28 10:47:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -107,7 +107,7 @@ void sqlite3Update(
|
|||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* Trying to update a view */
|
||||
int triggers_exist = 0; /* True if any row triggers exist */
|
||||
Trigger *pTrigger; /* List of triggers on pTab, if required */
|
||||
#endif
|
||||
int iBeginAfterTrigger = 0; /* Address of after trigger program */
|
||||
int iEndAfterTrigger = 0; /* Exit of after trigger program */
|
||||
|
@ -143,10 +143,10 @@ void sqlite3Update(
|
|||
** updated is a view
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
triggers_exist = sqlite3TriggersExist(pTab, TK_UPDATE, pChanges);
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0);
|
||||
isView = pTab->pSelect!=0;
|
||||
#else
|
||||
# define triggers_exist 0
|
||||
# define pTrigger 0
|
||||
# define isView 0
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_VIEW
|
||||
|
@ -154,7 +154,7 @@ void sqlite3Update(
|
|||
# define isView 0
|
||||
#endif
|
||||
|
||||
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
|
||||
if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
||||
|
@ -167,7 +167,7 @@ void sqlite3Update(
|
|||
/* If there are FOR EACH ROW triggers, allocate cursors for the
|
||||
** special OLD and NEW tables
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
newIdx = pParse->nTab++;
|
||||
oldIdx = pParse->nTab++;
|
||||
}
|
||||
|
@ -299,27 +299,27 @@ void sqlite3Update(
|
|||
|
||||
/* Generate the code for triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
int iGoto;
|
||||
|
||||
/* Create pseudo-tables for NEW and OLD
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, oldIdx, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenPseudo, newIdx, 0);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
|
||||
|
||||
iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
addr = sqlite3VdbeMakeLabel(v);
|
||||
iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
|
||||
newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr,
|
||||
&old_col_mask, &new_col_mask) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
|
||||
if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab,
|
||||
newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
|
||||
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr,
|
||||
&old_col_mask, &new_col_mask) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
|
@ -399,7 +399,7 @@ void sqlite3Update(
|
|||
}
|
||||
|
||||
/* Jump back to this point if a trigger encounters an IGNORE constraint. */
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeResolveLabel(v, addr);
|
||||
}
|
||||
|
||||
|
@ -412,7 +412,7 @@ void sqlite3Update(
|
|||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
|
||||
}
|
||||
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
int regRowid;
|
||||
int regRow;
|
||||
int regCols;
|
||||
|
@ -541,7 +541,7 @@ void sqlite3Update(
|
|||
/* If there are triggers, close all the cursors after each iteration
|
||||
** through the loop. The fire the after triggers.
|
||||
*/
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
|
||||
sqlite3VdbeJumpHere(v, iEndAfterTrigger);
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ void sqlite3Update(
|
|||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
|
||||
if( triggers_exist ){
|
||||
if( pTrigger ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0);
|
||||
}
|
||||
|
@ -633,12 +633,12 @@ static void updateVirtualTable(
|
|||
sqlite3CreateIdExpr(pParse, "_rowid_"), 0);
|
||||
if( pRowid ){
|
||||
pEList = sqlite3ExprListAppend(pParse, pEList,
|
||||
sqlite3ExprDup(db, pRowid), 0);
|
||||
sqlite3ExprDup(db, pRowid, 0), 0);
|
||||
}
|
||||
assert( pTab->iPKey<0 );
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]>=0 ){
|
||||
pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr);
|
||||
pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
|
||||
}else{
|
||||
pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName);
|
||||
}
|
||||
|
|
58
src/utf.c
58
src/utf.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains routines used to translate between UTF-8,
|
||||
** UTF-16, UTF-16BE, and UTF-16LE.
|
||||
**
|
||||
** $Id: utf.c,v 1.70 2008/12/10 22:30:25 shane Exp $
|
||||
** $Id: utf.c,v 1.73 2009/04/01 18:40:32 drh Exp $
|
||||
**
|
||||
** Notes on UTF-8:
|
||||
**
|
||||
|
@ -110,22 +110,20 @@ static const unsigned char sqlite3Utf8Trans1[] = {
|
|||
#define READ_UTF16LE(zIn, c){ \
|
||||
c = (*zIn++); \
|
||||
c += ((*zIn++)<<8); \
|
||||
if( c>=0xD800 && c<0xE000 ){ \
|
||||
if( c>=0xD800 && c<0xE000 ){ \
|
||||
int c2 = (*zIn++); \
|
||||
c2 += ((*zIn++)<<8); \
|
||||
c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
||||
if( (c & 0xFFFF0000)==0 ) c = 0xFFFD; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define READ_UTF16BE(zIn, c){ \
|
||||
c = ((*zIn++)<<8); \
|
||||
c += (*zIn++); \
|
||||
if( c>=0xD800 && c<0xE000 ){ \
|
||||
if( c>=0xD800 && c<0xE000 ){ \
|
||||
int c2 = ((*zIn++)<<8); \
|
||||
c2 += (*zIn++); \
|
||||
c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
||||
if( (c & 0xFFFF0000)==0 ) c = 0xFFFD; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
@ -168,13 +166,25 @@ static const unsigned char sqlite3Utf8Trans1[] = {
|
|||
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
|
||||
}
|
||||
int sqlite3Utf8Read(
|
||||
const unsigned char *z, /* First byte of UTF-8 character */
|
||||
const unsigned char *zTerm, /* Pretend this byte is 0x00 */
|
||||
const unsigned char *zIn, /* First byte of UTF-8 character */
|
||||
const unsigned char **pzNext /* Write first byte past UTF-8 char here */
|
||||
){
|
||||
int c;
|
||||
READ_UTF8(z, zTerm, c);
|
||||
*pzNext = z;
|
||||
|
||||
/* Same as READ_UTF8() above but without the zTerm parameter.
|
||||
** For this routine, we assume the UTF8 string is always zero-terminated.
|
||||
*/
|
||||
c = *(zIn++);
|
||||
if( c>=0xc0 ){
|
||||
c = sqlite3Utf8Trans1[c-0xc0];
|
||||
while( (*zIn & 0xc0)==0x80 ){
|
||||
c = (c<<6) + (0x3f & *(zIn++));
|
||||
}
|
||||
if( c<0x80
|
||||
|| (c&0xFFFFF800)==0xD800
|
||||
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; }
|
||||
}
|
||||
*pzNext = zIn;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -341,7 +351,8 @@ int sqlite3VdbeMemHandleBom(Mem *pMem){
|
|||
int rc = SQLITE_OK;
|
||||
u8 bom = 0;
|
||||
|
||||
if( pMem->n<0 || pMem->n>1 ){
|
||||
assert( pMem->n>=0 );
|
||||
if( pMem->n>1 ){
|
||||
u8 b1 = *(u8 *)pMem->z;
|
||||
u8 b2 = *(((u8 *)pMem->z) + 1);
|
||||
if( b1==0xFE && b2==0xFF ){
|
||||
|
@ -407,17 +418,16 @@ int sqlite3Utf8CharLen(const char *zIn, int nByte){
|
|||
int sqlite3Utf8To8(unsigned char *zIn){
|
||||
unsigned char *zOut = zIn;
|
||||
unsigned char *zStart = zIn;
|
||||
unsigned char *zTerm = &zIn[sqlite3Strlen30((char *)zIn)];
|
||||
u32 c;
|
||||
|
||||
while( zIn[0] ){
|
||||
c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn);
|
||||
c = sqlite3Utf8Read(zIn, (const u8**)&zIn);
|
||||
if( c!=0xfffd ){
|
||||
WRITE_UTF8(zOut, c);
|
||||
}
|
||||
}
|
||||
*zOut = 0;
|
||||
return zOut - zStart;
|
||||
return (int)(zOut - zStart);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -445,15 +455,13 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte){
|
|||
}
|
||||
|
||||
/*
|
||||
** pZ is a UTF-16 encoded unicode string. If nChar is less than zero,
|
||||
** return the number of bytes up to (but not including), the first pair
|
||||
** of consecutive 0x00 bytes in pZ. If nChar is not less than zero,
|
||||
** then return the number of bytes in the first nChar unicode characters
|
||||
** in pZ (or up until the first pair of 0x00 bytes, whichever comes first).
|
||||
** pZ is a UTF-16 encoded unicode string at least nChar characters long.
|
||||
** Return the number of bytes in the first nChar unicode characters
|
||||
** in pZ. nChar must be non-negative.
|
||||
*/
|
||||
int sqlite3Utf16ByteLen(const void *zIn, int nChar){
|
||||
unsigned int c = 1;
|
||||
char const *z = zIn;
|
||||
int c;
|
||||
unsigned char const *z = zIn;
|
||||
int n = 0;
|
||||
if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){
|
||||
/* Using an "if (SQLITE_UTF16NATIVE==SQLITE_UTF16BE)" construct here
|
||||
|
@ -465,17 +473,17 @@ int sqlite3Utf16ByteLen(const void *zIn, int nChar){
|
|||
** which branch will be followed. It is therefore assumed that no runtime
|
||||
** penalty is paid for this "if" statement.
|
||||
*/
|
||||
while( c && ((nChar<0) || n<nChar) ){
|
||||
while( n<nChar ){
|
||||
READ_UTF16BE(z, c);
|
||||
n++;
|
||||
}
|
||||
}else{
|
||||
while( c && ((nChar<0) || n<nChar) ){
|
||||
while( n<nChar ){
|
||||
READ_UTF16LE(z, c);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return (int)(z-(char const *)zIn)-((c==0)?2:0);
|
||||
return (int)(z-(unsigned char const *)zIn);
|
||||
}
|
||||
|
||||
#if defined(SQLITE_TEST)
|
||||
|
@ -488,7 +496,6 @@ void sqlite3UtfSelfTest(void){
|
|||
unsigned int i, t;
|
||||
unsigned char zBuf[20];
|
||||
unsigned char *z;
|
||||
unsigned char *zTerm;
|
||||
int n;
|
||||
unsigned int c;
|
||||
|
||||
|
@ -498,9 +505,8 @@ void sqlite3UtfSelfTest(void){
|
|||
n = (int)(z-zBuf);
|
||||
assert( n>0 && n<=4 );
|
||||
z[0] = 0;
|
||||
zTerm = z;
|
||||
z = zBuf;
|
||||
c = sqlite3Utf8Read(z, zTerm, (const u8**)&z);
|
||||
c = sqlite3Utf8Read(z, (const u8**)&z);
|
||||
t = i;
|
||||
if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD;
|
||||
if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD;
|
||||
|
|
10
src/util.c
10
src/util.c
|
@ -14,7 +14,7 @@
|
|||
** This file contains functions for allocating memory, comparing
|
||||
** strings, and stuff like that.
|
||||
**
|
||||
** $Id: util.c,v 1.248 2009/02/04 03:59:25 shane Exp $
|
||||
** $Id: util.c,v 1.249 2009/03/01 22:29:20 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
|
@ -694,7 +694,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
|
|||
/* a: p2<<28 | p4<<14 | p6 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
a &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
a &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x7f<<14)|(0x7f);
|
||||
b = b<<7;
|
||||
a |= b;
|
||||
|
@ -711,7 +711,7 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
|
|||
/* b: p3<<28 | p5<<14 | p7 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
b &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
/* moved CSE2 up */
|
||||
/* a &= (0x7f<<14)|(0x7f); */
|
||||
a = a<<7;
|
||||
|
@ -806,8 +806,8 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
|||
/* a: p0<<28 | p2<<14 | p4 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
a &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x7f<<28)|(0x7f<<14)|(0x7f);
|
||||
a &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b &= (0x1f<<28)|(0x7f<<14)|(0x7f);
|
||||
b = b<<7;
|
||||
*v = a | b;
|
||||
return 5;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
** Most of the code in this file may be omitted by defining the
|
||||
** SQLITE_OMIT_VACUUM macro.
|
||||
**
|
||||
** $Id: vacuum.c,v 1.86 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** $Id: vacuum.c,v 1.87 2009/04/02 20:16:59 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
@ -140,8 +140,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
|||
}
|
||||
#endif
|
||||
|
||||
if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes)
|
||||
|| (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes))
|
||||
if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0)
|
||||
|| (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
|
||||
|| db->mallocFailed
|
||||
){
|
||||
rc = SQLITE_NOMEM;
|
||||
|
@ -268,7 +268,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
|||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes);
|
||||
rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
|
||||
}
|
||||
|
||||
end_of_vacuum:
|
||||
|
|
308
src/vdbe.c
308
src/vdbe.c
|
@ -43,7 +43,7 @@
|
|||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.817 2009/02/16 17:55:47 shane Exp $
|
||||
** $Id: vdbe.c,v 1.832 2009/04/10 12:55:17 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
@ -187,7 +187,7 @@ int sqlite3VdbeOpcodeHasProperty(int opcode, int mask){
|
|||
static VdbeCursor *allocateCursor(
|
||||
Vdbe *p, /* The virtual machine */
|
||||
int iCur, /* Index of the new VdbeCursor */
|
||||
Op *pOp, /* */
|
||||
int nField, /* Number of fields in the table or index */
|
||||
int iDb, /* When database the cursor belongs to, or -1 */
|
||||
int isBtreeCursor /* */
|
||||
){
|
||||
|
@ -213,15 +213,6 @@ static VdbeCursor *allocateCursor(
|
|||
|
||||
int nByte;
|
||||
VdbeCursor *pCx = 0;
|
||||
/* If the opcode of pOp is OP_SetNumColumns, then pOp->p2 contains
|
||||
** the number of fields in the records contained in the table or
|
||||
** index being opened. Use this to reserve space for the
|
||||
** VdbeCursor.aType[] array.
|
||||
*/
|
||||
int nField = 0;
|
||||
if( pOp->opcode==OP_SetNumColumns || pOp->opcode==OP_OpenEphemeral ){
|
||||
nField = pOp->p2;
|
||||
}
|
||||
nByte =
|
||||
sizeof(VdbeCursor) +
|
||||
(isBtreeCursor?sqlite3BtreeCursorSize():0) +
|
||||
|
@ -563,7 +554,7 @@ int sqlite3VdbeExec(
|
|||
Mem *pOut = 0; /* Output operand */
|
||||
u8 opProperty;
|
||||
int iCompare = 0; /* Result of last OP_Compare operation */
|
||||
int *aPermute = 0; /* Permuation of columns for OP_Compare */
|
||||
int *aPermute = 0; /* Permutation of columns for OP_Compare */
|
||||
#ifdef VDBE_PROFILE
|
||||
u64 start; /* CPU clock count at start of opcode */
|
||||
int origPc; /* Program counter at start of opcode */
|
||||
|
@ -571,11 +562,13 @@ int sqlite3VdbeExec(
|
|||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
int nProgressOps = 0; /* Opcodes executed since progress callback. */
|
||||
#endif
|
||||
UnpackedRecord aTempRec[16]; /* Space to hold a transient UnpackedRecord */
|
||||
|
||||
/* Temporary space into which to unpack a record. */
|
||||
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
|
||||
|
||||
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
|
||||
assert( db->magic==SQLITE_MAGIC_BUSY );
|
||||
sqlite3BtreeMutexArrayEnter(&p->aMutex);
|
||||
sqlite3VdbeMutexArrayEnter(p);
|
||||
if( p->rc==SQLITE_NOMEM ){
|
||||
/* This happens if a malloc() inside a call to sqlite3_column_text() or
|
||||
** sqlite3_column_text16() failed. */
|
||||
|
@ -819,6 +812,16 @@ case OP_Yield: { /* in1 */
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: HaltIfNull P1 P2 P3 P4 *
|
||||
**
|
||||
** Check the value in register P3. If is is NULL then Halt using
|
||||
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
|
||||
** value in register P3 is not NULL, then this routine is a no-op.
|
||||
*/
|
||||
case OP_HaltIfNull: { /* in3 */
|
||||
if( (pIn3->flags & MEM_Null)==0 ) break;
|
||||
/* Fall through into OP_Halt */
|
||||
}
|
||||
|
||||
/* Opcode: Halt P1 P2 * P4 *
|
||||
**
|
||||
|
@ -967,26 +970,34 @@ case OP_Blob: { /* out2-prerelease */
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Variable P1 P2 * * *
|
||||
/* Opcode: Variable P1 P2 P3 P4 *
|
||||
**
|
||||
** The value of variable P1 is written into register P2. A variable is
|
||||
** an unknown in the original SQL string as handed to sqlite3_compile().
|
||||
** Any occurrence of the '?' character in the original SQL is considered
|
||||
** a variable. Variables in the SQL string are number from left to
|
||||
** right beginning with 1. The values of variables are set using the
|
||||
** sqlite3_bind() API.
|
||||
** Transfer the values of bound parameters P1..P1+P3-1 into registers
|
||||
** P2..P2+P3-1.
|
||||
**
|
||||
** If the parameter is named, then its name appears in P4 and P3==1.
|
||||
** The P4 value is used by sqlite3_bind_parameter_name().
|
||||
*/
|
||||
case OP_Variable: { /* out2-prerelease */
|
||||
case OP_Variable: {
|
||||
int j = pOp->p1 - 1;
|
||||
int k = pOp->p2;
|
||||
Mem *pVar;
|
||||
assert( j>=0 && j<p->nVar );
|
||||
int n = pOp->p3;
|
||||
assert( j>=0 && j+n<=p->nVar );
|
||||
assert( k>=1 && k+n-1<=p->nMem );
|
||||
assert( pOp->p4.z==0 || pOp->p3==1 );
|
||||
|
||||
pVar = &p->aVar[j];
|
||||
if( sqlite3VdbeMemTooBig(pVar) ){
|
||||
goto too_big;
|
||||
while( n-- > 0 ){
|
||||
pVar = &p->aVar[j++];
|
||||
if( sqlite3VdbeMemTooBig(pVar) ){
|
||||
goto too_big;
|
||||
}
|
||||
pOut = &p->aMem[k++];
|
||||
sqlite3VdbeMemReleaseExternal(pOut);
|
||||
pOut->flags = MEM_Null;
|
||||
sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
|
||||
UPDATE_MAX_BLOBSIZE(pOut);
|
||||
}
|
||||
sqlite3VdbeMemShallowCopy(pOut, &p->aVar[j], MEM_Static);
|
||||
UPDATE_MAX_BLOBSIZE(pOut);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1002,15 +1013,14 @@ case OP_Move: {
|
|||
int n = pOp->p3;
|
||||
int p1 = pOp->p1;
|
||||
int p2 = pOp->p2;
|
||||
assert( n>0 );
|
||||
assert( p1>0 );
|
||||
assert( p1+n<p->nMem );
|
||||
pIn1 = &p->aMem[p1];
|
||||
assert( p2>0 );
|
||||
assert( p2+n<p->nMem );
|
||||
pOut = &p->aMem[p2];
|
||||
assert( n>0 && p1>0 && p2>0 );
|
||||
assert( p1+n<=p2 || p2+n<=p1 );
|
||||
|
||||
pIn1 = &p->aMem[p1];
|
||||
pOut = &p->aMem[p2];
|
||||
while( n-- ){
|
||||
assert( pOut<=&p->aMem[p->nMem] );
|
||||
assert( pIn1<=&p->aMem[p->nMem] );
|
||||
zMalloc = pOut->zMalloc;
|
||||
pOut->zMalloc = 0;
|
||||
sqlite3VdbeMemMove(pOut, pIn1);
|
||||
|
@ -1076,7 +1086,24 @@ case OP_ResultRow: {
|
|||
int i;
|
||||
assert( p->nResColumn==pOp->p2 );
|
||||
assert( pOp->p1>0 );
|
||||
assert( pOp->p1+pOp->p2<=p->nMem );
|
||||
assert( pOp->p1+pOp->p2<=p->nMem+1 );
|
||||
|
||||
/* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
|
||||
** DML statements invoke this opcode to return the number of rows
|
||||
** modified to the user. This is the only way that a VM that
|
||||
** opens a statement transaction may invoke this opcode.
|
||||
**
|
||||
** In case this is such a statement, close any statement transaction
|
||||
** opened by this VM before returning control to the user. This is to
|
||||
** ensure that statement-transactions are always nested, not overlapping.
|
||||
** If the open statement-transaction is not closed here, then the user
|
||||
** may step another VM that opens its own statement transaction. This
|
||||
** may lead to overlapping statement transactions.
|
||||
*/
|
||||
assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
|
||||
if( SQLITE_OK!=(rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE)) ){
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invalidate all ephemeral cursor row caches */
|
||||
p->cacheCtr = (p->cacheCtr + 2)|1;
|
||||
|
@ -1095,7 +1122,6 @@ case OP_ResultRow: {
|
|||
|
||||
/* Return SQLITE_ROW
|
||||
*/
|
||||
p->nCallback++;
|
||||
p->pc = pc + 1;
|
||||
rc = SQLITE_ROW;
|
||||
goto vdbe_return;
|
||||
|
@ -1300,7 +1326,7 @@ case OP_Function: {
|
|||
apVal = p->apArg;
|
||||
assert( apVal || n==0 );
|
||||
|
||||
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem) );
|
||||
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) );
|
||||
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
|
||||
pArg = &p->aMem[pOp->p2];
|
||||
for(i=0; i<n; i++, pArg++){
|
||||
|
@ -1701,7 +1727,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
|||
|
||||
/* Opcode: Permutation * * * P4 *
|
||||
**
|
||||
** Set the permuation used by the OP_Compare operator to be the array
|
||||
** Set the permutation used by the OP_Compare operator to be the array
|
||||
** of integers in P4.
|
||||
**
|
||||
** The permutation is only valid until the next OP_Permutation, OP_Compare,
|
||||
|
@ -1736,9 +1762,9 @@ case OP_Compare: {
|
|||
assert( n>0 );
|
||||
assert( pKeyInfo!=0 );
|
||||
p1 = pOp->p1;
|
||||
assert( p1>0 && p1+n-1<p->nMem );
|
||||
assert( p1>0 && p1+n<=p->nMem+1 );
|
||||
p2 = pOp->p2;
|
||||
assert( p2>0 && p2+n-1<p->nMem );
|
||||
assert( p2>0 && p2+n<=p->nMem+1 );
|
||||
for(i=0; i<n; i++){
|
||||
int idx = aPermute ? aPermute[i] : i;
|
||||
CollSeq *pColl; /* Collating sequence to use on this term */
|
||||
|
@ -1932,9 +1958,11 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
|
|||
** this opcode must be present immediately before the opcode that
|
||||
** opens the cursor.
|
||||
*/
|
||||
#if 0
|
||||
case OP_SetNumColumns: {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Opcode: Column P1 P2 P3 P4 *
|
||||
**
|
||||
|
@ -2253,7 +2281,7 @@ case OP_MakeRecord: {
|
|||
|
||||
nField = pOp->p1;
|
||||
zAffinity = pOp->p4.z;
|
||||
assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem );
|
||||
assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 );
|
||||
pData0 = &p->aMem[nField];
|
||||
nField = pOp->p2;
|
||||
pLast = &pData0[nField-1];
|
||||
|
@ -2330,6 +2358,26 @@ case OP_MakeRecord: {
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Count P1 P2 * * *
|
||||
**
|
||||
** Store the number of entries (an integer value) in the table or index
|
||||
** opened by cursor P1 in register P2
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
case OP_Count: { /* out2-prerelease */
|
||||
i64 nEntry;
|
||||
BtCursor *pCrsr = p->apCsr[pOp->p1]->pCursor;
|
||||
if( pCrsr ){
|
||||
rc = sqlite3BtreeCount(pCrsr, &nEntry);
|
||||
}else{
|
||||
nEntry = 0;
|
||||
}
|
||||
pOut->flags = MEM_Int;
|
||||
pOut->u.i = nEntry;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Opcode: Statement P1 * * * *
|
||||
**
|
||||
** Begin an individual statement transaction which is part of a larger
|
||||
|
@ -2361,10 +2409,12 @@ case OP_Statement: {
|
|||
pBt = db->aDb[i].pBt;
|
||||
assert( sqlite3BtreeIsInTrans(pBt) );
|
||||
assert( (p->btreeMask & (1<<i))!=0 );
|
||||
if( !sqlite3BtreeIsInStmt(pBt) ){
|
||||
rc = sqlite3BtreeBeginStmt(pBt);
|
||||
p->openedStatement = 1;
|
||||
if( p->iStatement==0 ){
|
||||
assert( db->nStatement>=0 && db->nSavepoint>=0 );
|
||||
db->nStatement++;
|
||||
p->iStatement = db->nSavepoint + db->nStatement;
|
||||
}
|
||||
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2471,7 +2521,7 @@ case OP_Savepoint: {
|
|||
rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
|
@ -2507,7 +2557,8 @@ case OP_Savepoint: {
|
|||
**
|
||||
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
|
||||
** back any currently active btree transactions. If there are any active
|
||||
** VMs (apart from this one), then the COMMIT or ROLLBACK statement fails.
|
||||
** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if
|
||||
** there are active writing VMs or active VMs that use shared cache.
|
||||
**
|
||||
** This instruction causes the VM to halt.
|
||||
*/
|
||||
|
@ -2550,6 +2601,7 @@ case OP_AutoCommit: {
|
|||
goto vdbe_return;
|
||||
}
|
||||
}
|
||||
assert( db->nStatement==0 );
|
||||
sqlite3CloseSavepoints(db);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
rc = SQLITE_DONE;
|
||||
|
@ -2767,9 +2819,11 @@ case OP_VerifyCookie: {
|
|||
** to get a read lock but fails, the script terminates with an
|
||||
** SQLITE_BUSY error code.
|
||||
**
|
||||
** The P4 value is a pointer to a KeyInfo structure that defines the
|
||||
** content and collating sequence of indices. P4 is NULL for cursors
|
||||
** that are not pointing to indices.
|
||||
** The P4 value may be either an integer (P4_INT32) or a pointer to
|
||||
** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
|
||||
** structure, then said structure defines the content and collating
|
||||
** sequence of the index being opened. Otherwise, if P4 is an integer
|
||||
** value, it is set to the number of columns in the table.
|
||||
**
|
||||
** See also OpenWrite.
|
||||
*/
|
||||
|
@ -2779,9 +2833,11 @@ case OP_VerifyCookie: {
|
|||
** page is P2. Or if P5!=0 use the content of register P2 to find the
|
||||
** root page.
|
||||
**
|
||||
** The P4 value is a pointer to a KeyInfo structure that defines the
|
||||
** content and collating sequence of indices. P4 is NULL for cursors
|
||||
** that are not pointing to indices.
|
||||
** The P4 value may be either an integer (P4_INT32) or a pointer to
|
||||
** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
|
||||
** structure, then said structure defines the content and collating
|
||||
** sequence of the index being opened. Otherwise, if P4 is an integer
|
||||
** value, it is set to the number of columns in the table.
|
||||
**
|
||||
** This instruction works just like OpenRead except that it opens the cursor
|
||||
** in read/write mode. For a given table, there can be one or more read-only
|
||||
|
@ -2791,6 +2847,8 @@ case OP_VerifyCookie: {
|
|||
*/
|
||||
case OP_OpenRead:
|
||||
case OP_OpenWrite: {
|
||||
int nField = 0;
|
||||
KeyInfo *pKeyInfo = 0;
|
||||
int i = pOp->p1;
|
||||
int p2 = pOp->p2;
|
||||
int iDb = pOp->p3;
|
||||
|
@ -2824,16 +2882,19 @@ case OP_OpenWrite: {
|
|||
}
|
||||
}
|
||||
assert( i>=0 );
|
||||
pCur = allocateCursor(p, i, &pOp[-1], iDb, 1);
|
||||
if( pOp->p4type==P4_KEYINFO ){
|
||||
pKeyInfo = pOp->p4.pKeyInfo;
|
||||
pKeyInfo->enc = ENC(p->db);
|
||||
nField = pKeyInfo->nField+1;
|
||||
}else if( pOp->p4type==P4_INT32 ){
|
||||
nField = pOp->p4.i;
|
||||
}
|
||||
pCur = allocateCursor(p, i, nField, iDb, 1);
|
||||
if( pCur==0 ) goto no_mem;
|
||||
pCur->nullRow = 1;
|
||||
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, pCur->pCursor);
|
||||
if( pOp->p4type==P4_KEYINFO ){
|
||||
pCur->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
pCur->pKeyInfo->enc = ENC(p->db);
|
||||
}else{
|
||||
pCur->pKeyInfo = 0;
|
||||
}
|
||||
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
|
||||
pCur->pKeyInfo = pKeyInfo;
|
||||
|
||||
switch( rc ){
|
||||
case SQLITE_BUSY: {
|
||||
p->pc = pc;
|
||||
|
@ -2908,7 +2969,7 @@ case OP_OpenEphemeral: {
|
|||
SQLITE_OPEN_TRANSIENT_DB;
|
||||
|
||||
assert( i>=0 );
|
||||
pCx = allocateCursor(p, i, pOp, -1, 1);
|
||||
pCx = allocateCursor(p, i, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags,
|
||||
|
@ -2943,7 +3004,7 @@ case OP_OpenEphemeral: {
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: OpenPseudo P1 P2 * * *
|
||||
/* Opcode: OpenPseudo P1 P2 P3 * *
|
||||
**
|
||||
** Open a new cursor that points to a fake table that contains a single
|
||||
** row of data. Any attempt to write a second row of data causes the
|
||||
|
@ -2962,12 +3023,15 @@ case OP_OpenEphemeral: {
|
|||
** is stored. In this case, the vdbe program must ensure that the
|
||||
** memory cell containing the row data is not overwritten until the
|
||||
** pseudo table is closed (or a new row is inserted into it).
|
||||
**
|
||||
** P3 is the number of fields in the records that will be stored by
|
||||
** the pseudo-table.
|
||||
*/
|
||||
case OP_OpenPseudo: {
|
||||
int i = pOp->p1;
|
||||
VdbeCursor *pCx;
|
||||
assert( i>=0 );
|
||||
pCx = allocateCursor(p, i, &pOp[-1], -1, 0);
|
||||
pCx = allocateCursor(p, i, pOp->p3, -1, 0);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
pCx->pseudoTable = 1;
|
||||
|
@ -3512,9 +3576,8 @@ case OP_NewRowid: { /* out2-prerelease */
|
|||
#endif
|
||||
|
||||
if( !pC->useRandomRowid ){
|
||||
if( pC->nextRowidValid ){
|
||||
v = pC->nextRowid;
|
||||
}else{
|
||||
v = sqlite3BtreeGetCachedRowid(pC->pCursor);
|
||||
if( v==0 ){
|
||||
rc = sqlite3BtreeLast(pC->pCursor, &res);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
|
@ -3551,12 +3614,7 @@ case OP_NewRowid: { /* out2-prerelease */
|
|||
}
|
||||
#endif
|
||||
|
||||
if( v<MAX_ROWID ){
|
||||
pC->nextRowidValid = 1;
|
||||
pC->nextRowid = v+1;
|
||||
}else{
|
||||
pC->nextRowidValid = 0;
|
||||
}
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0);
|
||||
}
|
||||
if( pC->useRandomRowid ){
|
||||
assert( pOp->p3==0 ); /* SQLITE_FULL must have occurred prior to this */
|
||||
|
@ -3634,9 +3692,6 @@ case OP_Insert: {
|
|||
iKey = intToKey(pKey->u.i);
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = pKey->u.i;
|
||||
if( pC->nextRowidValid && pKey->u.i>=pC->nextRowid ){
|
||||
pC->nextRowidValid = 0;
|
||||
}
|
||||
if( pData->flags & MEM_Null ){
|
||||
pData->z = 0;
|
||||
pData->n = 0;
|
||||
|
@ -3671,6 +3726,7 @@ case OP_Insert: {
|
|||
}else{
|
||||
nZero = 0;
|
||||
}
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
|
||||
rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
|
||||
pData->z, pData->n, nZero,
|
||||
pOp->p5 & OPFLAG_APPEND);
|
||||
|
@ -3733,8 +3789,8 @@ case OP_Delete: {
|
|||
|
||||
rc = sqlite3VdbeCursorMoveto(pC);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
|
||||
rc = sqlite3BtreeDelete(pC->pCursor);
|
||||
pC->nextRowidValid = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
|
||||
/* Invoke the update-hook if required. */
|
||||
|
@ -4066,7 +4122,7 @@ case OP_IdxDelete: {
|
|||
VdbeCursor *pC;
|
||||
BtCursor *pCrsr;
|
||||
assert( pOp->p3>0 );
|
||||
assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem );
|
||||
assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 );
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
assert( p->apCsr[i]!=0 );
|
||||
if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
|
||||
|
@ -4099,11 +4155,13 @@ case OP_IdxRowid: { /* out2-prerelease */
|
|||
BtCursor *pCrsr;
|
||||
VdbeCursor *pC;
|
||||
|
||||
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
assert( p->apCsr[i]!=0 );
|
||||
if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
|
||||
i64 rowid;
|
||||
|
||||
rc = sqlite3VdbeCursorMoveto(pC);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
assert( pC->deferredMoveto==0 );
|
||||
assert( pC->isTable==0 );
|
||||
if( !pC->nullRow ){
|
||||
|
@ -4242,7 +4300,7 @@ case OP_Destroy: { /* out2-prerelease */
|
|||
** P2==1 then the table to be clear is in the auxiliary database file
|
||||
** that is used to store tables create using CREATE TEMPORARY TABLE.
|
||||
**
|
||||
** If the P3 value is non-zero, then the table refered to must be an
|
||||
** If the P3 value is non-zero, then the table referred to must be an
|
||||
** intkey table (an SQL table, not an index). In this case the row change
|
||||
** count is incremented by the number of rows in the table being cleared.
|
||||
** If P3 is greater than zero, then the value stored in register P3 is
|
||||
|
@ -4321,33 +4379,58 @@ case OP_CreateTable: { /* out2-prerelease */
|
|||
** then runs the new virtual machine. It is thus a re-entrant opcode.
|
||||
*/
|
||||
case OP_ParseSchema: {
|
||||
char *zSql;
|
||||
int iDb = pOp->p1;
|
||||
const char *zMaster;
|
||||
InitData initData;
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
if( !pOp->p2 && !DbHasProperty(db, iDb, DB_SchemaLoaded) ){
|
||||
break;
|
||||
|
||||
/* If pOp->p2 is 0, then this opcode is being executed to read a
|
||||
** single row, for example the row corresponding to a new index
|
||||
** created by this VDBE, from the sqlite_master table. It only
|
||||
** does this if the corresponding in-memory schema is currently
|
||||
** loaded. Otherwise, the new index definition can be loaded along
|
||||
** with the rest of the schema when it is required.
|
||||
**
|
||||
** Although the mutex on the BtShared object that corresponds to
|
||||
** database iDb (the database containing the sqlite_master table
|
||||
** read by this instruction) is currently held, it is necessary to
|
||||
** obtain the mutexes on all attached databases before checking if
|
||||
** the schema of iDb is loaded. This is because, at the start of
|
||||
** the sqlite3_exec() call below, SQLite will invoke
|
||||
** sqlite3BtreeEnterAll(). If all mutexes are not already held, the
|
||||
** iDb mutex may be temporarily released to avoid deadlock. If
|
||||
** this happens, then some other thread may delete the in-memory
|
||||
** schema of database iDb before the SQL statement runs. The schema
|
||||
** will not be reloaded becuase the db->init.busy flag is set. This
|
||||
** can result in a "no such table: sqlite_master" or "malformed
|
||||
** database schema" error being returned to the user.
|
||||
*/
|
||||
assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
||||
sqlite3BtreeEnterAll(db);
|
||||
if( pOp->p2 || DbHasProperty(db, iDb, DB_SchemaLoaded) ){
|
||||
const char *zMaster = SCHEMA_TABLE(iDb);
|
||||
char *zSql;
|
||||
InitData initData;
|
||||
initData.db = db;
|
||||
initData.iDb = pOp->p1;
|
||||
initData.pzErrMsg = &p->zErrMsg;
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s",
|
||||
db->aDb[iDb].zName, zMaster, pOp->p4.z);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
(void)sqlite3SafetyOff(db);
|
||||
assert( db->init.busy==0 );
|
||||
db->init.busy = 1;
|
||||
initData.rc = SQLITE_OK;
|
||||
assert( !db->mallocFailed );
|
||||
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
|
||||
if( rc==SQLITE_OK ) rc = initData.rc;
|
||||
sqlite3DbFree(db, zSql);
|
||||
db->init.busy = 0;
|
||||
(void)sqlite3SafetyOn(db);
|
||||
}
|
||||
}
|
||||
zMaster = SCHEMA_TABLE(iDb);
|
||||
initData.db = db;
|
||||
initData.iDb = pOp->p1;
|
||||
initData.pzErrMsg = &p->zErrMsg;
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s",
|
||||
db->aDb[iDb].zName, zMaster, pOp->p4.z);
|
||||
if( zSql==0 ) goto no_mem;
|
||||
(void)sqlite3SafetyOff(db);
|
||||
assert( db->init.busy==0 );
|
||||
db->init.busy = 1;
|
||||
initData.rc = SQLITE_OK;
|
||||
assert( !db->mallocFailed );
|
||||
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
|
||||
if( rc==SQLITE_OK ) rc = initData.rc;
|
||||
sqlite3DbFree(db, zSql);
|
||||
db->init.busy = 0;
|
||||
(void)sqlite3SafetyOn(db);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
goto no_mem;
|
||||
}
|
||||
|
@ -4781,7 +4864,7 @@ case OP_TableLock: {
|
|||
assert( (p->btreeMask & (1<<p1))!=0 );
|
||||
assert( isWriteLock==0 || isWriteLock==1 );
|
||||
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
|
||||
if( rc==SQLITE_LOCKED ){
|
||||
if( (rc&0xFF)==SQLITE_LOCKED ){
|
||||
const char *z = pOp->p4.z;
|
||||
sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z);
|
||||
}
|
||||
|
@ -4796,8 +4879,8 @@ case OP_TableLock: {
|
|||
** xBegin method for that table.
|
||||
**
|
||||
** Also, whether or not P4 is set, check that this is not being called from
|
||||
** within a callback to a virtual table xSync() method. If it is, set the
|
||||
** error code to SQLITE_LOCKED.
|
||||
** within a callback to a virtual table xSync() method. If it is, the error
|
||||
** code will be set to SQLITE_LOCKED.
|
||||
*/
|
||||
case OP_VBegin: {
|
||||
sqlite3_vtab *pVtab = pOp->p4.pVtab;
|
||||
|
@ -4863,7 +4946,7 @@ case OP_VOpen: {
|
|||
pVtabCursor->pVtab = pVtab;
|
||||
|
||||
/* Initialise vdbe cursor object */
|
||||
pCur = allocateCursor(p, pOp->p1, &pOp[-1], -1, 0);
|
||||
pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
|
||||
if( pCur ){
|
||||
pCur->pVtabCursor = pVtabCursor;
|
||||
pCur->pModule = pVtabCursor->pVtab->pModule;
|
||||
|
@ -5023,7 +5106,7 @@ case OP_VColumn: {
|
|||
pVtab->zErrMsg = 0;
|
||||
|
||||
/* Copy the result of the function to the P3 register. We
|
||||
** do this regardless of whether or not an error occured to ensure any
|
||||
** do this regardless of whether or not an error occurred to ensure any
|
||||
** dynamic allocation in sContext.s (a Mem struct) is released.
|
||||
*/
|
||||
sqlite3VdbeChangeEncoding(&sContext.s, encoding);
|
||||
|
@ -5204,13 +5287,14 @@ case OP_Pagecount: { /* out2-prerelease */
|
|||
** the UTF-8 string contained in P4 is emitted on the trace callback.
|
||||
*/
|
||||
case OP_Trace: {
|
||||
if( pOp->p4.z ){
|
||||
char *zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
|
||||
if( zTrace ){
|
||||
if( db->xTrace ){
|
||||
db->xTrace(db->pTraceArg, pOp->p4.z);
|
||||
db->xTrace(db->pTraceArg, zTrace);
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( (db->flags & SQLITE_SqlTrace)!=0 ){
|
||||
sqlite3DebugPrintf("SQL-trace: %s\n", pOp->p4.z);
|
||||
sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** simple program to access and modify the underlying database.
|
||||
**
|
||||
** $Id: vdbe.h,v 1.139 2008/10/31 10:53:23 danielk1977 Exp $
|
||||
** $Id: vdbe.h,v 1.141 2009/04/10 00:56:29 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_VDBE_H_
|
||||
#define _SQLITE_VDBE_H_
|
||||
|
@ -181,14 +181,13 @@ void sqlite3VdbeSetNumCols(Vdbe*,int);
|
|||
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
|
||||
void sqlite3VdbeCountChanges(Vdbe*);
|
||||
sqlite3 *sqlite3VdbeDb(Vdbe*);
|
||||
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n);
|
||||
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
|
||||
void sqlite3VdbeSwap(Vdbe*,Vdbe*);
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
int sqlite3VdbeReleaseMemory(int);
|
||||
#endif
|
||||
UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,
|
||||
UnpackedRecord*,int);
|
||||
UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int);
|
||||
void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
|
||||
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
** 6000 lines long) it was split up into several smaller files and
|
||||
** this header information was factored out.
|
||||
**
|
||||
** $Id: vdbeInt.h,v 1.162 2009/02/03 15:39:01 drh Exp $
|
||||
** $Id: vdbeInt.h,v 1.167 2009/04/10 12:55:17 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _VDBEINT_H_
|
||||
#define _VDBEINT_H_
|
||||
|
@ -59,13 +59,11 @@ struct VdbeCursor {
|
|||
BtCursor *pCursor; /* The cursor structure of the backend */
|
||||
int iDb; /* Index of cursor database in db->aDb[] (or -1) */
|
||||
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
|
||||
i64 nextRowid; /* Next rowid returned by OP_NewRowid */
|
||||
Bool zeroed; /* True if zeroed out and ready for reuse */
|
||||
Bool rowidIsValid; /* True if lastRowid is valid */
|
||||
Bool atFirst; /* True if pointing to first entry */
|
||||
Bool useRandomRowid; /* Generate new record numbers semi-randomly */
|
||||
Bool nullRow; /* True if pointing to a row with no data */
|
||||
Bool nextRowidValid; /* True if the nextRowid field is valid */
|
||||
Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */
|
||||
Bool ephemPseudoTable;
|
||||
Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
|
||||
|
@ -279,16 +277,13 @@ struct Vdbe {
|
|||
u32 magic; /* Magic number for sanity checking */
|
||||
int nMem; /* Number of memory locations currently allocated */
|
||||
Mem *aMem; /* The memory locations */
|
||||
int nCallback; /* Number of callbacks invoked so far */
|
||||
int cacheCtr; /* VdbeCursor row cache generation counter */
|
||||
int contextStackTop; /* Index of top element in the context stack */
|
||||
int contextStackDepth; /* The size of the "context" stack */
|
||||
Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/
|
||||
int pc; /* The program counter */
|
||||
int rc; /* Value to return */
|
||||
unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */
|
||||
int errorAction; /* Recovery action to do in case of an error */
|
||||
int inTempTrans; /* True if temp database is transactioned */
|
||||
int nResColumn; /* Number of columns in one row of the result set */
|
||||
char **azResColumn; /* Values for one row of result */
|
||||
char *zErrMsg; /* Error message written here */
|
||||
|
@ -300,17 +295,18 @@ struct Vdbe {
|
|||
u8 inVtabMethod; /* See comments above */
|
||||
u8 usesStmtJournal; /* True if uses a statement journal */
|
||||
u8 readOnly; /* True for read-only statements */
|
||||
u8 isPrepareV2; /* True if prepared with prepare_v2() */
|
||||
int nChange; /* Number of db changes made since last reset */
|
||||
i64 startTime; /* Time when query started - used for profiling */
|
||||
int btreeMask; /* Bitmask of db->aDb[] entries referenced */
|
||||
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
|
||||
int aCounter[2]; /* Counters used by sqlite3_stmt_status() */
|
||||
int nSql; /* Number of bytes in zSql */
|
||||
char *zSql; /* Text of the SQL statement that generated this */
|
||||
void *pFree; /* Free this when deleting the vdbe */
|
||||
#ifdef SQLITE_DEBUG
|
||||
FILE *trace; /* Write an execution trace here, if not NULL */
|
||||
#endif
|
||||
int openedStatement; /* True if this VM has opened a statement journal */
|
||||
int iStatement; /* Statement number (or 0 if has not opened stmt) */
|
||||
#ifdef SQLITE_SSE
|
||||
int fetchId; /* Statement number used by sqlite3_fetch_statement */
|
||||
int lru; /* Counter used for LRU cache replacement */
|
||||
|
@ -378,10 +374,17 @@ int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
|||
const char *sqlite3OpcodeName(int);
|
||||
int sqlite3VdbeOpcodeHasProperty(int, int);
|
||||
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
|
||||
int sqlite3VdbeCloseStatement(Vdbe *, int);
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
int sqlite3VdbeReleaseBuffers(Vdbe *p);
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
void sqlite3VdbeMutexArrayEnter(Vdbe *p);
|
||||
#else
|
||||
# define sqlite3VdbeMutexArrayEnter(p)
|
||||
#endif
|
||||
|
||||
int sqlite3VdbeMemTranslate(Mem*, u8);
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3VdbePrintSql(Vdbe*);
|
||||
|
|
144
src/vdbeapi.c
144
src/vdbeapi.c
|
@ -13,7 +13,7 @@
|
|||
** This file contains code use to implement APIs that are part of the
|
||||
** VDBE.
|
||||
**
|
||||
** $Id: vdbeapi.c,v 1.151 2009/02/04 03:59:25 shane Exp $
|
||||
** $Id: vdbeapi.c,v 1.161 2009/04/10 23:11:31 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
@ -204,12 +204,14 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
|
|||
rc = SQLITE_OK;
|
||||
}else{
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
sqlite3 *db = v->db;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = v->db->mutex;
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
stmtLruRemove(v);
|
||||
rc = sqlite3VdbeFinalize(v);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(mutex);
|
||||
}
|
||||
return rc;
|
||||
|
@ -234,6 +236,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
|
|||
stmtLruAdd(v);
|
||||
sqlite3VdbeMakeReady(v, -1, 0, 0, 0);
|
||||
assert( (rc & (v->db->errMask))==rc );
|
||||
rc = sqlite3ApiExit(v->db, rc);
|
||||
sqlite3_mutex_leave(v->db->mutex);
|
||||
}
|
||||
return rc;
|
||||
|
@ -440,7 +443,7 @@ static int sqlite3Step(Vdbe *p){
|
|||
}
|
||||
|
||||
if( p->pc<=0 && p->expired ){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( ALWAYS(p->rc==SQLITE_OK) ){
|
||||
p->rc = SQLITE_SCHEMA;
|
||||
}
|
||||
rc = SQLITE_ERROR;
|
||||
|
@ -488,34 +491,41 @@ static int sqlite3Step(Vdbe *p){
|
|||
#ifndef SQLITE_OMIT_TRACE
|
||||
/* Invoke the profile callback if there is one
|
||||
*/
|
||||
if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->nOp>0
|
||||
&& p->aOp[0].opcode==OP_Trace && p->aOp[0].p4.z!=0 ){
|
||||
if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){
|
||||
double rNow;
|
||||
u64 elapseTime;
|
||||
|
||||
sqlite3OsCurrentTime(db->pVfs, &rNow);
|
||||
elapseTime = (u64)((rNow - (int)rNow)*3600.0*24.0*1000000000.0);
|
||||
elapseTime -= p->startTime;
|
||||
db->xProfile(db->pProfileArg, p->aOp[0].p4.z, elapseTime);
|
||||
db->xProfile(db->pProfileArg, p->zSql, elapseTime);
|
||||
}
|
||||
#endif
|
||||
|
||||
db->errCode = rc;
|
||||
/*sqlite3Error(p->db, rc, 0);*/
|
||||
p->rc = sqlite3ApiExit(p->db, p->rc);
|
||||
end_of_step:
|
||||
assert( (rc&0xff)==rc );
|
||||
if( p->zSql && (rc&0xff)<SQLITE_ROW ){
|
||||
/* This behavior occurs if sqlite3_prepare_v2() was used to build
|
||||
** the prepared statement. Return error codes directly */
|
||||
p->db->errCode = p->rc;
|
||||
/* sqlite3Error(p->db, p->rc, 0); */
|
||||
return p->rc;
|
||||
}else{
|
||||
/* This is for legacy sqlite3_prepare() builds and when the code
|
||||
** is SQLITE_ROW or SQLITE_DONE */
|
||||
return rc;
|
||||
if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
end_of_step:
|
||||
/* At this point local variable rc holds the value that should be
|
||||
** returned if this statement was compiled using the legacy
|
||||
** sqlite3_prepare() interface. According to the docs, this can only
|
||||
** be one of the values in the first assert() below. Variable p->rc
|
||||
** contains the value that would be returned if sqlite3_finalize()
|
||||
** were called on statement p.
|
||||
*/
|
||||
assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR
|
||||
|| rc==SQLITE_BUSY || rc==SQLITE_MISUSE
|
||||
);
|
||||
assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE );
|
||||
if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
|
||||
/* If this statement was prepared using sqlite3_prepare_v2(), and an
|
||||
** error has occured, then return the error code in p->rc to the
|
||||
** caller. Set the error code in the database handle to the same value.
|
||||
*/
|
||||
rc = db->errCode = p->rc;
|
||||
}
|
||||
return (rc&db->errMask);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -545,11 +555,11 @@ int sqlite3_step(sqlite3_stmt *pStmt){
|
|||
sqlite3_mutex_enter(db->mutex);
|
||||
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
|
||||
&& cnt++ < 5
|
||||
&& vdbeReprepare(v) ){
|
||||
&& (rc = vdbeReprepare(v))==SQLITE_OK ){
|
||||
sqlite3_reset(pStmt);
|
||||
v->expired = 0;
|
||||
}
|
||||
if( rc==SQLITE_SCHEMA && v->zSql && db->pErr ){
|
||||
if( rc==SQLITE_SCHEMA && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){
|
||||
/* This case occurs after failing to recompile an sql statement.
|
||||
** The error message from the SQL compiler has already been loaded
|
||||
** into the database handle. This block copies the error message
|
||||
|
@ -608,7 +618,7 @@ void sqlite3InvalidFunction(
|
|||
const char *zName = context->pFunc->zName;
|
||||
char *zErr;
|
||||
UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
||||
zErr = sqlite3MPrintf(0,
|
||||
zErr = sqlite3_mprintf(
|
||||
"unable to use function %s in the requested context", zName);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
|
@ -711,7 +721,7 @@ failed:
|
|||
** context.
|
||||
*/
|
||||
int sqlite3_aggregate_count(sqlite3_context *p){
|
||||
assert( p && p->pFunc && p->pFunc->xStep );
|
||||
assert( p && p->pMem && p->pFunc && p->pFunc->xStep );
|
||||
return p->pMem->n;
|
||||
}
|
||||
#endif
|
||||
|
@ -754,7 +764,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
|
|||
}else{
|
||||
/* ((double)0) In case of SQLITE_OMIT_FLOATING_POINT... */
|
||||
static const Mem nullMem = {{0}, (double)0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 };
|
||||
if( pVm->db ){
|
||||
if( pVm && ALWAYS(pVm->db) ){
|
||||
sqlite3_mutex_enter(pVm->db->mutex);
|
||||
sqlite3Error(pVm->db, SQLITE_RANGE, 0);
|
||||
}
|
||||
|
@ -894,24 +904,23 @@ static const void *columnName(
|
|||
const void *ret = 0;
|
||||
Vdbe *p = (Vdbe *)pStmt;
|
||||
int n;
|
||||
sqlite3 *db = p->db;
|
||||
|
||||
|
||||
if( p!=0 ){
|
||||
n = sqlite3_column_count(pStmt);
|
||||
if( N<n && N>=0 ){
|
||||
N += useType*n;
|
||||
sqlite3_mutex_enter(p->db->mutex);
|
||||
ret = xFunc(&p->aColName[N]);
|
||||
|
||||
/* A malloc may have failed inside of the xFunc() call. If this
|
||||
** is the case, clear the mallocFailed flag and return NULL.
|
||||
*/
|
||||
if( p->db && p->db->mallocFailed ){
|
||||
p->db->mallocFailed = 0;
|
||||
ret = 0;
|
||||
}
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
assert( db!=0 );
|
||||
n = sqlite3_column_count(pStmt);
|
||||
if( N<n && N>=0 ){
|
||||
N += useType*n;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( db->mallocFailed==0 );
|
||||
ret = xFunc(&p->aColName[N]);
|
||||
/* A malloc may have failed inside of the xFunc() call. If this
|
||||
** is the case, clear the mallocFailed flag and return NULL.
|
||||
*/
|
||||
if( db->mallocFailed ){
|
||||
db->mallocFailed = 0;
|
||||
ret = 0;
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1151,8 +1160,8 @@ int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
|
|||
rc = sqlite3VdbeChangeEncoding(&p->aVar[i-1], ENC(p->db));
|
||||
}
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
rc = sqlite3ApiExit(p->db, rc);
|
||||
}
|
||||
rc = sqlite3ApiExit(p->db, rc);
|
||||
return rc;
|
||||
}
|
||||
int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
|
||||
|
@ -1182,18 +1191,21 @@ int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
|
|||
*/
|
||||
static void createVarMap(Vdbe *p){
|
||||
if( !p->okVar ){
|
||||
int j;
|
||||
Op *pOp;
|
||||
sqlite3_mutex_enter(p->db->mutex);
|
||||
if( !p->okVar ){
|
||||
int j;
|
||||
Op *pOp;
|
||||
for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
|
||||
if( pOp->opcode==OP_Variable ){
|
||||
assert( pOp->p1>0 && pOp->p1<=p->nVar );
|
||||
p->azVar[pOp->p1-1] = pOp->p4.z;
|
||||
}
|
||||
/* The race condition here is harmless. If two threads call this
|
||||
** routine on the same Vdbe at the same time, they both might end
|
||||
** up initializing the Vdbe.azVar[] array. That is a little extra
|
||||
** work but it results in the same answer.
|
||||
*/
|
||||
for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
|
||||
if( pOp->opcode==OP_Variable ){
|
||||
assert( pOp->p1>0 && pOp->p1<=p->nVar );
|
||||
p->azVar[pOp->p1-1] = pOp->p4.z;
|
||||
}
|
||||
p->okVar = 1;
|
||||
}
|
||||
p->okVar = 1;
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
}
|
||||
}
|
||||
|
@ -1238,36 +1250,40 @@ int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){
|
|||
|
||||
/*
|
||||
** Transfer all bindings from the first statement over to the second.
|
||||
** If the two statements contain a different number of bindings, then
|
||||
** an SQLITE_ERROR is returned.
|
||||
*/
|
||||
int sqlite3TransferBindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){
|
||||
Vdbe *pFrom = (Vdbe*)pFromStmt;
|
||||
Vdbe *pTo = (Vdbe*)pToStmt;
|
||||
int i, rc = SQLITE_OK;
|
||||
if( (pFrom->magic!=VDBE_MAGIC_RUN && pFrom->magic!=VDBE_MAGIC_HALT)
|
||||
|| (pTo->magic!=VDBE_MAGIC_RUN && pTo->magic!=VDBE_MAGIC_HALT)
|
||||
|| pTo->db!=pFrom->db ){
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
if( pFrom->nVar!=pTo->nVar ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
int i;
|
||||
assert( pTo->db==pFrom->db );
|
||||
assert( pTo->nVar==pFrom->nVar );
|
||||
sqlite3_mutex_enter(pTo->db->mutex);
|
||||
for(i=0; rc==SQLITE_OK && i<pFrom->nVar; i++){
|
||||
for(i=0; i<pFrom->nVar; i++){
|
||||
sqlite3VdbeMemMove(&pTo->aVar[i], &pFrom->aVar[i]);
|
||||
}
|
||||
sqlite3_mutex_leave(pTo->db->mutex);
|
||||
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
|
||||
return rc;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
/*
|
||||
** Deprecated external interface. Internal/core SQLite code
|
||||
** should call sqlite3TransferBindings.
|
||||
**
|
||||
** Is is misuse to call this routine with statements from different
|
||||
** database connections. But as this is a deprecated interface, we
|
||||
** will not bother to check for that condition.
|
||||
**
|
||||
** If the two statements contain a different number of bindings, then
|
||||
** an SQLITE_ERROR is returned. Nothing else can go wrong, so otherwise
|
||||
** SQLITE_OK is returned.
|
||||
*/
|
||||
int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){
|
||||
Vdbe *pFrom = (Vdbe*)pFromStmt;
|
||||
Vdbe *pTo = (Vdbe*)pToStmt;
|
||||
if( pFrom->nVar!=pTo->nVar ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return sqlite3TransferBindings(pFromStmt, pToStmt);
|
||||
}
|
||||
#endif
|
||||
|
|
300
src/vdbeaux.c
300
src/vdbeaux.c
|
@ -14,7 +14,7 @@
|
|||
** to version 2.8.7, all this code was combined into the vdbe.c source file.
|
||||
** But that file was getting too big so this subroutines were split out.
|
||||
**
|
||||
** $Id: vdbeaux.c,v 1.435 2009/02/03 16:51:25 danielk1977 Exp $
|
||||
** $Id: vdbeaux.c,v 1.451 2009/04/10 15:42:36 shane Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
@ -52,17 +52,22 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
|
|||
/*
|
||||
** Remember the SQL string for a prepared statement.
|
||||
*/
|
||||
void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n){
|
||||
void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
|
||||
if( p==0 ) return;
|
||||
#ifdef SQLITE_OMIT_TRACE
|
||||
if( !isPrepareV2 ) return;
|
||||
#endif
|
||||
assert( p->zSql==0 );
|
||||
p->zSql = sqlite3DbStrNDup(p->db, z, n);
|
||||
p->isPrepareV2 = isPrepareV2 ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the SQL associated with a prepared statement
|
||||
*/
|
||||
const char *sqlite3_sql(sqlite3_stmt *pStmt){
|
||||
return ((Vdbe *)pStmt)->zSql;
|
||||
Vdbe *p = (Vdbe *)pStmt;
|
||||
return (p->isPrepareV2 ? p->zSql : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -71,7 +76,6 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt){
|
|||
void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
|
||||
Vdbe tmp, *pTmp;
|
||||
char *zTmp;
|
||||
int nTmp;
|
||||
tmp = *pA;
|
||||
*pA = *pB;
|
||||
*pB = tmp;
|
||||
|
@ -84,9 +88,7 @@ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
|
|||
zTmp = pA->zSql;
|
||||
pA->zSql = pB->zSql;
|
||||
pB->zSql = zTmp;
|
||||
nTmp = pA->nSql;
|
||||
pA->nSql = pB->nSql;
|
||||
pB->nSql = nTmp;
|
||||
pB->isPrepareV2 = pA->isPrepareV2;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
|
@ -112,7 +114,7 @@ static int growOpArray(Vdbe *p){
|
|||
int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
|
||||
pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
|
||||
if( pNew ){
|
||||
p->nOpAlloc = nNew;
|
||||
p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
|
||||
p->aOp = pNew;
|
||||
}
|
||||
return (pNew ? SQLITE_OK : SQLITE_NOMEM);
|
||||
|
@ -901,8 +903,8 @@ int sqlite3VdbeList(
|
|||
}
|
||||
|
||||
if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
|
||||
p->db->mallocFailed = 1;
|
||||
return SQLITE_NOMEM;
|
||||
assert( p->db->mallocFailed );
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
|
||||
z = displayP4(pOp, pMem->z, 32);
|
||||
|
@ -918,8 +920,8 @@ int sqlite3VdbeList(
|
|||
|
||||
if( p->explain==1 ){
|
||||
if( sqlite3VdbeMemGrow(pMem, 4, 0) ){
|
||||
p->db->mallocFailed = 1;
|
||||
return SQLITE_NOMEM;
|
||||
assert( p->db->mallocFailed );
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
|
||||
pMem->n = 2;
|
||||
|
@ -998,6 +1000,40 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
|
|||
}
|
||||
#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */
|
||||
|
||||
/*
|
||||
** Allocate space from a fixed size buffer. Make *pp point to the
|
||||
** allocated space. (Note: pp is a char* rather than a void** to
|
||||
** work around the pointer aliasing rules of C.) *pp should initially
|
||||
** be zero. If *pp is not zero, that means that the space has already
|
||||
** been allocated and this routine is a noop.
|
||||
**
|
||||
** nByte is the number of bytes of space needed.
|
||||
**
|
||||
** *ppFrom point to available space and pEnd points to the end of the
|
||||
** available space.
|
||||
**
|
||||
** *pnByte is a counter of the number of bytes of space that have failed
|
||||
** to allocate. If there is insufficient space in *ppFrom to satisfy the
|
||||
** request, then increment *pnByte by the amount of the request.
|
||||
*/
|
||||
static void allocSpace(
|
||||
char *pp, /* IN/OUT: Set *pp to point to allocated buffer */
|
||||
int nByte, /* Number of bytes to allocate */
|
||||
u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */
|
||||
u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */
|
||||
int *pnByte /* If allocation cannot be made, increment *pnByte */
|
||||
){
|
||||
assert( EIGHT_BYTE_ALIGNMENT(*ppFrom) );
|
||||
if( (*(void**)pp)==0 ){
|
||||
nByte = ROUND8(nByte);
|
||||
if( (pEnd - *ppFrom)>=nByte ){
|
||||
*(void**)pp = (void *)*ppFrom;
|
||||
*ppFrom += nByte;
|
||||
}else{
|
||||
*pnByte += nByte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare a virtual machine for execution. This involves things such
|
||||
|
@ -1007,6 +1043,14 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
|
|||
**
|
||||
** This is the only way to move a VDBE from VDBE_MAGIC_INIT to
|
||||
** VDBE_MAGIC_RUN.
|
||||
**
|
||||
** This function may be called more than once on a single virtual machine.
|
||||
** The first call is made while compiling the SQL statement. Subsequent
|
||||
** calls are made as part of the process of resetting a statement to be
|
||||
** re-executed (from a call to sqlite3_reset()). The nVar, nMem, nCursor
|
||||
** and isExplain parameters are only passed correct values the first time
|
||||
** the function is called. On subsequent calls, from sqlite3_reset(), nVar
|
||||
** is passed -1 and nMem, nCursor and isExplain are all passed zero.
|
||||
*/
|
||||
void sqlite3VdbeMakeReady(
|
||||
Vdbe *p, /* The VDBE */
|
||||
|
@ -1039,37 +1083,52 @@ void sqlite3VdbeMakeReady(
|
|||
*/
|
||||
nMem += nCursor;
|
||||
|
||||
/*
|
||||
** Allocation space for registers.
|
||||
/* Allocate space for memory registers, SQL variables, VDBE cursors and
|
||||
** an array to marshal SQL function arguments in. This is only done the
|
||||
** first time this function is called for a given VDBE, not when it is
|
||||
** being called from sqlite3_reset() to reset the virtual machine.
|
||||
*/
|
||||
if( p->aMem==0 ){
|
||||
if( nVar>=0 && !db->mallocFailed ){
|
||||
u8 *zCsr = (u8 *)&p->aOp[p->nOp];
|
||||
u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc];
|
||||
int nByte;
|
||||
int nArg; /* Maximum number of args passed to a user function. */
|
||||
resolveP2Values(p, &nArg);
|
||||
assert( nVar>=0 );
|
||||
if( isExplain && nMem<10 ){
|
||||
nMem = 10;
|
||||
}
|
||||
p->aMem = sqlite3DbMallocZero(db,
|
||||
nMem*sizeof(Mem) /* aMem */
|
||||
+ nVar*sizeof(Mem) /* aVar */
|
||||
+ nArg*sizeof(Mem*) /* apArg */
|
||||
+ nVar*sizeof(char*) /* azVar */
|
||||
+ nCursor*sizeof(VdbeCursor*)+1 /* apCsr */
|
||||
);
|
||||
if( !db->mallocFailed ){
|
||||
p->aMem--; /* aMem[] goes from 1..nMem */
|
||||
p->nMem = nMem; /* not from 0..nMem-1 */
|
||||
p->aVar = &p->aMem[nMem+1];
|
||||
zCsr += (zCsr - (u8*)0)&7;
|
||||
assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
|
||||
if( zEnd<zCsr ) zEnd = zCsr;
|
||||
|
||||
do {
|
||||
memset(zCsr, 0, zEnd-zCsr);
|
||||
nByte = 0;
|
||||
allocSpace((char*)&p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
|
||||
allocSpace((char*)&p->apCsr,
|
||||
nCursor*sizeof(VdbeCursor*), &zCsr, zEnd, &nByte
|
||||
);
|
||||
if( nByte ){
|
||||
p->pFree = sqlite3DbMallocRaw(db, nByte);
|
||||
}
|
||||
zCsr = p->pFree;
|
||||
zEnd = &zCsr[nByte];
|
||||
}while( nByte && !db->mallocFailed );
|
||||
|
||||
p->nCursor = nCursor;
|
||||
if( p->aVar ){
|
||||
p->nVar = nVar;
|
||||
p->okVar = 0;
|
||||
p->apArg = (Mem**)&p->aVar[nVar];
|
||||
p->azVar = (char**)&p->apArg[nArg];
|
||||
p->apCsr = (VdbeCursor**)&p->azVar[nVar];
|
||||
p->nCursor = nCursor;
|
||||
for(n=0; n<nVar; n++){
|
||||
p->aVar[n].flags = MEM_Null;
|
||||
p->aVar[n].db = db;
|
||||
}
|
||||
}
|
||||
if( p->aMem ){
|
||||
p->aMem--; /* aMem[] goes from 1..nMem */
|
||||
p->nMem = nMem; /* not from 0..nMem-1 */
|
||||
for(n=1; n<=nMem; n++){
|
||||
p->aMem[n].flags = MEM_Null;
|
||||
p->aMem[n].db = db;
|
||||
|
@ -1084,14 +1143,13 @@ void sqlite3VdbeMakeReady(
|
|||
|
||||
p->pc = -1;
|
||||
p->rc = SQLITE_OK;
|
||||
p->uniqueCnt = 0;
|
||||
p->errorAction = OE_Abort;
|
||||
p->explain |= isExplain;
|
||||
p->magic = VDBE_MAGIC_RUN;
|
||||
p->nChange = 0;
|
||||
p->cacheCtr = 1;
|
||||
p->minWriteFileFormat = 255;
|
||||
p->openedStatement = 0;
|
||||
p->iStatement = 0;
|
||||
#ifdef VDBE_PROFILE
|
||||
{
|
||||
int i;
|
||||
|
@ -1405,7 +1463,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
|
|||
** sqlite3BtreeCommitPhaseOne(), then there is a chance that the
|
||||
** master journal file will be orphaned. But we cannot delete it,
|
||||
** in case the master journal file name was written into the journal
|
||||
** file before the failure occured.
|
||||
** file before the failure occurred.
|
||||
*/
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
|
@ -1510,6 +1568,75 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If the Vdbe passed as the first argument opened a statement-transaction,
|
||||
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
|
||||
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
|
||||
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
|
||||
** statement transaction is commtted.
|
||||
**
|
||||
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
|
||||
** Otherwise SQLITE_OK.
|
||||
*/
|
||||
int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
|
||||
sqlite3 *const db = p->db;
|
||||
int rc = SQLITE_OK;
|
||||
if( p->iStatement && db->nStatement ){
|
||||
int i;
|
||||
const int iSavepoint = p->iStatement-1;
|
||||
|
||||
assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE);
|
||||
assert( db->nStatement>0 );
|
||||
assert( p->iStatement==(db->nStatement+db->nSavepoint) );
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
int rc2 = SQLITE_OK;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
}
|
||||
if( rc2==SQLITE_OK ){
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
db->nStatement--;
|
||||
p->iStatement = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If SQLite is compiled to support shared-cache mode and to be threadsafe,
|
||||
** this routine obtains the mutex associated with each BtShared structure
|
||||
** that may be accessed by the VM passed as an argument. In doing so it
|
||||
** sets the BtShared.db member of each of the BtShared structures, ensuring
|
||||
** that the correct busy-handler callback is invoked if required.
|
||||
**
|
||||
** If SQLite is not threadsafe but does support shared-cache mode, then
|
||||
** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables
|
||||
** of all of BtShared structures accessible via the database handle
|
||||
** associated with the VM. Of course only a subset of these structures
|
||||
** will be accessed by the VM, and we could use Vdbe.btreeMask to figure
|
||||
** that subset out, but there is no advantage to doing so.
|
||||
**
|
||||
** If SQLite is not threadsafe and does not support shared-cache mode, this
|
||||
** function is a no-op.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
void sqlite3VdbeMutexArrayEnter(Vdbe *p){
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3BtreeMutexArrayEnter(&p->aMutex);
|
||||
#else
|
||||
sqlite3BtreeEnterAll(p->db);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine is called the when a VDBE tries to halt. If the VDBE
|
||||
** has made changes and is in autocommit mode, then commit those
|
||||
|
@ -1524,10 +1651,8 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
|
|||
** means the close did not happen and needs to be repeated.
|
||||
*/
|
||||
int sqlite3VdbeHalt(Vdbe *p){
|
||||
int rc; /* Used to store transient return codes */
|
||||
sqlite3 *db = p->db;
|
||||
int i;
|
||||
int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */
|
||||
int isSpecialError; /* Set to true if SQLITE_NOMEM or IOERR */
|
||||
|
||||
/* This function contains the logic that determines if a statement or
|
||||
** transaction will be committed or rolled back as a result of the
|
||||
|
@ -1557,9 +1682,11 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
/* No commit or rollback needed if the program never started */
|
||||
if( p->pc>=0 ){
|
||||
int mrc; /* Primary error code from p->rc */
|
||||
int eStatementOp = 0;
|
||||
int isSpecialError; /* Set to true if a 'special' error */
|
||||
|
||||
/* Lock all btrees used by the statement */
|
||||
sqlite3BtreeMutexArrayEnter(&p->aMutex);
|
||||
sqlite3VdbeMutexArrayEnter(p);
|
||||
|
||||
/* Check for one of the special errors */
|
||||
mrc = p->rc & 0xff;
|
||||
|
@ -1571,11 +1698,11 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
*/
|
||||
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
|
||||
if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
p->rc = SQLITE_BUSY;
|
||||
}else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL)
|
||||
&& p->usesStmtJournal ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
}else{
|
||||
/* We are forced to roll back the active transaction. Before doing
|
||||
** so, abort any other statements this handle currently has active.
|
||||
|
@ -1588,8 +1715,8 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
}
|
||||
}
|
||||
|
||||
/* If the auto-commit flag is set and this is the only active vdbe, then
|
||||
** we do either a commit or rollback of the current transaction.
|
||||
/* If the auto-commit flag is set and this is the only active writer
|
||||
** VM, then we do either a commit or rollback of the current transaction.
|
||||
**
|
||||
** Note: This block also runs if one of the special errors handled
|
||||
** above has occurred.
|
||||
|
@ -1604,7 +1731,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
** successful or hit an 'OR FAIL' constraint. This means a commit
|
||||
** is required.
|
||||
*/
|
||||
int rc = vdbeCommit(db, p);
|
||||
rc = vdbeCommit(db, p);
|
||||
if( rc==SQLITE_BUSY ){
|
||||
sqlite3BtreeMutexArrayLeave(&p->aMutex);
|
||||
return SQLITE_BUSY;
|
||||
|
@ -1617,13 +1744,12 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
}else{
|
||||
sqlite3RollbackAll(db);
|
||||
}
|
||||
}else if( !xFunc ){
|
||||
db->nStatement = 0;
|
||||
}else if( eStatementOp==0 ){
|
||||
if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
|
||||
if( p->openedStatement ){
|
||||
xFunc = sqlite3BtreeCommitStmt;
|
||||
}
|
||||
eStatementOp = SAVEPOINT_RELEASE;
|
||||
}else if( p->errorAction==OE_Abort ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
}else{
|
||||
invalidateCursorsOnModifiedBtrees(db);
|
||||
sqlite3RollbackAll(db);
|
||||
|
@ -1632,33 +1758,26 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
}
|
||||
}
|
||||
|
||||
/* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or
|
||||
** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs
|
||||
** and the return code is still SQLITE_OK, set the return code to the new
|
||||
** error value.
|
||||
/* If eStatementOp is non-zero, then a statement transaction needs to
|
||||
** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
|
||||
** do so. If this operation returns an error, and the current statement
|
||||
** error code is SQLITE_OK or SQLITE_CONSTRAINT, then set the error
|
||||
** code to the new value.
|
||||
*/
|
||||
assert(!xFunc ||
|
||||
xFunc==sqlite3BtreeCommitStmt ||
|
||||
xFunc==sqlite3BtreeRollbackStmt
|
||||
);
|
||||
for(i=0; xFunc && i<db->nDb; i++){
|
||||
int rc;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
rc = xFunc(pBt);
|
||||
if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
|
||||
p->rc = rc;
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = 0;
|
||||
}
|
||||
if( eStatementOp ){
|
||||
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
|
||||
if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
|
||||
p->rc = rc;
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this was an INSERT, UPDATE or DELETE and the statement was committed,
|
||||
** set the change counter.
|
||||
/* If this was an INSERT, UPDATE or DELETE and no statement transaction
|
||||
** has been rolled back, update the database connection change-counter.
|
||||
*/
|
||||
if( p->changeCntOn && p->pc>=0 ){
|
||||
if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
|
||||
if( eStatementOp!=SAVEPOINT_ROLLBACK ){
|
||||
sqlite3VdbeSetChanges(db, p->nChange);
|
||||
}else{
|
||||
sqlite3VdbeSetChanges(db, 0);
|
||||
|
@ -1690,6 +1809,15 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* If the auto-commit flag is set to true, then any locks that were held
|
||||
** by connection db have now been released. Call sqlite3ConnectionUnlocked()
|
||||
** to invoke any required unlock-notify callbacks.
|
||||
*/
|
||||
if( db->autoCommit ){
|
||||
sqlite3ConnectionUnlocked(db);
|
||||
}
|
||||
|
||||
assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -1847,17 +1975,15 @@ void sqlite3VdbeDelete(Vdbe *p){
|
|||
sqlite3DbFree(db, pOp->zComment);
|
||||
#endif
|
||||
}
|
||||
sqlite3DbFree(db, p->aOp);
|
||||
}
|
||||
releaseMemArray(p->aVar, p->nVar);
|
||||
sqlite3DbFree(db, p->aLabel);
|
||||
if( p->aMem ){
|
||||
sqlite3DbFree(db, &p->aMem[1]);
|
||||
}
|
||||
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
|
||||
sqlite3DbFree(db, p->aColName);
|
||||
sqlite3DbFree(db, p->zSql);
|
||||
p->magic = VDBE_MAGIC_DEAD;
|
||||
sqlite3DbFree(db, p->aOp);
|
||||
sqlite3DbFree(db, p->pFree);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
|
@ -2221,30 +2347,40 @@ UnpackedRecord *sqlite3VdbeRecordUnpack(
|
|||
KeyInfo *pKeyInfo, /* Information about the record format */
|
||||
int nKey, /* Size of the binary record */
|
||||
const void *pKey, /* The binary record */
|
||||
UnpackedRecord *pSpace,/* Space available to hold resulting object */
|
||||
char *pSpace, /* Unaligned space available to hold the object */
|
||||
int szSpace /* Size of pSpace[] in bytes */
|
||||
){
|
||||
const unsigned char *aKey = (const unsigned char *)pKey;
|
||||
UnpackedRecord *p;
|
||||
int nByte, d;
|
||||
UnpackedRecord *p; /* The unpacked record that we will return */
|
||||
int nByte; /* Memory space needed to hold p, in bytes */
|
||||
int d;
|
||||
u32 idx;
|
||||
u16 u; /* Unsigned loop counter */
|
||||
u16 u; /* Unsigned loop counter */
|
||||
u32 szHdr;
|
||||
Mem *pMem;
|
||||
int nOff; /* Increase pSpace by this much to 8-byte align it */
|
||||
|
||||
assert( sizeof(Mem)>sizeof(*p) );
|
||||
nByte = sizeof(Mem)*(pKeyInfo->nField+2);
|
||||
/*
|
||||
** We want to shift the pointer pSpace up such that it is 8-byte aligned.
|
||||
** Thus, we need to calculate a value, nOff, between 0 and 7, to shift
|
||||
** it by. If pSpace is already 8-byte aligned, nOff should be zero.
|
||||
*/
|
||||
nOff = (8 - (SQLITE_PTR_TO_INT(pSpace) & 7)) & 7;
|
||||
pSpace += nOff;
|
||||
szSpace -= nOff;
|
||||
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
|
||||
if( nByte>szSpace ){
|
||||
p = sqlite3DbMallocRaw(pKeyInfo->db, nByte);
|
||||
if( p==0 ) return 0;
|
||||
p->flags = UNPACKED_NEED_FREE | UNPACKED_NEED_DESTROY;
|
||||
}else{
|
||||
p = pSpace;
|
||||
p = (UnpackedRecord*)pSpace;
|
||||
p->flags = UNPACKED_NEED_DESTROY;
|
||||
}
|
||||
p->pKeyInfo = pKeyInfo;
|
||||
p->nField = pKeyInfo->nField + 1;
|
||||
p->aMem = pMem = &((Mem*)p)[1];
|
||||
p->aMem = pMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
idx = getVarint32(aKey, szHdr);
|
||||
d = szHdr;
|
||||
u = 0;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
**
|
||||
** This file contains code used to implement incremental BLOB I/O.
|
||||
**
|
||||
** $Id: vdbeblob.c,v 1.26 2008/10/02 14:49:02 danielk1977 Exp $
|
||||
** $Id: vdbeblob.c,v 1.31 2009/03/24 15:08:10 drh Exp $
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
@ -70,17 +70,15 @@ int sqlite3_blob_open(
|
|||
/* One of the following two instructions is replaced by an
|
||||
** OP_Noop before exection.
|
||||
*/
|
||||
{OP_SetNumColumns, 0, 0, 0}, /* 2: Num cols for cursor */
|
||||
{OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
|
||||
{OP_SetNumColumns, 0, 0, 0}, /* 4: Num cols for cursor */
|
||||
{OP_OpenWrite, 0, 0, 0}, /* 5: Open cursor 0 for read/write */
|
||||
{OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
|
||||
{OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
|
||||
|
||||
{OP_Variable, 1, 1, 0}, /* 6: Push the rowid to the stack */
|
||||
{OP_NotExists, 0, 10, 1}, /* 7: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 8 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 9 */
|
||||
{OP_Close, 0, 0, 0}, /* 10 */
|
||||
{OP_Halt, 0, 0, 0}, /* 11 */
|
||||
{OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
|
||||
{OP_NotExists, 0, 8, 1}, /* 5: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 6 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 7 */
|
||||
{OP_Close, 0, 0, 0}, /* 8 */
|
||||
{OP_Halt, 0, 0, 0}, /* 9 */
|
||||
};
|
||||
|
||||
Vdbe *v = 0;
|
||||
|
@ -178,19 +176,19 @@ int sqlite3_blob_open(
|
|||
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
||||
** parameter of the other to pTab->tnum.
|
||||
*/
|
||||
sqlite3VdbeChangeToNoop(v, (flags ? 3 : 5), 1);
|
||||
sqlite3VdbeChangeP2(v, (flags ? 5 : 3), pTab->tnum);
|
||||
sqlite3VdbeChangeP3(v, (flags ? 5 : 3), iDb);
|
||||
sqlite3VdbeChangeToNoop(v, (flags ? 2 : 3), 1);
|
||||
sqlite3VdbeChangeP2(v, (flags ? 3 : 2), pTab->tnum);
|
||||
sqlite3VdbeChangeP3(v, (flags ? 3 : 2), iDb);
|
||||
|
||||
/* Configure the OP_SetNumColumns. Configure the cursor to
|
||||
/* Configure the number of columns. Configure the cursor to
|
||||
** think that the table has one more column than it really
|
||||
** does. An OP_Column to retrieve this imaginary column will
|
||||
** always return an SQL NULL. This is useful because it means
|
||||
** we can invoke OP_Column to fill in the vdbe cursors type
|
||||
** and offset cache without causing any IO.
|
||||
*/
|
||||
sqlite3VdbeChangeP2(v, flags ? 4 : 2, pTab->nCol+1);
|
||||
sqlite3VdbeChangeP2(v, 8, pTab->nCol);
|
||||
sqlite3VdbeChangeP4(v, flags ? 3 : 2, SQLITE_INT_TO_PTR(pTab->nCol+1), P4_INT32);
|
||||
sqlite3VdbeChangeP2(v, 6, pTab->nCol);
|
||||
if( !db->mallocFailed ){
|
||||
sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
|
||||
}
|
||||
|
@ -250,8 +248,8 @@ int sqlite3_blob_open(
|
|||
|
||||
blob_open_out:
|
||||
zErr[sizeof(zErr)-1] = '\0';
|
||||
if( rc!=SQLITE_OK || db->mallocFailed ){
|
||||
sqlite3_finalize((sqlite3_stmt *)v);
|
||||
if( v && (rc!=SQLITE_OK || db->mallocFailed) ){
|
||||
sqlite3VdbeFinalize(v);
|
||||
}
|
||||
sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr));
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
|
@ -266,9 +264,13 @@ blob_open_out:
|
|||
int sqlite3_blob_close(sqlite3_blob *pBlob){
|
||||
Incrblob *p = (Incrblob *)pBlob;
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
|
||||
db = p->db;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
rc = sqlite3_finalize(p->pStmt);
|
||||
sqlite3DbFree(p->db, p);
|
||||
sqlite3DbFree(db, p);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
** only within the VDBE. Interface routines refer to a Mem using the
|
||||
** name sqlite_value
|
||||
**
|
||||
** $Id: vdbemem.c,v 1.137 2009/02/04 03:59:25 shane Exp $
|
||||
** $Id: vdbemem.c,v 1.140 2009/04/05 12:22:09 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
@ -209,6 +209,7 @@ int sqlite3VdbeMemStringify(Mem *pMem, int enc){
|
|||
assert( !(fg&(MEM_Str|MEM_Blob)) );
|
||||
assert( fg&(MEM_Int|MEM_Real) );
|
||||
assert( (pMem->flags&MEM_RowSet)==0 );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
|
||||
|
||||
if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){
|
||||
|
@ -321,6 +322,10 @@ static i64 doubleToInt64(double r){
|
|||
if( r<(double)minInt ){
|
||||
return minInt;
|
||||
}else if( r>(double)maxInt ){
|
||||
/* minInt is correct here - not maxInt. It turns out that assigning
|
||||
** a very large positive number to an integer results in a very large
|
||||
** negative integer. This makes no sense, but it is what x86 hardware
|
||||
** does so for compatibility we will do the same in software. */
|
||||
return minInt;
|
||||
}else{
|
||||
return (i64)r;
|
||||
|
@ -333,13 +338,15 @@ static i64 doubleToInt64(double r){
|
|||
** If pMem is an integer, then the value is exact. If pMem is
|
||||
** a floating-point then the value returned is the integer part.
|
||||
** If pMem is a string or blob, then we make an attempt to convert
|
||||
** it into a integer and return that. If pMem is NULL, return 0.
|
||||
** it into a integer and return that. If pMem represents an
|
||||
** an SQL-NULL value, return 0.
|
||||
**
|
||||
** If pMem is a string, its encoding might be changed.
|
||||
** If pMem represents a string value, its encoding might be changed.
|
||||
*/
|
||||
i64 sqlite3VdbeIntValue(Mem *pMem){
|
||||
int flags;
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
flags = pMem->flags;
|
||||
if( flags & MEM_Int ){
|
||||
return pMem->u.i;
|
||||
|
@ -368,6 +375,7 @@ i64 sqlite3VdbeIntValue(Mem *pMem){
|
|||
*/
|
||||
double sqlite3VdbeRealValue(Mem *pMem){
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
if( pMem->flags & MEM_Real ){
|
||||
return pMem->r;
|
||||
}else if( pMem->flags & MEM_Int ){
|
||||
|
@ -398,6 +406,7 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){
|
|||
assert( pMem->flags & MEM_Real );
|
||||
assert( (pMem->flags & MEM_RowSet)==0 );
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
|
||||
pMem->u.i = doubleToInt64(pMem->r);
|
||||
if( pMem->r==(double)pMem->u.i ){
|
||||
|
@ -411,6 +420,8 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){
|
|||
int sqlite3VdbeMemIntegerify(Mem *pMem){
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( (pMem->flags & MEM_RowSet)==0 );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
|
||||
pMem->u.i = sqlite3VdbeIntValue(pMem);
|
||||
MemSetTypeFlag(pMem, MEM_Int);
|
||||
return SQLITE_OK;
|
||||
|
@ -422,6 +433,8 @@ int sqlite3VdbeMemIntegerify(Mem *pMem){
|
|||
*/
|
||||
int sqlite3VdbeMemRealify(Mem *pMem){
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
|
||||
|
||||
pMem->r = sqlite3VdbeRealValue(pMem);
|
||||
MemSetTypeFlag(pMem, MEM_Real);
|
||||
return SQLITE_OK;
|
||||
|
|
17
src/vtab.c
17
src/vtab.c
|
@ -11,11 +11,16 @@
|
|||
*************************************************************************
|
||||
** This file contains code used to help implement virtual tables.
|
||||
**
|
||||
** $Id: vtab.c,v 1.81 2008/12/10 19:26:24 drh Exp $
|
||||
** $Id: vtab.c,v 1.85 2009/04/11 16:27:20 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** The actual function that does the work of creating a new module.
|
||||
** This function implements the sqlite3_create_module() and
|
||||
** sqlite3_create_module_v2() interfaces.
|
||||
*/
|
||||
static int createModule(
|
||||
sqlite3 *db, /* Database in which module is registered */
|
||||
const char *zName, /* Name assigned to this module */
|
||||
|
@ -97,6 +102,7 @@ void sqlite3VtabLock(sqlite3_vtab *pVtab){
|
|||
** disconnect the virtual table.
|
||||
*/
|
||||
void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
|
||||
assert( pVtab->nRef>0 );
|
||||
pVtab->nRef--;
|
||||
assert(db);
|
||||
assert( sqlite3SafetyCheckOk(db) );
|
||||
|
@ -118,7 +124,8 @@ void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){
|
|||
*/
|
||||
void sqlite3VtabClear(Table *p){
|
||||
sqlite3_vtab *pVtab = p->pVtab;
|
||||
sqlite3 *db = p->db;
|
||||
Schema *pSchema = p->pSchema;
|
||||
sqlite3 *db = pSchema ? pSchema->db : 0;
|
||||
if( pVtab ){
|
||||
assert( p->pMod && p->pMod->pModule );
|
||||
sqlite3VtabUnlock(db, pVtab);
|
||||
|
@ -571,7 +578,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
|||
}
|
||||
sParse.declareVtab = 0;
|
||||
|
||||
sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
|
||||
if( sParse.pVdbe ){
|
||||
sqlite3VdbeFinalize(sParse.pVdbe);
|
||||
}
|
||||
sqlite3DeleteTable(sParse.pNewTable);
|
||||
sParse.pNewTable = 0;
|
||||
|
||||
|
@ -711,7 +720,7 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){
|
|||
/* Special case: If db->aVTrans is NULL and db->nVTrans is greater
|
||||
** than zero, then this function is being called from within a
|
||||
** virtual module xSync() callback. It is illegal to write to
|
||||
** virtual module tables in this case, so return SQLITE_LOCKED.
|
||||
** virtual module tables in this case, so return SQLITE_MISUSE.
|
||||
*/
|
||||
if( sqlite3VtabInSync(db) ){
|
||||
return SQLITE_LOCKED;
|
||||
|
|
15
src/walker.c
15
src/walker.c
|
@ -12,7 +12,7 @@
|
|||
** This file contains routines used for walking the parser tree for
|
||||
** an SQL statement.
|
||||
**
|
||||
** $Id: walker.c,v 1.1 2008/08/20 16:35:10 drh Exp $
|
||||
** $Id: walker.c,v 1.4 2009/04/08 13:51:52 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
|
@ -41,13 +41,18 @@
|
|||
int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
|
||||
int rc;
|
||||
if( pExpr==0 ) return WRC_Continue;
|
||||
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
|
||||
testcase( ExprHasProperty(pExpr, EP_SpanToken) );
|
||||
testcase( ExprHasProperty(pExpr, EP_Reduced) );
|
||||
rc = pWalker->xExprCallback(pWalker, pExpr);
|
||||
if( rc==WRC_Continue ){
|
||||
if( rc==WRC_Continue
|
||||
&& !ExprHasAnyProperty(pExpr,EP_TokenOnly|EP_SpanToken) ){
|
||||
if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
|
||||
if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->pList) ) return WRC_Abort;
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->pSelect) ){
|
||||
return WRC_Abort;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
|
||||
}else{
|
||||
if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
|
||||
}
|
||||
}
|
||||
return rc & WRC_Abort;
|
||||
|
|
167
src/where.c
167
src/where.c
|
@ -16,7 +16,7 @@
|
|||
** so is applicable. Because this module is responsible for selecting
|
||||
** indices, you might also think of this module as the "query optimizer".
|
||||
**
|
||||
** $Id: where.c,v 1.368 2009/02/04 03:59:25 shane Exp $
|
||||
** $Id: where.c,v 1.382 2009/04/07 13:48:12 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
|||
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
|
||||
int sqlite3WhereTrace = 0;
|
||||
#endif
|
||||
#if 0
|
||||
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
||||
# define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X
|
||||
#else
|
||||
# define WHERETRACE(X)
|
||||
|
@ -431,8 +431,11 @@ static Bitmask exprTableUsage(WhereMaskSet *pMaskSet, Expr *p){
|
|||
}
|
||||
mask = exprTableUsage(pMaskSet, p->pRight);
|
||||
mask |= exprTableUsage(pMaskSet, p->pLeft);
|
||||
mask |= exprListTableUsage(pMaskSet, p->pList);
|
||||
mask |= exprSelectTableUsage(pMaskSet, p->pSelect);
|
||||
if( ExprHasProperty(p, EP_xIsSelect) ){
|
||||
mask |= exprSelectTableUsage(pMaskSet, p->x.pSelect);
|
||||
}else{
|
||||
mask |= exprListTableUsage(pMaskSet, p->x.pList);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){
|
||||
|
@ -634,7 +637,7 @@ static int isLikeOrGlob(
|
|||
#ifdef SQLITE_EBCDIC
|
||||
if( *pnoCase ) return 0;
|
||||
#endif
|
||||
pList = pExpr->pList;
|
||||
pList = pExpr->x.pList;
|
||||
pRight = pList->a[0].pExpr;
|
||||
if( pRight->op!=TK_STRING ){
|
||||
return 0;
|
||||
|
@ -653,7 +656,7 @@ static int isLikeOrGlob(
|
|||
(pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){
|
||||
return 0;
|
||||
}
|
||||
sqlite3DequoteExpr(db, pRight);
|
||||
sqlite3DequoteExpr(pRight);
|
||||
z = (char *)pRight->token.z;
|
||||
cnt = 0;
|
||||
if( z ){
|
||||
|
@ -689,7 +692,7 @@ static int isMatchOfColumn(
|
|||
sqlite3StrNICmp((const char*)pExpr->token.z,"match",5)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
pList = pExpr->pList;
|
||||
pList = pExpr->x.pList;
|
||||
if( pList->nExpr!=2 ){
|
||||
return 0;
|
||||
}
|
||||
|
@ -953,17 +956,18 @@ static void exprAnalyzeOrTerm(
|
|||
assert( pOrTerm->eOperator==WO_EQ );
|
||||
assert( pOrTerm->leftCursor==iCursor );
|
||||
assert( pOrTerm->u.leftColumn==iColumn );
|
||||
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight);
|
||||
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
|
||||
pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup, 0);
|
||||
pLeft = pOrTerm->pExpr->pLeft;
|
||||
}
|
||||
assert( pLeft!=0 );
|
||||
pDup = sqlite3ExprDup(db, pLeft);
|
||||
pDup = sqlite3ExprDup(db, pLeft, 0);
|
||||
pNew = sqlite3Expr(db, TK_IN, pDup, 0, 0);
|
||||
if( pNew ){
|
||||
int idxNew;
|
||||
transferJoinMarkings(pNew, pExpr);
|
||||
pNew->pList = pList;
|
||||
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
|
||||
pNew->x.pList = pList;
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
|
@ -1026,8 +1030,11 @@ static void exprAnalyze(
|
|||
op = pExpr->op;
|
||||
if( op==TK_IN ){
|
||||
assert( pExpr->pRight==0 );
|
||||
pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->pList)
|
||||
| exprSelectTableUsage(pMaskSet, pExpr->pSelect);
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
pTerm->prereqRight = exprSelectTableUsage(pMaskSet, pExpr->x.pSelect);
|
||||
}else{
|
||||
pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->x.pList);
|
||||
}
|
||||
}else if( op==TK_ISNULL ){
|
||||
pTerm->prereqRight = 0;
|
||||
}else{
|
||||
|
@ -1057,7 +1064,7 @@ static void exprAnalyze(
|
|||
Expr *pDup;
|
||||
if( pTerm->leftCursor>=0 ){
|
||||
int idxNew;
|
||||
pDup = sqlite3ExprDup(db, pExpr);
|
||||
pDup = sqlite3ExprDup(db, pExpr, 0);
|
||||
if( db->mallocFailed ){
|
||||
sqlite3ExprDelete(db, pDup);
|
||||
return;
|
||||
|
@ -1100,7 +1107,7 @@ static void exprAnalyze(
|
|||
** BETWEEN term is skipped.
|
||||
*/
|
||||
else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){
|
||||
ExprList *pList = pExpr->pList;
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
int i;
|
||||
static const u8 ops[] = {TK_GE, TK_LE};
|
||||
assert( pList!=0 );
|
||||
|
@ -1108,8 +1115,8 @@ static void exprAnalyze(
|
|||
for(i=0; i<2; i++){
|
||||
Expr *pNewExpr;
|
||||
int idxNew;
|
||||
pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft),
|
||||
sqlite3ExprDup(db, pList->a[i].pExpr), 0);
|
||||
pNewExpr = sqlite3Expr(db, ops[i], sqlite3ExprDup(db, pExpr->pLeft, 0),
|
||||
sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0);
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
|
@ -1148,18 +1155,18 @@ static void exprAnalyze(
|
|||
Expr *pNewExpr1, *pNewExpr2;
|
||||
int idxNew1, idxNew2;
|
||||
|
||||
pLeft = pExpr->pList->a[1].pExpr;
|
||||
pRight = pExpr->pList->a[0].pExpr;
|
||||
pLeft = pExpr->x.pList->a[1].pExpr;
|
||||
pRight = pExpr->x.pList->a[0].pExpr;
|
||||
pStr1 = sqlite3PExpr(pParse, TK_STRING, 0, 0, 0);
|
||||
if( pStr1 ){
|
||||
sqlite3TokenCopy(db, &pStr1->token, &pRight->token);
|
||||
pStr1->token.n = nPattern;
|
||||
pStr1->flags = EP_Dequoted;
|
||||
}
|
||||
pStr2 = sqlite3ExprDup(db, pStr1);
|
||||
pStr2 = sqlite3ExprDup(db, pStr1, 0);
|
||||
if( !db->mallocFailed ){
|
||||
u8 c, *pC;
|
||||
assert( pStr2->token.dyn );
|
||||
/* assert( pStr2->token.dyn ); */
|
||||
pC = (u8*)&pStr2->token.z[nPattern-1];
|
||||
c = *pC;
|
||||
if( noCase ){
|
||||
|
@ -1168,11 +1175,11 @@ static void exprAnalyze(
|
|||
}
|
||||
*pC = c + 1;
|
||||
}
|
||||
pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprDup(db,pLeft), pStr1, 0);
|
||||
pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprDup(db,pLeft,0),pStr1,0);
|
||||
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew1==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew1);
|
||||
pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprDup(db,pLeft), pStr2, 0);
|
||||
pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprDup(db,pLeft,0),pStr2,0);
|
||||
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew2==0 );
|
||||
exprAnalyze(pSrc, pWC, idxNew2);
|
||||
|
@ -1198,13 +1205,13 @@ static void exprAnalyze(
|
|||
WhereTerm *pNewTerm;
|
||||
Bitmask prereqColumn, prereqExpr;
|
||||
|
||||
pRight = pExpr->pList->a[0].pExpr;
|
||||
pLeft = pExpr->pList->a[1].pExpr;
|
||||
pRight = pExpr->x.pList->a[0].pExpr;
|
||||
pLeft = pExpr->x.pList->a[1].pExpr;
|
||||
prereqExpr = exprTableUsage(pMaskSet, pRight);
|
||||
prereqColumn = exprTableUsage(pMaskSet, pLeft);
|
||||
if( (prereqExpr & prereqColumn)==0 ){
|
||||
Expr *pNewExpr;
|
||||
pNewExpr = sqlite3Expr(db, TK_MATCH, 0, sqlite3ExprDup(db, pRight), 0);
|
||||
pNewExpr = sqlite3Expr(db, TK_MATCH, 0, sqlite3ExprDup(db, pRight, 0), 0);
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
testcase( idxNew==0 );
|
||||
pNewTerm = &pWC->a[idxNew];
|
||||
|
@ -1708,15 +1715,15 @@ static double bestVirtualIndex(
|
|||
** * Whether or not there must be separate lookups in the
|
||||
** index and in the main table.
|
||||
**
|
||||
** If there was an INDEXED BY clause attached to the table in the SELECT
|
||||
** statement, then this function only considers plans using the
|
||||
** named index. If one cannot be found, then the returned cost is
|
||||
** SQLITE_BIG_DBL. If a plan can be found that uses the named index,
|
||||
** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in
|
||||
** the SQL statement, then this function only considers plans using the
|
||||
** named index. If no such plan is found, then the returned cost is
|
||||
** SQLITE_BIG_DBL. If a plan is found that uses the named index,
|
||||
** then the cost is calculated in the usual way.
|
||||
**
|
||||
** If a NOT INDEXED clause was attached to the table in the SELECT
|
||||
** statement, then no indexes are considered. However, the selected
|
||||
** plan may still take advantage of the tables built-in rowid
|
||||
** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table
|
||||
** in the SELECT statement, then no indexes are considered. However, the
|
||||
** selected plan may still take advantage of the tables built-in rowid
|
||||
** index.
|
||||
*/
|
||||
static void bestIndex(
|
||||
|
@ -1755,6 +1762,13 @@ static void bestIndex(
|
|||
if( pProbe==0 &&
|
||||
findTerm(pWC, iCur, -1, 0, WO_EQ|WO_IN|WO_LT|WO_LE|WO_GT|WO_GE,0)==0 &&
|
||||
(pOrderBy==0 || !sortableByRowid(iCur, pOrderBy, pWC->pMaskSet, &rev)) ){
|
||||
if( pParse->db->flags & SQLITE_ReverseOrder ){
|
||||
/* For application testing, randomly reverse the output order for
|
||||
** SELECT statements that omit the ORDER BY clause. This will help
|
||||
** to find cases where
|
||||
*/
|
||||
pCost->plan.wsFlags |= WHERE_REVERSE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
pCost->rCost = SQLITE_BIG_DBL;
|
||||
|
@ -1776,10 +1790,12 @@ static void bestIndex(
|
|||
pCost->rCost = 0;
|
||||
pCost->nRow = 1;
|
||||
return;
|
||||
}else if( (pExpr = pTerm->pExpr)->pList!=0 ){
|
||||
}else if( !ExprHasProperty((pExpr = pTerm->pExpr), EP_xIsSelect)
|
||||
&& pExpr->x.pList
|
||||
){
|
||||
/* Rowid IN (LIST): cost is NlogN where N is the number of list
|
||||
** elements. */
|
||||
pCost->rCost = pCost->nRow = pExpr->pList->nExpr;
|
||||
pCost->rCost = pCost->nRow = pExpr->x.pList->nExpr;
|
||||
pCost->rCost *= estLog(pCost->rCost);
|
||||
}else{
|
||||
/* Rowid IN (SELECT): cost is NlogN where N is the number of rows
|
||||
|
@ -1828,7 +1844,15 @@ static void bestIndex(
|
|||
cost += cost*estLog(cost);
|
||||
WHERETRACE(("... sorting increases cost to %.9g\n", cost));
|
||||
}
|
||||
}else if( pParse->db->flags & SQLITE_ReverseOrder ){
|
||||
/* For application testing, randomly reverse the output order for
|
||||
** SELECT statements that omit the ORDER BY clause. This will help
|
||||
** to find cases where
|
||||
*/
|
||||
wsFlags |= WHERE_REVERSE;
|
||||
}
|
||||
|
||||
/* Remember this case if it is the best so far */
|
||||
if( cost<pCost->rCost ){
|
||||
pCost->rCost = cost;
|
||||
pCost->nRow = nRow;
|
||||
|
@ -1909,12 +1933,18 @@ static void bestIndex(
|
|||
pProbe = pSrc->pIndex;
|
||||
}
|
||||
for(; pProbe; pProbe=(pSrc->pIndex ? 0 : pProbe->pNext)){
|
||||
double inMultiplier = 1;
|
||||
double inMultiplier = 1; /* Number of equality look-ups needed */
|
||||
int inMultIsEst = 0; /* True if inMultiplier is an estimate */
|
||||
|
||||
WHERETRACE(("... index %s:\n", pProbe->zName));
|
||||
|
||||
/* Count the number of columns in the index that are satisfied
|
||||
** by x=EXPR constraints or x IN (...) constraints.
|
||||
** by x=EXPR constraints or x IN (...) constraints. For a term
|
||||
** of the form x=EXPR we only have to do a single binary search.
|
||||
** But for x IN (...) we have to do a number of binary searched
|
||||
** equal to the number of entries on the RHS of the IN operator.
|
||||
** The inMultipler variable with try to estimate the number of
|
||||
** binary searches needed.
|
||||
*/
|
||||
wsFlags = 0;
|
||||
for(i=0; i<pProbe->nColumn; i++){
|
||||
|
@ -1925,23 +1955,33 @@ static void bestIndex(
|
|||
if( pTerm->eOperator & WO_IN ){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
wsFlags |= WHERE_COLUMN_IN;
|
||||
if( pExpr->pSelect!=0 ){
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
inMultiplier *= 25;
|
||||
}else if( pExpr->pList ){
|
||||
inMultiplier *= pExpr->pList->nExpr + 1;
|
||||
inMultIsEst = 1;
|
||||
}else if( pExpr->x.pList ){
|
||||
inMultiplier *= pExpr->x.pList->nExpr + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
nRow = pProbe->aiRowEst[i] * inMultiplier;
|
||||
cost = nRow * estLog(inMultiplier);
|
||||
/* If inMultiplier is an estimate and that estimate results in an
|
||||
** nRow it that is more than half number of rows in the table,
|
||||
** then reduce inMultipler */
|
||||
if( inMultIsEst && nRow*2 > pProbe->aiRowEst[0] ){
|
||||
nRow = pProbe->aiRowEst[0]/2;
|
||||
inMultiplier = nRow/pProbe->aiRowEst[i];
|
||||
}
|
||||
cost = nRow + inMultiplier*estLog(pProbe->aiRowEst[0]);
|
||||
nEq = i;
|
||||
if( pProbe->onError!=OE_None && (wsFlags & WHERE_COLUMN_IN)==0
|
||||
&& nEq==pProbe->nColumn ){
|
||||
wsFlags |= WHERE_UNIQUE;
|
||||
}
|
||||
WHERETRACE(("...... nEq=%d inMult=%.9g cost=%.9g\n",nEq,inMultiplier,cost));
|
||||
WHERETRACE(("...... nEq=%d inMult=%.9g nRow=%.9g cost=%.9g\n",
|
||||
nEq, inMultiplier, nRow, cost));
|
||||
|
||||
/* Look for range constraints
|
||||
/* Look for range constraints. Assume that each range constraint
|
||||
** makes the search space 1/3rd smaller.
|
||||
*/
|
||||
if( nEq<pProbe->nColumn ){
|
||||
int j = pProbe->aiColumn[nEq];
|
||||
|
@ -1958,7 +1998,8 @@ static void bestIndex(
|
|||
cost /= 3;
|
||||
nRow /= 3;
|
||||
}
|
||||
WHERETRACE(("...... range reduces cost to %.9g\n", cost));
|
||||
WHERETRACE(("...... range reduces nRow to %.9g and cost to %.9g\n",
|
||||
nRow, cost));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1978,6 +2019,12 @@ static void bestIndex(
|
|||
cost += cost*estLog(cost);
|
||||
WHERETRACE(("...... orderby increases cost to %.9g\n", cost));
|
||||
}
|
||||
}else if( pParse->db->flags & SQLITE_ReverseOrder ){
|
||||
/* For application testing, randomly reverse the output order for
|
||||
** SELECT statements that omit the ORDER BY clause. This will help
|
||||
** to find cases where
|
||||
*/
|
||||
wsFlags |= WHERE_REVERSE;
|
||||
}
|
||||
|
||||
/* Check to see if we can get away with using just the index without
|
||||
|
@ -2738,11 +2785,13 @@ static Bitmask codeOneLoopStart(
|
|||
/* Case 5: There is no usable index. We must do a complete
|
||||
** scan of the entire table.
|
||||
*/
|
||||
static const u8 aStep[] = { OP_Next, OP_Prev };
|
||||
static const u8 aStart[] = { OP_Rewind, OP_Last };
|
||||
assert( bRev==0 || bRev==1 );
|
||||
assert( omitTable==0 );
|
||||
assert( bRev==0 );
|
||||
pLevel->op = OP_Next;
|
||||
pLevel->op = aStep[bRev];
|
||||
pLevel->p1 = iCur;
|
||||
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addrBrk);
|
||||
pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
|
||||
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
|
||||
codeRowSetEarly = 0;
|
||||
}
|
||||
|
@ -2836,7 +2885,7 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
|
|||
assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed );
|
||||
if( pInfo->needToFreeIdxStr ){
|
||||
sqlite3_free(pInfo->idxStr);
|
||||
}
|
||||
}
|
||||
sqlite3DbFree(db, pInfo);
|
||||
}
|
||||
}
|
||||
|
@ -2943,6 +2992,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||
int regRowSet /* Register hold RowSet if WHERE_FILL_ROWSET is set */
|
||||
){
|
||||
int i; /* Loop counter */
|
||||
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
|
||||
WhereInfo *pWInfo; /* Will become the return value of this function */
|
||||
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
|
||||
Bitmask notReady; /* Cursors that are not yet positioned */
|
||||
|
@ -2968,15 +3018,19 @@ WhereInfo *sqlite3WhereBegin(
|
|||
}
|
||||
|
||||
/* Allocate and initialize the WhereInfo structure that will become the
|
||||
** return value.
|
||||
** return value. A single allocation is used to store the WhereInfo
|
||||
** struct, the contents of WhereInfo.a[], the WhereClause structure
|
||||
** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
|
||||
** field (type Bitmask) it must be aligned on an 8-byte boundary on
|
||||
** some architectures. Hence the ROUND8() below.
|
||||
*/
|
||||
db = pParse->db;
|
||||
pWInfo = sqlite3DbMallocZero(db,
|
||||
sizeof(WhereInfo)
|
||||
+ (pTabList->nSrc-1)*sizeof(WhereLevel)
|
||||
+ sizeof(WhereClause)
|
||||
+ sizeof(WhereMaskSet)
|
||||
);
|
||||
nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel));
|
||||
pWInfo = sqlite3DbMallocZero(db,
|
||||
nByteWInfo +
|
||||
sizeof(WhereClause) +
|
||||
sizeof(WhereMaskSet)
|
||||
);
|
||||
if( db->mallocFailed ){
|
||||
goto whereBeginError;
|
||||
}
|
||||
|
@ -2985,7 +3039,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||
pWInfo->pTabList = pTabList;
|
||||
pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
|
||||
pWInfo->regRowSet = (wctrlFlags & WHERE_FILL_ROWSET) ? regRowSet : -1;
|
||||
pWInfo->pWC = pWC = (WhereClause*)&pWInfo->a[pWInfo->nLevel];
|
||||
pWInfo->pWC = pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo];
|
||||
pWInfo->wctrlFlags = wctrlFlags;
|
||||
pMaskSet = (WhereMaskSet*)&pWC[1];
|
||||
|
||||
|
@ -3231,7 +3285,7 @@ WhereInfo *sqlite3WhereBegin(
|
|||
Bitmask b = pTabItem->colUsed;
|
||||
int n = 0;
|
||||
for(; b; b=b>>1, n++){}
|
||||
sqlite3VdbeChangeP2(v, sqlite3VdbeCurrentAddr(v)-2, n);
|
||||
sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, SQLITE_INT_TO_PTR(n), P4_INT32);
|
||||
assert( n<=pTab->nCol );
|
||||
}
|
||||
}else{
|
||||
|
@ -3244,7 +3298,6 @@ WhereInfo *sqlite3WhereBegin(
|
|||
int iIdxCur = pLevel->iIdxCur;
|
||||
assert( pIx->pSchema==pTab->pSchema );
|
||||
assert( iIdxCur>=0 );
|
||||
sqlite3VdbeAddOp2(v, OP_SetNumColumns, 0, pIx->nColumn+1);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb,
|
||||
(char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
VdbeComment((v, "%s", pIx->zName));
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the ALTER TABLE statement.
|
||||
#
|
||||
# $Id: alter.test,v 1.31 2009/02/13 03:43:32 drh Exp $
|
||||
# $Id: alter.test,v 1.32 2009/03/24 15:08:10 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
@ -677,7 +677,7 @@ do_test alter-9.2 {
|
|||
execsql {
|
||||
SELECT SQLITE_RENAME_TABLE(0,0);
|
||||
SELECT SQLITE_RENAME_TABLE(10,20);
|
||||
SELECT SQLITE_RENAME_TABLE("foo", "foo");
|
||||
SELECT SQLITE_RENAME_TABLE('foo', 'foo');
|
||||
}
|
||||
} {{} {} {}}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# file format change that may be used in the future to implement
|
||||
# "ALTER TABLE ... ADD COLUMN".
|
||||
#
|
||||
# $Id: alter2.test,v 1.13 2008/03/19 00:21:31 drh Exp $
|
||||
# $Id: alter2.test,v 1.14 2009/04/07 14:14:22 danielk1977 Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
@ -220,6 +220,8 @@ ifcapable trigger {
|
|||
SELECT * FROM clog;
|
||||
}
|
||||
} {{} 2 {} 6 {} 10}
|
||||
} else {
|
||||
execsql { CREATE TABLE abc3(a, b); }
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
@ -276,9 +278,7 @@ ifcapable vacuum {
|
|||
get_file_format
|
||||
} {2}
|
||||
do_test alter2-5.2 {
|
||||
execsql {
|
||||
VACUUM;
|
||||
}
|
||||
execsql { VACUUM }
|
||||
} {}
|
||||
do_test alter2-5.3 {
|
||||
get_file_format
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#***********************************************************************
|
||||
# This file runs all tests.
|
||||
#
|
||||
# $Id: async.test,v 1.14 2008/09/15 14:47:21 danielk1977 Exp $
|
||||
# $Id: async.test,v 1.19 2009/04/11 10:25:04 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
@ -17,15 +17,16 @@ if {[catch {sqlite3async_enable}]} {
|
|||
return
|
||||
}
|
||||
|
||||
rename finish_test really_finish_test
|
||||
rename finish_test async_really_finish_test
|
||||
proc finish_test {} {
|
||||
catch {db close}
|
||||
catch {db2 close}
|
||||
catch {db3 close}
|
||||
}
|
||||
if {[info exists ISQUICK]} { set ASYNC_SAVE_ISQUICK $ISQUICK }
|
||||
set ISQUICK 1
|
||||
|
||||
set INCLUDE {
|
||||
set ASYNC_INCLUDE {
|
||||
insert.test
|
||||
insert2.test
|
||||
insert3.test
|
||||
|
@ -42,9 +43,9 @@ set INCLUDE {
|
|||
# Enable asynchronous IO.
|
||||
sqlite3async_enable 1
|
||||
|
||||
rename do_test really_do_test
|
||||
rename do_test async_really_do_test
|
||||
proc do_test {name args} {
|
||||
uplevel really_do_test async_io-$name $args
|
||||
uplevel async_really_do_test async_io-$name $args
|
||||
sqlite3async_start
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_wait
|
||||
|
@ -53,7 +54,7 @@ proc do_test {name args} {
|
|||
|
||||
foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
|
||||
set tail [file tail $testfile]
|
||||
if {[lsearch -exact $INCLUDE $tail]<0} continue
|
||||
if {[lsearch -exact $ASYNC_INCLUDE $tail]<0} continue
|
||||
source $testfile
|
||||
|
||||
# Make sure everything is flushed through. This is because [source]ing
|
||||
|
@ -77,6 +78,11 @@ sqlite3async_halt never
|
|||
sqlite3async_enable 0
|
||||
set sqlite3async_trace 0
|
||||
|
||||
really_finish_test
|
||||
rename really_do_test do_test
|
||||
rename really_finish_test finish_test
|
||||
rename do_test {}
|
||||
rename async_really_do_test do_test
|
||||
rename finish_test {}
|
||||
rename async_really_finish_test finish_test
|
||||
|
||||
if {[info exists ASYNC_SAVE_ISQUICK]} { set ISQUICK $ASYNC_SAVE_ISQUICK }
|
||||
finish_test
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# $Id: async2.test,v 1.9 2007/11/05 17:01:08 danielk1977 Exp $
|
||||
# $Id: async2.test,v 1.10 2009/03/24 17:43:57 drh Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
@ -47,7 +47,7 @@ set sql_script {
|
|||
db close
|
||||
|
||||
foreach err [list ioerr malloc-transient malloc-persistent] {
|
||||
set ::go 1
|
||||
set ::go 10
|
||||
for {set n 1} {$::go} {incr n} {
|
||||
set ::sqlite_io_error_pending 0
|
||||
sqlite3_memdebug_fail -1
|
||||
|
@ -58,6 +58,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] {
|
|||
|
||||
sqlite3async_enable 1
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
|
||||
switch -- $err {
|
||||
ioerr { set ::sqlite_io_error_pending $n }
|
||||
|
@ -78,7 +79,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] {
|
|||
sqlite3_memdebug_fail -1
|
||||
|
||||
sqlite3 db test.db
|
||||
set c [db eval {SELECT c FROM counter LIMIT 1}]
|
||||
set c [db one {SELECT c FROM counter LIMIT 1}]
|
||||
switch -- $c {
|
||||
1 {
|
||||
do_test async-$err-1.1.$n {
|
||||
|
@ -112,7 +113,7 @@ foreach err [list ioerr malloc-transient malloc-persistent] {
|
|||
} {klmnopqrst and seven}
|
||||
}
|
||||
FIN {
|
||||
set ::go 0
|
||||
incr ::go -1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# Specifically, it tests that the xFullPathname() method of
|
||||
# of the asynchronous vfs works correctly.
|
||||
#
|
||||
# $Id: async3.test,v 1.3 2007/11/05 17:01:08 danielk1977 Exp $
|
||||
# $Id: async3.test,v 1.4 2009/03/28 18:56:14 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
@ -27,6 +27,7 @@ if { [info commands sqlite3async_enable]=="" } {
|
|||
|
||||
db close
|
||||
sqlite3async_enable 1
|
||||
#set sqlite3async_trace 1
|
||||
sqlite3async_start
|
||||
|
||||
set paths {
|
||||
|
@ -67,6 +68,7 @@ foreach p $paths {
|
|||
}
|
||||
|
||||
db close
|
||||
|
||||
sqlite3async_halt idle
|
||||
sqlite3async_wait
|
||||
sqlite3async_halt never
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the SELECT statement.
|
||||
#
|
||||
# $Id: autovacuum.test,v 1.28 2008/09/10 10:57:28 danielk1977 Exp $
|
||||
# $Id: autovacuum.test,v 1.29 2009/04/06 17:50:03 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
@ -660,6 +660,37 @@ do_test autovacuum-8.2 {
|
|||
} {1 {database is locked}}
|
||||
catch {db2 close}
|
||||
catch {db eval {COMMIT}}
|
||||
|
||||
|
||||
do_test autovacuum-9.1 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
DROP TABLE t3;
|
||||
DROP TABLE t4;
|
||||
DROP TABLE t5;
|
||||
PRAGMA page_count;
|
||||
}
|
||||
} {1}
|
||||
do_test autovacuum-9.2 {
|
||||
file size test.db
|
||||
} 1024
|
||||
do_test autovacuum-9.3 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(NULL, randstr(50,50));
|
||||
}
|
||||
for {set ii 0} {$ii < 10} {incr ii} {
|
||||
db eval { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 }
|
||||
}
|
||||
file size test.db
|
||||
} $::sqlite_pending_byte
|
||||
do_test autovacuum-9.4 {
|
||||
execsql { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 }
|
||||
} {}
|
||||
do_test autovacuum-9.5 {
|
||||
execsql { DELETE FROM t1 WHERE rowid > (SELECT max(a)/2 FROM t1) }
|
||||
file size test.db
|
||||
} $::sqlite_pending_byte
|
||||
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the sqlite3_backup_XXX API.
|
||||
#
|
||||
# $Id: backup.test,v 1.8 2009/02/12 17:01:50 danielk1977 Exp $
|
||||
# $Id: backup.test,v 1.9 2009/03/16 13:19:36 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
@ -736,7 +736,7 @@ do_test backup-7.2.1 {
|
|||
} {}
|
||||
do_test backup-7.2.2 {
|
||||
B step 5000
|
||||
} {SQLITE_LOCKED}
|
||||
} {SQLITE_BUSY}
|
||||
do_test backup-7.2.3 {
|
||||
execsql { ROLLBACK }
|
||||
B step 5000
|
||||
|
|
|
@ -13,11 +13,13 @@
|
|||
# of the TCL interface - methods which are based on the
|
||||
# sqlite3_backup_XXX API.
|
||||
#
|
||||
# $Id: backup2.test,v 1.2 2009/02/11 16:06:19 shane Exp $
|
||||
# $Id: backup2.test,v 1.4 2009/04/07 14:14:23 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !trigger||!view { finish_test ; return }
|
||||
|
||||
# Fill a database with test data.
|
||||
#
|
||||
do_test backup2-1 {
|
||||
|
@ -111,8 +113,8 @@ do_test backup2-6 {
|
|||
# Try to backup to something that is not a database file.
|
||||
#
|
||||
do_test backup2-7 {
|
||||
catch {file attributes bu2.db -permissions rw-------}
|
||||
catch {file attributes bu2.db -readonly 0}
|
||||
catch {file attributes bu2.db -permissions rw-------}
|
||||
set out [open bu2.db w]
|
||||
puts $out "This is not a valid database file"
|
||||
close $out
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# focus of this file is testing the handling of IO errors by the
|
||||
# sqlite3_backup_XXX APIs.
|
||||
#
|
||||
# $Id: backup_ioerr.test,v 1.2 2009/02/04 22:46:47 drh Exp $
|
||||
# $Id: backup_ioerr.test,v 1.3 2009/04/10 18:41:01 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
@ -53,7 +53,7 @@ proc populate_database {db {xtra_large 0}} {
|
|||
do_test backup_ioerr-1.1 {
|
||||
populate_database db
|
||||
set nPage [expr {[file size test.db] / 1024}]
|
||||
expr {$nPage>140 && $nPage<150}
|
||||
expr {$nPage>130 && $nPage<160}
|
||||
} {1}
|
||||
do_test backup_ioerr-1.2 {
|
||||
expr {[file size test.db] > $sqlite_pending_byte}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# focus of this script testing the ability of SQLite to handle database
|
||||
# files larger than 4GB.
|
||||
#
|
||||
# $Id: bigfile.test,v 1.11 2008/11/21 22:21:51 drh Exp $
|
||||
# $Id: bigfile.test,v 1.12 2009/03/05 04:27:08 shane Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
@ -66,7 +66,6 @@ do_test bigfile-1.1 {
|
|||
db close
|
||||
if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} {
|
||||
puts "**** Unable to create a file larger than 4096 MB. *****"
|
||||
puts "$msg"
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
@ -83,7 +82,11 @@ do_test bigfile-1.2 {
|
|||
# tests. We will know the above test failed because the "db" command
|
||||
# does not exist.
|
||||
#
|
||||
if {[llength [info command db]]>0} {
|
||||
if {[llength [info command db]]<=0} {
|
||||
puts "**** Large file support appears to be broken. *****"
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test bigfile-1.3 {
|
||||
execsql {
|
||||
|
@ -98,11 +101,6 @@ do_test bigfile-1.4 {
|
|||
SELECT md5sum(x) FROM t1;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.5 {
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t2;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
|
||||
db close
|
||||
if {[catch {fake_big_file 8192 [pwd]/test.db}]} {
|
||||
|
@ -111,12 +109,18 @@ if {[catch {fake_big_file 8192 [pwd]/test.db}]} {
|
|||
return
|
||||
}
|
||||
|
||||
do_test bigfile-1.6 {
|
||||
do_test bigfile-1.5 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t1;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.6 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t2;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.7 {
|
||||
execsql {
|
||||
CREATE TABLE t3 AS SELECT * FROM t1;
|
||||
|
@ -135,11 +139,6 @@ do_test bigfile-1.9 {
|
|||
SELECT md5sum(x) FROM t2;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.10 {
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t3;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
|
||||
db close
|
||||
if {[catch {fake_big_file 16384 [pwd]/test.db}]} {
|
||||
|
@ -148,33 +147,40 @@ if {[catch {fake_big_file 16384 [pwd]/test.db}]} {
|
|||
return
|
||||
}
|
||||
|
||||
do_test bigfile-1.11 {
|
||||
do_test bigfile-1.10 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t1;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.11 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t2;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.12 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t3;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.13 {
|
||||
execsql {
|
||||
CREATE TABLE t4 AS SELECT * FROM t1;
|
||||
SELECT md5sum(x) FROM t4;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.13 {
|
||||
do_test bigfile-1.14 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t1;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.14 {
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t2;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.15 {
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t3;
|
||||
SELECT md5sum(x) FROM t2;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.16 {
|
||||
|
@ -182,12 +188,5 @@ do_test bigfile-1.16 {
|
|||
SELECT md5sum(x) FROM t3;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
do_test bigfile-1.17 {
|
||||
execsql {
|
||||
SELECT md5sum(x) FROM t4;
|
||||
}
|
||||
} $::MAGIC_SUM
|
||||
|
||||
} ;# End of the "if( db command exists )"
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script testing the sqlite_bind API.
|
||||
#
|
||||
# $Id: bind.test,v 1.46 2009/02/09 05:18:33 danielk1977 Exp $
|
||||
# $Id: bind.test,v 1.47 2009/02/20 03:55:05 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
@ -683,4 +683,76 @@ do_test bind-14.4 {
|
|||
param_names db { SELECT @a, @b FROM (SELECT NULL) }
|
||||
} {@a @b}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Tests of the OP_Variable opcode where P3>1
|
||||
#
|
||||
do_test bind-15.1 {
|
||||
db eval {CREATE TABLE t4(a,b,c,d,e,f,g,h);}
|
||||
set VM [sqlite3_prepare db {
|
||||
INSERT INTO t4(a,b,c,d,f,g,h,e) VALUES(?,?,?,?,?,?,?,?)
|
||||
} -1 TAIL]
|
||||
sqlite3_bind_int $VM 1 1
|
||||
sqlite3_bind_int $VM 2 2
|
||||
sqlite3_bind_int $VM 3 3
|
||||
sqlite3_bind_int $VM 4 4
|
||||
sqlite3_bind_int $VM 5 5
|
||||
sqlite3_bind_int $VM 6 6
|
||||
sqlite3_bind_int $VM 7 7
|
||||
sqlite3_bind_int $VM 8 8
|
||||
sqlite3_step $VM
|
||||
sqlite3_finalize $VM
|
||||
db eval {SELECT * FROM t4}
|
||||
} {1 2 3 4 8 5 6 7}
|
||||
do_test bind-15.2 {
|
||||
db eval {DELETE FROM t4}
|
||||
set VM [sqlite3_prepare db {
|
||||
INSERT INTO t4(a,b,c,d,e,f,g,h) VALUES(?,?,?,?,?,?,?,?)
|
||||
} -1 TAIL]
|
||||
sqlite3_bind_int $VM 1 1
|
||||
sqlite3_bind_int $VM 2 2
|
||||
sqlite3_bind_int $VM 3 3
|
||||
sqlite3_bind_int $VM 4 4
|
||||
sqlite3_bind_int $VM 5 5
|
||||
sqlite3_bind_int $VM 6 6
|
||||
sqlite3_bind_int $VM 7 7
|
||||
sqlite3_bind_int $VM 8 8
|
||||
sqlite3_step $VM
|
||||
sqlite3_finalize $VM
|
||||
db eval {SELECT * FROM t4}
|
||||
} {1 2 3 4 5 6 7 8}
|
||||
do_test bind-15.3 {
|
||||
db eval {DELETE FROM t4}
|
||||
set VM [sqlite3_prepare db {
|
||||
INSERT INTO t4(h,g,f,e,d,c,b,a) VALUES(?,?,?,?,?,?,?,?)
|
||||
} -1 TAIL]
|
||||
sqlite3_bind_int $VM 1 1
|
||||
sqlite3_bind_int $VM 2 2
|
||||
sqlite3_bind_int $VM 3 3
|
||||
sqlite3_bind_int $VM 4 4
|
||||
sqlite3_bind_int $VM 5 5
|
||||
sqlite3_bind_int $VM 6 6
|
||||
sqlite3_bind_int $VM 7 7
|
||||
sqlite3_bind_int $VM 8 8
|
||||
sqlite3_step $VM
|
||||
sqlite3_finalize $VM
|
||||
db eval {SELECT * FROM t4}
|
||||
} {8 7 6 5 4 3 2 1}
|
||||
do_test bind-15.4 {
|
||||
db eval {DELETE FROM t4}
|
||||
set VM [sqlite3_prepare db {
|
||||
INSERT INTO t4(a,b,c,d,e,f,g,h) VALUES(?,?,?,?4,?,?6,?,?)
|
||||
} -1 TAIL]
|
||||
sqlite3_bind_int $VM 1 1
|
||||
sqlite3_bind_int $VM 2 2
|
||||
sqlite3_bind_int $VM 3 3
|
||||
sqlite3_bind_int $VM 4 4
|
||||
sqlite3_bind_int $VM 5 5
|
||||
sqlite3_bind_int $VM 6 6
|
||||
sqlite3_bind_int $VM 7 7
|
||||
sqlite3_bind_int $VM 8 8
|
||||
sqlite3_step $VM
|
||||
sqlite3_finalize $VM
|
||||
db eval {SELECT * FROM t4}
|
||||
} {1 2 3 4 5 6 7 8}
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script testing the sqlite_transfer_bindings() API.
|
||||
#
|
||||
# $Id: bindxfer.test,v 1.6 2008/10/12 00:27:54 shane Exp $
|
||||
# $Id: bindxfer.test,v 1.7 2009/04/09 14:02:44 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
@ -68,11 +68,8 @@ ifcapable deprecated {
|
|||
do_test bindxfer-1.8 {
|
||||
set VALUES
|
||||
} {one two {}}
|
||||
do_test bindxfer-1.9-misuse {
|
||||
catch {sqlite3_finalize $VM1}
|
||||
catch {sqlite3_finalize $VM2}
|
||||
sqlite3_transfer_bindings $VM1 $VM2
|
||||
} 21 ;# SQLITE_MISUSE
|
||||
catch {sqlite3_finalize $VM1}
|
||||
catch {sqlite3_finalize $VM2}
|
||||
do_test bindxfer-1.10 {
|
||||
set VM1 [sqlite3_prepare $DB {SELECT ?, ?, ?} -1 TAIL]
|
||||
set VM2 [sqlite3_prepare $DB {SELECT ?, ?, ?, ?} -1 TAIL]
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# Unit testing of the Bitvec object.
|
||||
#
|
||||
# $Id: bitvec.test,v 1.3 2008/11/19 18:30:35 shane Exp $
|
||||
# $Id: bitvec.test,v 1.4 2009/04/01 23:49:04 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
@ -140,6 +140,14 @@ do_test bitvec-2.5 {
|
|||
do_test bitvec-2.6 {
|
||||
sqlite3BitvecBuiltinTest 50000 {3 60 2 50000 1 1 0}
|
||||
} 0
|
||||
do_test bitvec-2.7 {
|
||||
sqlite3BitvecBuiltinTest 5000 {
|
||||
1 25 121 125
|
||||
1 50 121 125
|
||||
2 25 121 125
|
||||
0
|
||||
}
|
||||
} 0
|
||||
|
||||
# This procedure runs sqlite3BitvecBuiltinTest with argments "n" and
|
||||
# "program". But it also causes a malloc error to occur after the
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing CHECK constraints
|
||||
#
|
||||
# $Id: check.test,v 1.11 2007/07/23 19:39:47 drh Exp $
|
||||
# $Id: check.test,v 1.12 2009/03/24 15:08:10 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
@ -115,11 +115,12 @@ do_test check-1.17 {
|
|||
} {4 11.0}
|
||||
|
||||
do_test check-2.1 {
|
||||
breakpoint
|
||||
execsql {
|
||||
CREATE TABLE t2(
|
||||
x INTEGER CHECK( typeof(coalesce(x,0))=="integer" ),
|
||||
y REAL CHECK( typeof(coalesce(y,0.1))=="real" ),
|
||||
z TEXT CHECK( typeof(coalesce(z,''))=="text" )
|
||||
y REAL CHECK( typeof(coalesce(y,0.1))=='real' ),
|
||||
z TEXT CHECK( typeof(coalesce(z,''))=='text' )
|
||||
);
|
||||
}
|
||||
} {}
|
||||
|
@ -129,6 +130,8 @@ do_test check-2.2 {
|
|||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 2.2 three}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_test check-2.3 {
|
||||
execsql {
|
||||
INSERT INTO t2 VALUES(NULL, NULL, NULL);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# This file implements tests to make sure SQLite does not crash or
|
||||
# segfault if it sees a corrupt database file.
|
||||
#
|
||||
# $Id: corrupt2.test,v 1.18 2008/09/29 11:49:48 danielk1977 Exp $
|
||||
# $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
@ -233,6 +233,8 @@ db2 close
|
|||
|
||||
proc corruption_test {args} {
|
||||
set A(-corrupt) {}
|
||||
set A(-sqlprep) {}
|
||||
set A(-tclprep) {}
|
||||
array set A $args
|
||||
|
||||
catch {db close}
|
||||
|
@ -240,6 +242,7 @@ proc corruption_test {args} {
|
|||
file delete -force corrupt.db-journal
|
||||
|
||||
sqlite3 db corrupt.db
|
||||
eval $A(-tclprep)
|
||||
db eval $A(-sqlprep)
|
||||
db close
|
||||
|
||||
|
@ -455,4 +458,78 @@ corruption_test -sqlprep {
|
|||
} {SQLITE_CORRUPT}
|
||||
}
|
||||
|
||||
corruption_test -sqlprep {
|
||||
PRAGMA auto_vacuum = incremental;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, randstr(100,100));
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
DELETE FROM t1;
|
||||
} -corrupt {
|
||||
set offset [expr [file size corrupt.db] - 1024]
|
||||
hexio_write corrupt.db $offset FF
|
||||
hexio_write corrupt.db 24 12345678
|
||||
} -test {
|
||||
do_test corrupt2-11.1 {
|
||||
catchsql { PRAGMA incremental_vacuum }
|
||||
} {1 {database disk image is malformed}}
|
||||
}
|
||||
corruption_test -sqlprep {
|
||||
PRAGMA auto_vacuum = incremental;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, randstr(100,100));
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
DELETE FROM t1;
|
||||
} -corrupt {
|
||||
set pgno [expr [file size corrupt.db] / 1024]
|
||||
hexio_write corrupt.db [expr 1024+5*($pgno-3)] 03
|
||||
hexio_write corrupt.db 24 12345678
|
||||
} -test {
|
||||
do_test corrupt2-12.1 {
|
||||
catchsql { PRAGMA incremental_vacuum }
|
||||
} {1 {database disk image is malformed}}
|
||||
}
|
||||
|
||||
ifcapable autovacuum {
|
||||
# It is not possible for the last page in a database file to be the
|
||||
# pending-byte page (AKA the locking page). This test verifies that if
|
||||
# an attempt is made to commit a transaction to such an auto-vacuum
|
||||
# database SQLITE_CORRUPT is returned.
|
||||
#
|
||||
corruption_test -tclprep {
|
||||
db eval {
|
||||
PRAGMA auto_vacuum = full;
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(NULL, randstr(50,50));
|
||||
}
|
||||
for {set ii 0} {$ii < 10} {incr ii} {
|
||||
db eval { INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 }
|
||||
}
|
||||
} -corrupt {
|
||||
do_test corrupt2-13.1 {
|
||||
file size corrupt.db
|
||||
} $::sqlite_pending_byte
|
||||
hexio_write corrupt.db [expr $::sqlite_pending_byte+1023] 00
|
||||
} -test {
|
||||
do_test corrupt2-13.2 {
|
||||
file size corrupt.db
|
||||
} [expr $::sqlite_pending_byte + 1024]
|
||||
do_test corrupt2-13.3 {
|
||||
catchsql { DELETE FROM t1 WHERE rowid < 30; }
|
||||
} {1 {database disk image is malformed}}
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue