Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -163,11 +163,11 @@ # Object files for the SQLite library (non-amalgamation). # 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 \ + delete.lo expr.lo fault.lo fkey.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 \ notify.lo opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \ @@ -206,10 +206,11 @@ $(TOP)/src/complete.c \ $(TOP)/src/date.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ $(TOP)/src/fault.c \ + $(TOP)/src/fkey.c \ $(TOP)/src/func.c \ $(TOP)/src/global.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ @@ -233,13 +234,13 @@ $(TOP)/src/mutex_w32.c \ $(TOP)/src/notify.c \ $(TOP)/src/os.c \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ + $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ - $(TOP)/src/os_os2.c \ $(TOP)/src/pager.c \ $(TOP)/src/pager.h \ $(TOP)/src/parse.y \ $(TOP)/src/pcache.c \ $(TOP)/src/pcache.h \ @@ -336,11 +337,11 @@ $(TOP)/src/build.c \ $(TOP)/src/date.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ $(TOP)/src/insert.c \ - $(TOP)/src/malloc.c \ + $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ $(TOP)/src/pager.c \ @@ -379,21 +380,23 @@ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ + $(TOP)/src/test_init.c \ + $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ - $(TOP)/src/test_md5.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ - $(TOP)/src/test_thread.c + $(TOP)/src/test_thread.c \ + $(TOP)/src/test_wsd.c # Header files used by all library source files. # HDR = \ sqlite3.h \ @@ -452,17 +455,10 @@ ./config.status sqlite3.pc: $(TOP)/sqlite3.pc.in ./config.status -# Generate the file "last_change" which contains the date of change -# of the most recently modified source code file -# -last_change: $(SRC) - cat $(SRC) | grep '$$Id: ' | sort -k 5 | tail -1 \ - | $(NAWK) '{print $$5,$$6}' >last_change - libsqlite3.la: $(LIBOBJ) $(LTLINK) -o $@ $(LIBOBJ) $(TLIBS) \ ${ALLOWRELEASE} -rpath "$(libdir)" -version-info "8:6:8" libtclsqlite3.la: tclsqlite.lo libsqlite3.la @@ -495,13 +491,13 @@ sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl # Rules to build the LEMON compiler generator # -lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c +lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/src/lempar.c $(BCC) -o $@ $(TOP)/tool/lemon.c - cp $(TOP)/tool/lempar.c . + cp $(TOP)/src/lempar.c . # Rule to build the amalgamation # sqlite3.lo: sqlite3.c @@ -552,10 +548,13 @@ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/expr.c fault.lo: $(TOP)/src/fault.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/fault.c +fkey.lo: $(TOP)/src/fkey.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/fkey.c + func.lo: $(TOP)/src/func.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/func.c global.lo: $(TOP)/src/global.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/global.c @@ -680,13 +679,12 @@ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/select.c status.lo: $(TOP)/src/status.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/status.c -sqlite3.h: $(TOP)/src/sqlite.h.in - sed -e s/--VERS--/$(RELEASE)/ $(TOP)/src/sqlite.h.in | \ - sed -e s/--VERSION-NUMBER--/$(VERSION_NUMBER)/ >sqlite3.h +sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION + tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h table.lo: $(TOP)/src/table.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/table.c tclsqlite.lo: $(TOP)/src/tclsqlite.c $(HDR) Index: Makefile.vxworks ================================================================== --- Makefile.vxworks +++ Makefile.vxworks @@ -487,13 +487,13 @@ fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl tclsh $(TOP)/ext/fts3/mkfts3amal.tcl # Rules to build the LEMON compiler generator # -lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c +lemon: $(TOP)/tool/lemon.c $(TOP)/src/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c - cp $(TOP)/tool/lempar.c . + cp $(TOP)/src/lempar.c . # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o Index: README ================================================================== --- README +++ README @@ -22,13 +22,18 @@ "Makefile.linux-gcc" in the top directory of the source tree that you can copy and edit to suit your needs. Comments on the generic makefile show what changes are needed. The linux binaries on the website are created using the generic makefile, -not the configure script. -The windows binaries on the website are created using MinGW32 configured -as a cross-compiler running under Linux. For details, see the ./publish.sh -script at the top-level of the source tree. +not the configure script. The windows binaries on the website are created +using MinGW32 configured as a cross-compiler running under Linux. For +details, see the ./publish.sh script at the top-level of the source tree. +The developers do not use teh configure script. + +SQLite does not require TCL to run, but a TCL installation is required +by the makefiles. SQLite contains a lot of generated code and TCL is +used to do much of that code generation. The makefile also requires +AWK. Contacts: http://www.sqlite.org/ Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -3.6.16.1 +3.6.20 Index: addopcodes.awk ================================================================== --- addopcodes.awk +++ addopcodes.awk @@ -17,15 +17,18 @@ printf "#define TK_%-29s %4d\n", "TO_TEXT", ++max printf "#define TK_%-29s %4d\n", "TO_BLOB", ++max printf "#define TK_%-29s %4d\n", "TO_NUMERIC", ++max printf "#define TK_%-29s %4d\n", "TO_INT", ++max printf "#define TK_%-29s %4d\n", "TO_REAL", ++max + printf "#define TK_%-29s %4d\n", "ISNOT", ++max printf "#define TK_%-29s %4d\n", "END_OF_FILE", ++max printf "#define TK_%-29s %4d\n", "ILLEGAL", ++max printf "#define TK_%-29s %4d\n", "SPACE", ++max printf "#define TK_%-29s %4d\n", "UNCLOSED_STRING", ++max printf "#define TK_%-29s %4d\n", "FUNCTION", ++max printf "#define TK_%-29s %4d\n", "COLUMN", ++max printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", ++max printf "#define TK_%-29s %4d\n", "AGG_COLUMN", ++max printf "#define TK_%-29s %4d\n", "CONST_FUNC", ++max + printf "#define TK_%-29s %4d\n", "UMINUS", ++max + printf "#define TK_%-29s %4d\n", "UPLUS", ++max } ADDED art/src_logo.gif Index: art/src_logo.gif ================================================================== --- /dev/null +++ art/src_logo.gif cannot compute difference between binary files Index: configure ================================================================== --- configure +++ configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for sqlite 3.6.16. +# Generated by GNU Autoconf 2.62 for sqlite 3.6.20. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. @@ -741,12 +741,12 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.6.16' -PACKAGE_STRING='sqlite 3.6.16' +PACKAGE_VERSION='3.6.20' +PACKAGE_STRING='sqlite 3.6.20' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include @@ -1485,11 +1485,11 @@ # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.6.16 to adapt to many kinds of systems. +\`configure' configures sqlite 3.6.20 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. @@ -1550,11 +1550,11 @@ _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.6.16:";; + short | recursive ) echo "Configuration of sqlite 3.6.20:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options @@ -1668,11 +1668,11 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.6.16 +sqlite configure 3.6.20 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation @@ -1682,11 +1682,11 @@ fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.6.16, which was +It was created by sqlite $as_me 3.6.20, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ _ACEOF @@ -2063,11 +2063,11 @@ please regen with autoconf" >&2;} { (exit 1); exit 1; }; } fi # The following RCS revision string applies to configure.in -# $Revision: 1.73 $ +# $Revision: 1.56 $ ######### # Programs needed # case `pwd` in @@ -13970,11 +13970,11 @@ # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.6.16, which was +This file was extended by sqlite $as_me 3.6.20, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS @@ -14023,11 +14023,11 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.6.16 +sqlite config.status 3.6.20 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation DELETED doc/report1.txt Index: doc/report1.txt ================================================================== --- doc/report1.txt +++ /dev/null @@ -1,121 +0,0 @@ -An SQLite (version 1.0) database was used in a large military application -where the database contained 105 tables and indices. The following is -a breakdown on the sizes of keys and data within these tables and indices: - -Entries: 967089 -Size: 45896104 -Avg Size: 48 -Key Size: 11112265 -Avg Key Size: 12 -Max Key Size: 99 - - 0..8 263 0% - 9..12 5560 0% - 13..16 71394 7% - 17..24 180717 26% - 25..32 215442 48% - 33..40 151118 64% - 41..48 77479 72% - 49..56 13983 74% - 57..64 14481 75% - 65..80 41342 79% - 81..96 127098 92% - 97..112 38054 96% - 113..128 14197 98% - 129..144 8208 99% - 145..160 3326 99% - 161..176 1242 99% - 177..192 604 99% - 193..208 222 99% - 209..224 213 99% - 225..240 132 99% - 241..256 58 99% - 257..288 515 99% - 289..320 64 99% - 321..352 39 99% - 353..384 44 99% - 385..416 25 99% - 417..448 24 99% - 449..480 26 99% - 481..512 27 99% - 513..1024 470 99% - 1025..2048 396 99% - 2049..4096 187 99% - 4097..8192 78 99% - 8193..16384 35 99% -16385..32768 17 99% -32769..65536 6 99% -65537..65541 3 100% - -If the indices are omitted, the statistics for the 49 tables -become the following: - -Entries: 451103 -Size: 30930282 -Avg Size: 69 -Key Size: 1804412 -Avg Key Size: 4 -Max Key Size: 4 - - 0..24 89 0% - 25..32 9417 2% - 33..40 119162 28% - 41..48 68710 43% - 49..56 9539 45% - 57..64 12435 48% - 65..80 38650 57% - 81..96 126877 85% - 97..112 38030 93% - 113..128 14183 96% - 129..144 7668 98% - 145..160 3302 99% - 161..176 1238 99% - 177..192 597 99% - 193..208 217 99% - 209..224 211 99% - 225..240 130 99% - 241..256 57 99% - 257..288 100 99% - 289..320 62 99% - 321..352 34 99% - 353..384 43 99% - 385..416 24 99% - 417..448 24 99% - 449..480 25 99% - 481..512 27 99% - 513..1024 153 99% - 1025..2048 92 99% - 2049..4096 7 100% - -The 56 indices have these statistics: - -Entries: 512422 -Size: 14879828 -Avg Size: 30 -Key Size: 9253204 -Avg Key Size: 19 -Max Key Size: 99 - - 0..8 246 0% - 9..12 5486 1% - 13..16 70717 14% - 17..24 178246 49% - 25..32 205722 89% - 33..40 31951 96% - 41..48 8768 97% - 49..56 4444 98% - 57..64 2046 99% - 65..80 2691 99% - 81..96 202 99% - 97..112 11 99% - 113..144 527 99% - 145..160 20 99% - 161..288 406 99% - 289..1024 316 99% - 1025..2048 304 99% - 2049..4096 180 99% - 4097..8192 78 99% - 8193..16384 35 99% -16385..32768 17 99% -32769..65536 6 99% -65537..65541 3 100% Index: ext/async/sqlite3async.c ================================================================== --- ext/async/sqlite3async.c +++ ext/async/sqlite3async.c @@ -8,11 +8,11 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** -** $Id: sqlite3async.c,v 1.6 2009/04/30 17:45:34 shane Exp $ +** $Id: sqlite3async.c,v 1.7 2009/07/18 11:52:04 danielk1977 Exp $ ** ** This file contains the implementation of an asynchronous IO backend ** for SQLite. */ @@ -666,12 +666,12 @@ sqlite3_int64 iOffset ){ AsyncFileData *p = ((AsyncFile *)pFile)->pData; int rc = SQLITE_OK; sqlite3_int64 filesize; - int nRead; sqlite3_file *pBase = p->pBaseRead; + sqlite3_int64 iAmt64 = (sqlite3_int64)iAmt; /* Grab the write queue mutex for the duration of the call */ async_mutex_enter(ASYNC_MUTEX_QUEUE); /* If an I/O error has previously occurred in this virtual file @@ -681,15 +681,16 @@ rc = async.ioError; goto asyncread_out; } if( pBase->pMethods ){ + sqlite3_int64 nRead; rc = pBase->pMethods->xFileSize(pBase, &filesize); if( rc!=SQLITE_OK ){ goto asyncread_out; } - nRead = (int)MIN(filesize - iOffset, iAmt); + nRead = MIN(filesize - iOffset, iAmt64); if( nRead>0 ){ rc = pBase->pMethods->xRead(pBase, zOut, nRead, iOffset); ASYNC_TRACE(("READ %s %d bytes at %d\n", p->zName, nRead, iOffset)); } } @@ -701,18 +702,24 @@ for(pWrite=async.pQueueFirst; pWrite; pWrite = pWrite->pNext){ if( pWrite->op==ASYNC_WRITE && ( (pWrite->pFileData==p) || (zName && pWrite->pFileData->zName==zName) )){ + sqlite3_int64 nCopy; + sqlite3_int64 nByte64 = (sqlite3_int64)pWrite->nByte; + + /* Set variable iBeginIn to the offset in buffer pWrite->zBuf[] from + ** which data should be copied. Set iBeginOut to the offset within + ** the output buffer to which data should be copied. If either of + ** these offsets is a negative number, set them to 0. + */ sqlite3_int64 iBeginOut = (pWrite->iOffset-iOffset); sqlite3_int64 iBeginIn = -iBeginOut; - int nCopy; - if( iBeginIn<0 ) iBeginIn = 0; if( iBeginOut<0 ) iBeginOut = 0; - nCopy = (int)MIN(pWrite->nByte-iBeginIn, iAmt-iBeginOut); + nCopy = MIN(nByte64-iBeginIn, iAmt64-iBeginOut); if( nCopy>0 ){ memcpy(&((char *)zOut)[iBeginOut], &pWrite->zBuf[iBeginIn], nCopy); ASYNC_TRACE(("OVERREAD %d bytes at %d\n", nCopy, iBeginOut+iOffset)); } } @@ -1063,11 +1070,14 @@ } if( !isAsyncOpen ){ int flagsout; rc = pVfs->xOpen(pVfs, pData->zName, pData->pBaseRead, flags, &flagsout); - if( rc==SQLITE_OK && (flagsout&SQLITE_OPEN_READWRITE) ){ + if( rc==SQLITE_OK + && (flagsout&SQLITE_OPEN_READWRITE) + && (flags&SQLITE_OPEN_EXCLUSIVE)==0 + ){ rc = pVfs->xOpen(pVfs, pData->zName, pData->pBaseWrite, flags, 0); } if( pOutFlags ){ *pOutFlags = flagsout; } Index: ext/fts3/fts3_expr.c ================================================================== --- ext/fts3/fts3_expr.c +++ ext/fts3/fts3_expr.c @@ -419,11 +419,13 @@ iCol = pParse->iDefaultCol; iColLen = 0; for(ii=0; iinCol; ii++){ const char *zStr = pParse->azCol[ii]; int nStr = strlen(zStr); - if( nInput>nStr && zInput[nStr]==':' && memcmp(zStr, zInput, nStr)==0 ){ + if( nInput>nStr && zInput[nStr]==':' + && sqlite3_strnicmp(zStr, zInput, nStr)==0 + ){ iCol = ii; iColLen = ((zInput - z) + nStr + 1); break; } } @@ -536,14 +538,14 @@ } memset(pNot, 0, sizeof(Fts3Expr)); pNot->eType = FTSQUERY_NOT; pNot->pRight = p; if( pNotBranch ){ - pNotBranch->pLeft = p; - pNot->pRight = pNotBranch; + pNot->pLeft = pNotBranch; } pNotBranch = pNot; + p = pPrev; }else{ int eType = p->eType; assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot ); isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); @@ -621,11 +623,15 @@ rc = SQLITE_OK; if( !sqlite3_fts3_enable_parentheses && pNotBranch ){ if( !pRet ){ rc = SQLITE_ERROR; }else{ - pNotBranch->pLeft = pRet; + Fts3Expr *pIter = pNotBranch; + while( pIter->pLeft ){ + pIter = pIter->pLeft; + } + pIter->pLeft = pRet; pRet = pNotBranch; } } } *pnConsumed = n - nIn; Index: ext/rtree/rtree.c ================================================================== --- ext/rtree/rtree.c +++ ext/rtree/rtree.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code for implementations of the r-tree and r*-tree ** algorithms packaged as an SQLite virtual table module. -** -** $Id: rtree.c,v 1.12 2008/12/22 15:04:32 danielk1977 Exp $ */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) /* @@ -1484,22 +1482,22 @@ ** here is the LinearPickSeeds algorithm from Gutman[1984]. The ** indices of the two seed cells in the array are stored in local ** variables iLeftSeek and iRightSeed. */ for(i=0; inDim; i++){ - float x1 = aCell[0].aCoord[i*2]; - float x2 = aCell[0].aCoord[i*2+1]; + float x1 = DCOORD(aCell[0].aCoord[i*2]); + float x2 = DCOORD(aCell[0].aCoord[i*2+1]); float x3 = x1; float x4 = x2; int jj; int iCellLeft = 0; int iCellRight = 0; for(jj=1; jjx4 ) x4 = right; if( left>x3 ){ x3 = left; @@ -1853,10 +1851,13 @@ int iRightSeed = 1; int *aiUsed; int i; aiUsed = sqlite3_malloc(sizeof(int)*nCell); + if( !aiUsed ){ + return SQLITE_NOMEM; + } memset(aiUsed, 0, sizeof(int)*nCell); PickSeeds(pRtree, aCell, nCell, &iLeftSeed, &iRightSeed); memcpy(pBboxLeft, &aCell[iLeftSeed], sizeof(RtreeCell)); @@ -2340,11 +2341,11 @@ #endif /* ** The xUpdate method for rtree module virtual tables. */ -int rtreeUpdate( +static int rtreeUpdate( sqlite3_vtab *pVtab, int nData, sqlite3_value **azData, sqlite_int64 *pRowid ){ @@ -2735,12 +2736,14 @@ if( zSql ){ zTmp = zSql; zSql = sqlite3_mprintf("%s);", zTmp); sqlite3_free(zTmp); } - if( !zSql || sqlite3_declare_vtab(db, zSql) ){ + if( !zSql ){ rc = SQLITE_NOMEM; + }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){ + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } sqlite3_free(zSql); } if( rc==SQLITE_OK ){ Index: ext/rtree/rtree1.test ================================================================== --- ext/rtree/rtree1.test +++ ext/rtree/rtree1.test @@ -9,12 +9,10 @@ # #*********************************************************************** # # The focus of this file is testing the r-tree extension. # -# $Id: rtree1.test,v 1.6 2008/12/22 15:04:32 danielk1977 Exp $ -# if {![info exists testdir]} { set testdir [file join [file dirname $argv0] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] @@ -390,7 +388,15 @@ execsql { SELECT count(*) FROM bar b1, bar b2, foo s1 WHERE b1.minX <= b2.maxX AND s1.id = b1.id; } } {1600} + +#------------------------------------------------------------------------- +# Ticket #3970: Check that the error message is meaningful when a +# keyword is used as a column name. +# +do_test rtree-10.1 { + catchsql { CREATE VIRTUAL TABLE t7 USING rtree(index, x1, y1, x2, y2) } +} {1 {near "index": syntax error}} finish_test Index: ext/rtree/rtree2.test ================================================================== --- ext/rtree/rtree2.test +++ ext/rtree/rtree2.test @@ -9,12 +9,10 @@ # #*********************************************************************** # # The focus of this file is testing the r-tree extension. # -# $Id: rtree2.test,v 1.4 2008/07/14 15:37:01 danielk1977 Exp $ -# if {![info exists testdir]} { set testdir [file join [file dirname $argv0] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] Index: ext/rtree/rtree3.test ================================================================== --- ext/rtree/rtree3.test +++ ext/rtree/rtree3.test @@ -10,12 +10,10 @@ #*********************************************************************** # # The focus of this file is testing that the r-tree correctly handles # out-of-memory conditions. # -# $Id: rtree3.test,v 1.2 2008/06/23 15:55:52 danielk1977 Exp $ -# if {![info exists testdir]} { set testdir [file join [file dirname $argv0] .. .. test] } source $testdir/tester.tcl @@ -69,6 +67,5 @@ } db eval COMMIT } finish_test - Index: ext/rtree/rtree4.test ================================================================== --- ext/rtree/rtree4.test +++ ext/rtree/rtree4.test @@ -9,12 +9,10 @@ # #*********************************************************************** # # Randomized test cases for the rtree extension. # -# $Id: rtree4.test,v 1.3 2008/06/23 15:55:52 danielk1977 Exp $ -# if {![info exists testdir]} { set testdir [file join [file dirname $argv0] .. .. test] } source $testdir/tester.tcl Index: ext/rtree/rtree5.test ================================================================== --- ext/rtree/rtree5.test +++ ext/rtree/rtree5.test @@ -10,12 +10,10 @@ #*********************************************************************** # # The focus of this file is testing the r-tree extension when it is # configured to store values as 32 bit integers. # -# $Id: rtree5.test,v 1.1 2008/07/14 15:37:01 danielk1977 Exp $ -# if {![info exists testdir]} { set testdir [file join [file dirname $argv0] .. .. test] } source $testdir/tester.tcl Index: ext/rtree/rtree6.test ================================================================== --- ext/rtree/rtree6.test +++ ext/rtree/rtree6.test @@ -6,12 +6,11 @@ # 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. # #*********************************************************************** -# -# $Id: rtree6.test,v 1.1 2008/09/01 12:47:00 danielk1977 Exp $ +# # if {![info exists testdir]} { set testdir [file join [file dirname $argv0] .. .. test] } @@ -94,12 +93,12 @@ ] do_test rtree6.2.4 { query_plan {SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10} } [list \ - {TABLE t2} \ {TABLE t1 VIRTUAL TABLE INDEX 2:CaEb} \ + {TABLE t2} \ ] do_test rtree6.2.5 { query_plan {SELECT * FROM t1,t2 WHERE k=ii AND x1$x2 AND y1<$y1 AND y2>$y2} } }] puts "$rtree_select_time" - - Index: ext/rtree/rtree_util.tcl ================================================================== --- ext/rtree/rtree_util.tcl +++ ext/rtree/rtree_util.tcl @@ -11,12 +11,10 @@ # # This file contains Tcl code that may be useful for testing or # analyzing r-tree structures created with this module. It is # used by both test procedures and the r-tree viewer application. # -# $Id: rtree_util.tcl,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $ -# #-------------------------------------------------------------------------- # PUBLIC API: # @@ -190,6 +188,5 @@ proc rtree_treedump {db zTab} { set d [rtree_depth $db $zTab] rtree_nodetreedump $db $zTab "" $d 1 } - Index: ext/rtree/tkt3363.test ================================================================== --- ext/rtree/tkt3363.test +++ ext/rtree/tkt3363.test @@ -9,12 +9,10 @@ # #*********************************************************************** # # The focus of this file is testing that ticket #3363 is fixed. # -# $Id: tkt3363.test,v 1.1 2008/09/08 11:07:03 danielk1977 Exp $ -# if {![info exists testdir]} { set testdir [file join [file dirname $argv0] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] @@ -48,7 +46,5 @@ SELECT count(*) FROM t1 WHERE y2>4000425.0; } } {7} finish_test - - Index: ext/rtree/viewrtree.tcl ================================================================== --- ext/rtree/viewrtree.tcl +++ ext/rtree/viewrtree.tcl @@ -184,6 +184,5 @@ return $zReport } view_node bind .c view_node - Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -50,11 +50,11 @@ # Object files for the SQLite library. # LIBOBJ+= alter.o analyze.o attach.o auth.o \ backup.o bitvec.o btmutex.o btree.o build.o \ - callback.o complete.o date.o delete.o expr.o fault.o \ + callback.o complete.o date.o delete.o expr.o fault.o fkey.o \ fts3.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_tokenizer.o fts3_tokenizer1.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 \ @@ -88,10 +88,11 @@ $(TOP)/src/complete.c \ $(TOP)/src/date.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ $(TOP)/src/fault.c \ + $(TOP)/src/fkey.c \ $(TOP)/src/func.c \ $(TOP)/src/global.c \ $(TOP)/src/hash.c \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ @@ -190,11 +191,11 @@ $(TOP)/ext/fts3/fts3_tokenizer.h \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_tokenizer1.c SRC += \ $(TOP)/ext/icu/sqliteicu.h \ - $(TOP)/ext/icu/icu.c + $(TOP)/ext/icu/icu.c SRC += \ $(TOP)/ext/rtree/rtree.h \ $(TOP)/ext/rtree/rtree.c @@ -227,30 +228,32 @@ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ + $(TOP)/src/test_init.c \ + $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ - $(TOP)/src/test_md5.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ - $(TOP)/src/test_wsd.c \ + $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ $(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \ $(TOP)/src/build.c $(TOP)/src/date.c \ - $(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/os.c \ + $(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/mem5.c \ + $(TOP)/src/os.c \ $(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \ $(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \ $(TOP)/src/printf.c $(TOP)/src/random.c $(TOP)/src/pcache.c \ $(TOP)/src/pcache1.c $(TOP)/src/select.c $(TOP)/src/tokenize.c \ $(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \ @@ -343,13 +346,13 @@ fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl tclsh $(TOP)/ext/fts3/mkfts3amal.tcl # Rules to build the LEMON compiler generator # -lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c +lemon: $(TOP)/tool/lemon.c $(TOP)/src/lempar.c $(BCC) -o lemon $(TOP)/tool/lemon.c - cp $(TOP)/tool/lempar.c . + cp $(TOP)/src/lempar.c . # Rules to build individual *.o files from generated *.c files. This # applies to: # # parse.o @@ -386,14 +389,12 @@ rm -f parse.h ./lemon $(OPTS) parse.y mv parse.h parse.h.temp awk -f $(TOP)/addopcodes.awk parse.h.temp >parse.h -sqlite3.h: $(TOP)/src/sqlite.h.in - sed -e s/--VERS--/`cat ${TOP}/VERSION`/ \ - -e s/--VERSION-NUMBER--/`cat ${TOP}/VERSION | sed 's/[^0-9]/ /g' | $(NAWK) '{printf "%d%03d%03d",$$1,$$2,$$3}'`/ \ - $(TOP)/src/sqlite.h.in >sqlite3.h +sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION + tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash >keywordhash.h Index: mkopcodeh.awk ================================================================== --- mkopcodeh.awk +++ mkopcodeh.awk @@ -23,21 +23,15 @@ # code generator run (infinitesimally) faster and more importantly it makes # the library footprint smaller. # # This script also scans for lines of the form: # -# case OP_aaaa: /* no-push */ -# -# When the no-push comment is found on an opcode, it means that that -# opcode does not leave a result on the stack. By identifying which -# opcodes leave results on the stack it is possible to determine a -# much smaller upper bound on the size of the stack. This allows -# a smaller stack to be allocated, which is important to embedded -# systems with limited memory space. This script generates a series -# of "NOPUSH_MASK" defines that contain bitmaps of opcodes that leave -# results on the stack. The NOPUSH_MASK defines are used in vdbeaux.c -# to help determine the maximum stack size. +# case OP_aaaa: /* jump, in1, in2, in3, out2-prerelease, out3 */ +# +# When such comments are found on an opcode, it means that certain +# properties apply to that opcode. Set corresponding flags using the +# OPFLG_INITIALIZER macro. # # Remember the TK_ values from the parse.h file /^#define TK_/ { @@ -78,21 +72,25 @@ in3[name] = 1 }else if(x=="out3"){ out3[name] = 1 } } + order[n_op++] = name; } # Assign numbers to all opcodes and output the result. END { cnt = 0 max = 0 print "/* Automatically generated. Do not edit */" print "/* See the mkopcodeh.awk script for details */" op["OP_Noop"] = -1; + order[n_op++] = "OP_Noop"; op["OP_Explain"] = -1; - for(name in op){ + order[n_op++] = "OP_Explain"; + for(i=0; i 'CREATE TABLE t1(a REFERENCES t3)' +*/ +#ifndef SQLITE_OMIT_FOREIGN_KEY +static void renameParentFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + char *zOutput = 0; + char *zResult; + unsigned char const *zInput = sqlite3_value_text(argv[0]); + unsigned char const *zOld = sqlite3_value_text(argv[1]); + unsigned char const *zNew = sqlite3_value_text(argv[2]); + + unsigned const char *z; /* Pointer to token */ + int n; /* Length of token z */ + int token; /* Type of token */ + + UNUSED_PARAMETER(NotUsed); + for(z=zInput; *z; z=z+n){ + n = sqlite3GetToken(z, &token); + if( token==TK_REFERENCES ){ + char *zParent; + do { + z += n; + n = sqlite3GetToken(z, &token); + }while( token==TK_SPACE ); + + zParent = sqlite3DbStrNDup(db, (const char *)z, n); + if( zParent==0 ) break; + sqlite3Dequote(zParent); + if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){ + char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"", + (zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew + ); + sqlite3DbFree(db, zOutput); + zOutput = zOut; + zInput = &z[n]; + } + sqlite3DbFree(db, zParent); + } + } + + zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput), + sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC); + sqlite3DbFree(db, zOutput); +} +#endif + #ifndef SQLITE_OMIT_TRIGGER /* This function is used by SQL generated to implement the ** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER ** statement. The second is a table name. The table name in the CREATE ** TRIGGER statement is replaced with the third argument and the result @@ -170,11 +231,59 @@ renameTableFunc, 0, 0); #ifndef SQLITE_OMIT_TRIGGER sqlite3CreateFunc(db, "sqlite_rename_trigger", 2, SQLITE_UTF8, 0, renameTriggerFunc, 0, 0); #endif +#ifndef SQLITE_OMIT_FOREIGN_KEY + sqlite3CreateFunc(db, "sqlite_rename_parent", 3, SQLITE_UTF8, 0, + renameParentFunc, 0, 0); +#endif +} + +/* +** This function is used to create the text of expressions of the form: +** +** name= OR name= OR ... +** +** If argument zWhere is NULL, then a pointer string containing the text +** "name=" is returned, where is the quoted version +** of the string passed as argument zConstant. The returned buffer is +** allocated using sqlite3DbMalloc(). It is the responsibility of the +** caller to ensure that it is eventually freed. +** +** If argument zWhere is not NULL, then the string returned is +** " OR name=", where is the contents of zWhere. +** In this case zWhere is passed to sqlite3DbFree() before returning. +** +*/ +static char *whereOrName(sqlite3 *db, char *zWhere, char *zConstant){ + char *zNew; + if( !zWhere ){ + zNew = sqlite3MPrintf(db, "name=%Q", zConstant); + }else{ + zNew = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, zConstant); + sqlite3DbFree(db, zWhere); + } + return zNew; +} + +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) +/* +** Generate the text of a WHERE expression which can be used to select all +** tables that have foreign key constraints that refer to table pTab (i.e. +** constraints for which pTab is the parent table) from the sqlite_master +** table. +*/ +static char *whereForeignKeys(Parse *pParse, Table *pTab){ + FKey *p; + char *zWhere = 0; + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + zWhere = whereOrName(pParse->db, zWhere, p->pFrom->zName); + } + return zWhere; } +#endif /* ** Generate the text of a WHERE expression which can be used to select all ** temporary triggers on table pTab from the sqlite_temp_master table. If ** table pTab has no temporary triggers, or is itself stored in the @@ -181,11 +290,10 @@ ** temporary database, NULL is returned. */ static char *whereTempTriggers(Parse *pParse, Table *pTab){ Trigger *pTrig; char *zWhere = 0; - char *tmp = 0; const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */ /* If the table is not located in the temp-db (in which case NULL is ** returned, loop through the tables list of triggers. For each trigger ** that is not part of the temp-db schema, add a clause to the WHERE @@ -193,17 +301,11 @@ */ if( pTab->pSchema!=pTempSchema ){ sqlite3 *db = pParse->db; for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ - if( !zWhere ){ - zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name); - }else{ - tmp = zWhere; - zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->name); - sqlite3DbFree(db, tmp); - } + zWhere = whereOrName(db, zWhere, pTrig->zName); } } } return zWhere; } @@ -233,15 +335,15 @@ #ifndef SQLITE_OMIT_TRIGGER /* Drop any table triggers from the internal schema. */ 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); + sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0); } #endif - /* Drop the table and index from the internal schema */ + /* Drop the table and index from the internal schema. */ sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); /* Reload the table, index and permanent trigger schemas. */ zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName); if( !zWhere ) return; @@ -275,11 +377,11 @@ const char *zTabName; /* Original name of the table */ Vdbe *v; #ifndef SQLITE_OMIT_TRIGGER char *zWhere = 0; /* Where clause to locate temp triggers */ #endif - int isVirtualRename = 0; /* True if this is a v-table with an xRename() */ + VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); @@ -330,12 +432,15 @@ #ifndef SQLITE_OMIT_VIRTUALTABLE if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto exit_rename_table; } - if( IsVirtual(pTab) && pTab->pMod->pModule->xRename ){ - isVirtualRename = 1; + if( IsVirtual(pTab) ){ + pVTab = sqlite3GetVTable(db, pTab); + if( pVTab->pVtab->pModule->xRename==0 ){ + pVTab = 0; + } } #endif /* Begin a transaction and code the VerifyCookie for database iDb. ** Then modify the schema cookie (since the ALTER TABLE modifies the @@ -344,29 +449,45 @@ */ v = sqlite3GetVdbe(pParse); if( v==0 ){ goto exit_rename_table; } - sqlite3BeginWriteOperation(pParse, isVirtualRename, iDb); + sqlite3BeginWriteOperation(pParse, pVTab!=0, iDb); sqlite3ChangeCookie(pParse, iDb); /* If this is a virtual table, invoke the xRename() function if ** one is defined. The xRename() callback will modify the names ** of any resources used by the v-table implementation (including other ** SQLite tables) that are identified by the name of the virtual table. */ #ifndef SQLITE_OMIT_VIRTUALTABLE - if( isVirtualRename ){ + if( pVTab ){ int i = ++pParse->nMem; sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0); - sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); + sqlite3MayAbort(pParse); } #endif /* figure out how many UTF-8 characters are in zName */ zTabName = pTab->zName; nTabName = sqlite3Utf8CharLen(zTabName, -1); + +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) + if( db->flags&SQLITE_ForeignKeys ){ + /* If foreign-key support is enabled, rewrite the CREATE TABLE + ** statements corresponding to all child tables of foreign key constraints + ** for which the renamed table is the parent table. */ + if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){ + sqlite3NestedParse(pParse, + "UPDATE sqlite_master SET " + "sql = sqlite_rename_parent(sql, %Q, %Q) " + "WHERE %s;", zTabName, zName, zWhere); + sqlite3DbFree(db, zWhere); + } + } +#endif /* Modify the sqlite_master table to use the new table name. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET " #ifdef SQLITE_OMIT_TRIGGER @@ -412,10 +533,22 @@ "UPDATE sqlite_temp_master SET " "sql = sqlite_rename_trigger(sql, %Q), " "tbl_name = %Q " "WHERE %s;", zName, zName, zWhere); sqlite3DbFree(db, zWhere); + } +#endif + +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) + if( db->flags&SQLITE_ForeignKeys ){ + FKey *p; + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + Table *pFrom = p->pFrom; + if( pFrom!=pTab ){ + reloadTableSchema(pParse, p->pFrom, pFrom->zName); + } + } } #endif /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, zName); @@ -508,10 +641,15 @@ return; } if( pNew->pIndex ){ sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column"); return; + } + if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ + sqlite3ErrorMsg(pParse, + "Cannot add a REFERENCES column with non-NULL default value"); + return; } if( pCol->notNull && !pDflt ){ sqlite3ErrorMsg(pParse, "Cannot add a NOT NULL column with default value NULL"); return; Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -8,77 +8,94 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code associated with the ANALYZE command. -** -** @(#) $Id: analyze.c,v 1.52 2009/04/16 17:45:48 drh Exp $ */ #ifndef SQLITE_OMIT_ANALYZE #include "sqliteInt.h" /* -** This routine generates code that opens the sqlite_stat1 table on cursor -** iStatCur. +** This routine generates code that opens the sqlite_stat1 table for +** writing with cursor iStatCur. If the library was built with the +** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is +** opened for writing using cursor (iStatCur+1) ** ** If the sqlite_stat1 tables does not previously exist, it is created. -** If it does previously exist, all entires associated with table zWhere -** are removed. If zWhere==0 then all entries are removed. +** Similarly, if the sqlite_stat2 table does not exist and the library +** is compiled with SQLITE_ENABLE_STAT2 defined, it is created. +** +** Argument zWhere may be a pointer to a buffer containing a table name, +** or it may be a NULL pointer. If it is not NULL, then all entries in +** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated +** with the named table are deleted. If zWhere==0, then code is generated +** to delete all stat table entries. */ static void openStatTable( Parse *pParse, /* Parsing context */ int iDb, /* The database we are looking in */ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ const char *zWhere /* Delete entries associated with this table */ ){ + static struct { + const char *zName; + const char *zCols; + } aTable[] = { + { "sqlite_stat1", "tbl,idx,stat" }, +#ifdef SQLITE_ENABLE_STAT2 + { "sqlite_stat2", "tbl,idx,sampleno,sample" }, +#endif + }; + + int aRoot[] = {0, 0}; + u8 aCreateTbl[] = {0, 0}; + + int i; sqlite3 *db = pParse->db; Db *pDb; - int iRootPage; - u8 createStat1 = 0; - Table *pStat; Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3VdbeDb(v)==db ); pDb = &db->aDb[iDb]; - if( (pStat = sqlite3FindTable(db, "sqlite_stat1", pDb->zName))==0 ){ - /* The sqlite_stat1 tables does not exist. Create it. - ** Note that a side-effect of the CREATE TABLE statement is to leave - ** the rootpage of the new table in register pParse->regRoot. This is - ** important because the OpenWrite opcode below will be needing it. */ - sqlite3NestedParse(pParse, - "CREATE TABLE %Q.sqlite_stat1(tbl,idx,stat)", - pDb->zName - ); - iRootPage = pParse->regRoot; - createStat1 = 1; /* Cause rootpage to be taken from top of stack */ - }else if( zWhere ){ - /* The sqlite_stat1 table exists. Delete all entries associated with - ** the table zWhere. */ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", - pDb->zName, zWhere - ); - iRootPage = pStat->tnum; - }else{ - /* The sqlite_stat1 table already exists. Delete all rows. */ - iRootPage = pStat->tnum; - sqlite3VdbeAddOp2(v, OP_Clear, pStat->tnum, iDb); - } - - /* Open the sqlite_stat1 table for writing. Unless it was created - ** by this vdbe program, lock it for writing at the shared-cache level. - ** If this vdbe did create the sqlite_stat1 table, then it must have - ** already obtained a schema-lock, making the write-lock redundant. - */ - if( !createStat1 ){ - sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1"); - } - sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb); - sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32); - sqlite3VdbeChangeP5(v, createStat1); + + for(i=0; izName))==0 ){ + /* The sqlite_stat[12] table does not exist. Create it. Note that a + ** side-effect of the CREATE TABLE statement is to leave the rootpage + ** of the new table in register pParse->regRoot. This is important + ** because the OpenWrite opcode below will be needing it. */ + sqlite3NestedParse(pParse, + "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols + ); + aRoot[i] = pParse->regRoot; + aCreateTbl[i] = 1; + }else{ + /* The table already exists. If zWhere is not NULL, delete all entries + ** associated with the table zWhere. If zWhere is NULL, delete the + ** entire contents of the table. */ + aRoot[i] = pStat->tnum; + sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); + if( zWhere ){ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.%s WHERE tbl=%Q", pDb->zName, zTab, zWhere + ); + }else{ + /* The sqlite_stat[12] table already exists. Delete all rows. */ + sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); + } + } + } + + /* Open the sqlite_stat[12] tables for writing. */ + for(i=0; idb; /* Database handle */ + Index *pIdx; /* An index to being analyzed */ + int iIdxCur; /* Cursor open on index being analyzed */ + Vdbe *v; /* The virtual machine being built up */ + int i; /* Loop counter */ + int topOfLoop; /* The top of the loop */ + int endOfLoop; /* The end of the loop */ + int addr; /* The address of an instruction */ + int iDb; /* Index of database containing pTab */ + int regTabname = iMem++; /* Register containing table name */ + int regIdxname = iMem++; /* Register containing index name */ + int regSampleno = iMem++; /* Register containing next sample number */ + int regCol = iMem++; /* Content of a column analyzed table */ + int regRec = iMem++; /* Register holding completed record */ + int regTemp = iMem++; /* Temporary use register */ + int regRowid = iMem++; /* Rowid for the inserted record */ + +#ifdef SQLITE_ENABLE_STAT2 + int regTemp2 = iMem++; /* Temporary use register */ + int regSamplerecno = iMem++; /* Index of next sample to record */ + int regRecno = iMem++; /* Current sample index */ + int regLast = iMem++; /* Index of last sample to record */ + int regFirst = iMem++; /* Index of first sample to record */ +#endif v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){ /* Do no analysis for tables that have no indices */ return; } - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + assert( sqlite3BtreeHoldsAllMutexes(db) ); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, - pParse->db->aDb[iDb].zName ) ){ + db->aDb[iDb].zName ) ){ return; } #endif /* Establish a read-lock on the table at the shared-cache level. */ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); iIdxCur = pParse->nTab++; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int nCol = pIdx->nColumn; KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - int regFields; /* Register block for building records */ - int regRec; /* Register holding completed record */ - int regTemp; /* Temporary use register */ - int regCol; /* Content of a column from the table being analyzed */ - int regRowid; /* Rowid for the inserted record */ - int regF2; - - /* Open a cursor to the index to be analyzed - */ - assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) ); - nCol = pIdx->nColumn; + + if( iMem+1+(nCol*2)>pParse->nMem ){ + pParse->nMem = iMem+1+(nCol*2); + } + + /* Open a cursor to the index to be analyzed. */ + assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, (char *)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); - regFields = iMem+nCol*2; - regTemp = regRowid = regCol = regFields+3; - regRec = regCol+1; - if( regRec>pParse->nMem ){ - pParse->nMem = regRec; - } - - /* Memory cells are used as follows: - ** - ** mem[iMem]: The total number of rows in the table. - ** mem[iMem+1]: Number of distinct values in column 1 - ** ... - ** mem[iMem+nCol]: Number of distinct values in column N - ** mem[iMem+nCol+1] Last observed value of column 1 - ** ... - ** mem[iMem+nCol+nCol]: Last observed value of column N - ** - ** Cells iMem through iMem+nCol are initialized to 0. The others - ** are initialized to NULL. + + /* Populate the registers containing the table and index names. */ + if( pTab->pIndex==pIdx ){ + sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); + } + sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); + +#ifdef SQLITE_ENABLE_STAT2 + + /* If this iteration of the loop is generating code to analyze the + ** first index in the pTab->pIndex list, then register regLast has + ** not been populated. In this case populate it now. */ + if( pTab->pIndex==pIdx ){ + sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno); + sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2-1, regTemp); + sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regTemp2); + + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regLast); + sqlite3VdbeAddOp2(v, OP_Null, 0, regFirst); + addr = sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, 0, regLast); + sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regFirst); + sqlite3VdbeAddOp3(v, OP_Multiply, regLast, regTemp, regLast); + sqlite3VdbeAddOp2(v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES*2-2); + sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regLast); + sqlite3VdbeJumpHere(v, addr); + } + + /* Zero the regSampleno and regRecno registers. */ + sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno); + sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno); + sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno); +#endif + + /* The block of memory cells initialized here is used as follows. + ** + ** iMem: + ** The total number of rows in the table. + ** + ** iMem+1 .. iMem+nCol: + ** Number of distinct entries in index considering the + ** left-most N columns only, where N is between 1 and nCol, + ** inclusive. + ** + ** iMem+nCol+1 .. Mem+2*nCol: + ** Previous value of indexed columns, from left to right. + ** + ** Cells iMem through iMem+nCol are initialized to 0. The others are + ** initialized to contain an SQL NULL. */ for(i=0; i<=nCol; i++){ sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i); } for(i=0; imallocFailed ){ + /* If a malloc failure has occurred, then the result of the expression + ** passed as the second argument to the call to sqlite3VdbeJumpHere() + ** below may be negative. Which causes an assert() to fail (or an + ** out-of-bounds write if SQLITE_DEBUG is not defined). */ + return; } sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); for(i=0; i0 then it is always the case the D>0 so division by zero ** is never possible. */ addr = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); - sqlite3VdbeAddOp4(v, OP_String8, 0, regFields, 0, pTab->zName, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, regFields+1, 0, pIdx->zName, 0); - regF2 = regFields+2; - sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regF2); + sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); for(i=0; inTab++; + iStatCur = pParse->nTab; + pParse->nTab += 2; openStatTable(pParse, iDb, iStatCur, 0); iMem = pParse->nMem+1; for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); analyzeOneTable(pParse, pTab, iStatCur, iMem); @@ -265,11 +364,12 @@ assert( pTab!=0 ); assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); - iStatCur = pParse->nTab++; + iStatCur = pParse->nTab; + pParse->nTab += 2; openStatTable(pParse, iDb, iStatCur, pTab->zName); analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1); loadAnalysis(pParse, iDb); } @@ -385,11 +485,51 @@ } return 0; } /* -** Load the content of the sqlite_stat1 table into the index hash tables. +** If the Index.aSample variable is not NULL, delete the aSample[] array +** and its contents. +*/ +void sqlite3DeleteIndexSamples(Index *pIdx){ +#ifdef SQLITE_ENABLE_STAT2 + if( pIdx->aSample ){ + int j; + sqlite3 *dbMem = pIdx->pTable->dbMem; + for(j=0; jaSample[j]; + if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ + sqlite3DbFree(pIdx->pTable->dbMem, p->u.z); + } + } + sqlite3DbFree(dbMem, pIdx->aSample); + pIdx->aSample = 0; + } +#else + UNUSED_PARAMETER(pIdx); +#endif +} + +/* +** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The +** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] +** arrays. The contents of sqlite_stat2 are used to populate the +** Index.aSample[] arrays. +** +** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR +** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined +** during compilation and the sqlite_stat2 table is present, no data is +** read from it. +** +** If SQLITE_ENABLE_STAT2 was defined during compilation and the +** sqlite_stat2 table is not present in the database, SQLITE_ERROR is +** returned. However, in this case, data is read from the sqlite_stat1 +** table (if it is present) before returning. +** +** If an OOM error occurs, this function always sets db->mallocFailed. +** This means if the caller does not care about other errors, the return +** code may be ignored. */ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ analysisInfo sInfo; HashElem *i; char *zSql; @@ -401,32 +541,111 @@ /* Clear any prior statistics */ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); + sqlite3DeleteIndexSamples(pIdx); } - /* Check to make sure the sqlite_stat1 table existss */ + /* Check to make sure the sqlite_stat1 table exists */ sInfo.db = db; sInfo.zDatabase = db->aDb[iDb].zName; if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ - return SQLITE_ERROR; + return SQLITE_ERROR; } - /* Load new statistics out of the sqlite_stat1 table */ - zSql = sqlite3MPrintf(db, "SELECT idx, stat FROM %Q.sqlite_stat1", - sInfo.zDatabase); + zSql = sqlite3MPrintf(db, + "SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ (void)sqlite3SafetyOff(db); rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); (void)sqlite3SafetyOn(db); sqlite3DbFree(db, zSql); - if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + } + + + /* Load the statistics from the sqlite_stat2 table. */ +#ifdef SQLITE_ENABLE_STAT2 + if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){ + rc = SQLITE_ERROR; + } + if( rc==SQLITE_OK ){ + sqlite3_stmt *pStmt = 0; + + zSql = sqlite3MPrintf(db, + "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase); + if( !zSql ){ + rc = SQLITE_NOMEM; + }else{ + (void)sqlite3SafetyOff(db); + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + (void)sqlite3SafetyOn(db); + sqlite3DbFree(db, zSql); + } + + if( rc==SQLITE_OK ){ + (void)sqlite3SafetyOff(db); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + char *zIndex = (char *)sqlite3_column_text(pStmt, 0); + Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase); + if( pIdx ){ + int iSample = sqlite3_column_int(pStmt, 1); + sqlite3 *dbMem = pIdx->pTable->dbMem; + assert( dbMem==db || dbMem==0 ); + if( iSample=0 ){ + int eType = sqlite3_column_type(pStmt, 2); + + if( pIdx->aSample==0 ){ + static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES; + pIdx->aSample = (IndexSample *)sqlite3DbMallocZero(dbMem, sz); + if( pIdx->aSample==0 ){ + db->mallocFailed = 1; + break; + } + } + + assert( pIdx->aSample ); + { + IndexSample *pSample = &pIdx->aSample[iSample]; + pSample->eType = (u8)eType; + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + pSample->u.r = sqlite3_column_double(pStmt, 2); + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ + const char *z = (const char *)( + (eType==SQLITE_BLOB) ? + sqlite3_column_blob(pStmt, 2): + sqlite3_column_text(pStmt, 2) + ); + int n = sqlite3_column_bytes(pStmt, 2); + if( n>24 ){ + n = 24; + } + pSample->nByte = (u8)n; + pSample->u.z = sqlite3DbMallocRaw(dbMem, n); + if( pSample->u.z ){ + memcpy(pSample->u.z, z, n); + }else{ + db->mallocFailed = 1; + break; + } + } + } + } + } + } + rc = sqlite3_finalize(pStmt); + (void)sqlite3SafetyOn(db); + } + } +#endif + + if( rc==SQLITE_NOMEM ){ + db->mallocFailed = 1; } return rc; } #endif /* SQLITE_OMIT_ANALYZE */ Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. -** -** $Id: attach.c,v 1.93 2009/05/31 21:21:41 drh Exp $ */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_ATTACH /* @@ -149,11 +147,11 @@ } aNew->zName = sqlite3DbStrDup(db, zName); aNew->safety_level = 3; #if SQLITE_HAS_CODEC - { + if( rc==SQLITE_OK ){ extern int sqlite3CodecAttach(sqlite3*, int, const void*, int); extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); int nKey; char *zKey; int t = sqlite3_value_type(argv[2]); @@ -166,17 +164,17 @@ case SQLITE_TEXT: case SQLITE_BLOB: nKey = sqlite3_value_bytes(argv[2]); zKey = (char *)sqlite3_value_blob(argv[2]); - sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; } } #endif Index: src/auth.c ================================================================== --- src/auth.c +++ src/auth.c @@ -11,12 +11,10 @@ ************************************************************************* ** This file contains code used to implement the sqlite3_set_authorizer() ** API. This facility is an optional feature of the library. Embedded ** systems that do not need this facility may omit it by recompiling ** the library with -DSQLITE_OMIT_AUTHORIZATION=1 -** -** $Id: auth.c,v 1.31 2009/05/04 18:01:40 drh Exp $ */ #include "sqliteInt.h" /* ** All of the code in this file may be omitted by defining a single @@ -88,10 +86,43 @@ */ static void sqliteAuthBadReturnCode(Parse *pParse){ sqlite3ErrorMsg(pParse, "authorizer malfunction"); pParse->rc = SQLITE_ERROR; } + +/* +** Invoke the authorization callback for permission to read column zCol from +** table zTab in database zDb. This function assumes that an authorization +** callback has been registered (i.e. that sqlite3.xAuth is not NULL). +** +** If SQLITE_IGNORE is returned and pExpr is not NULL, then pExpr is changed +** to an SQL NULL expression. Otherwise, if pExpr is NULL, then SQLITE_IGNORE +** is treated as SQLITE_DENY. In this case an error is left in pParse. +*/ +int sqlite3AuthReadCol( + Parse *pParse, /* The parser context */ + const char *zTab, /* Table name */ + const char *zCol, /* Column name */ + int iDb /* Index of containing database. */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + char *zDb = db->aDb[iDb].zName; /* Name of attached database */ + int rc; /* Auth callback return code */ + + rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext); + if( rc==SQLITE_DENY ){ + if( db->nDb>2 || iDb!=0 ){ + sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol); + }else{ + sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited", zTab, zCol); + } + pParse->rc = SQLITE_AUTH; + }else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){ + sqliteAuthBadReturnCode(pParse); + } + return rc; +} /* ** The pExpr should be a TK_COLUMN expression. The table referred to ** is in pTabList or else it is the NEW or OLD table of a trigger. ** Check to see if it is OK to read this particular column. @@ -105,68 +136,51 @@ Expr *pExpr, /* The expression to check authorization on */ Schema *pSchema, /* The schema of the expression */ SrcList *pTabList /* All table that pExpr might refer to */ ){ sqlite3 *db = pParse->db; - int rc; Table *pTab = 0; /* The table being read */ const char *zCol; /* Name of the column of the table */ int iSrc; /* Index in pTabList->a[] of table being read */ - const char *zDBase; /* Name of database being accessed */ - TriggerStack *pStack; /* The stack of current triggers */ int iDb; /* The index of the database the expression refers to */ + int iCol; /* Index of column in table */ if( db->xAuth==0 ) return; - assert( pExpr->op==TK_COLUMN ); iDb = sqlite3SchemaToIndex(pParse->db, pSchema); if( iDb<0 ){ /* An attempt to read a column out of a subquery or other ** temporary table. */ return; } - if( pTabList ){ - for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ - if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; - } - assert( iSrcnSrc ); - pTab = pTabList->a[iSrc].pTab; - }else{ - pStack = pParse->trigStack; - if( ALWAYS(pStack) ){ - /* This must be an attempt to read the NEW or OLD pseudo-tables - ** of a trigger. - */ - assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx ); - pTab = pStack->pTab; - } - } - if( NEVER(pTab==0) ) return; - if( pExpr->iColumn>=0 ){ - assert( pExpr->iColumnnCol ); - zCol = pTab->aCol[pExpr->iColumn].zName; + + assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); + if( pExpr->op==TK_TRIGGER ){ + pTab = pParse->pTriggerTab; + }else{ + assert( pTabList ); + for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ + if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ + pTab = pTabList->a[iSrc].pTab; + break; + } + } + } + iCol = pExpr->iColumn; + if( NEVER(pTab==0) ) return; + + if( iCol>=0 ){ + assert( iColnCol ); + zCol = pTab->aCol[iCol].zName; }else if( pTab->iPKey>=0 ){ assert( pTab->iPKeynCol ); zCol = pTab->aCol[pTab->iPKey].zName; }else{ zCol = "ROWID"; } assert( iDb>=0 && iDbnDb ); - zDBase = db->aDb[iDb].zName; - rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, - pParse->zAuthContext); - if( rc==SQLITE_IGNORE ){ + if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ pExpr->op = TK_NULL; - }else if( rc==SQLITE_DENY ){ - if( db->nDb>2 || iDb!=0 ){ - sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited", - zDBase, pTab->zName, zCol); - }else{ - sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited",pTab->zName,zCol); - } - pParse->rc = SQLITE_AUTH; - }else if( rc!=SQLITE_OK ){ - sqliteAuthBadReturnCode(pParse); } } /* ** Do an authorization check using the code and arguments given. Return Index: src/backup.c ================================================================== --- src/backup.c +++ src/backup.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains the implementation of the sqlite3_backup_XXX() ** API functions and the related features. -** -** $Id: backup.c,v 1.17 2009/06/03 11:25:07 danielk1977 Exp $ */ #include "sqliteInt.h" #include "btreeInt.h" /* Macro to find the minimum of two numeric values. @@ -316,11 +314,11 @@ /* Lock the destination database, if it is not locked already. */ if( SQLITE_OK==rc && p->bDestLocked==0 && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) ){ p->bDestLocked = 1; - rc = sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); + sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); } /* If there is no open read-transaction on the source database, open ** one now. If a transaction is opened here, then it will be closed ** before this function exits. @@ -356,21 +354,22 @@ }else if( !p->isAttached ){ attachBackupObject(p); } } - if( rc==SQLITE_DONE ){ + /* Update the schema version field in the destination database. This + ** is to make sure that the schema-version really does change in + ** the case where the source and destination databases have the + ** same schema version. + */ + if( rc==SQLITE_DONE + && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK + ){ const int nSrcPagesize = sqlite3BtreeGetPageSize(p->pSrc); const int nDestPagesize = sqlite3BtreeGetPageSize(p->pDest); int nDestTruncate; - /* Update the schema version field in the destination database. This - ** is to make sure that the schema-version really does change in - ** the case where the source and destination databases have the - ** same schema version. - */ - sqlite3BtreeUpdateMeta(p->pDest, 1, p->iDestSchema+1); if( p->pDestDb ){ sqlite3ResetInternalSchema(p->pDestDb, 0); } /* Set nDestTruncate to the final number of pages in the destination Index: src/bitvec.c ================================================================== --- src/bitvec.c +++ src/bitvec.c @@ -31,17 +31,15 @@ ** 5 and 500 set operations per Bitvec object, though the number of sets can ** sometimes grow into tens of thousands or larger. The size of the ** Bitvec object is the number of pages in the database file at the ** 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.15 2009/06/02 21:31:39 drh Exp $ */ #include "sqliteInt.h" /* Size of the Bitvec structure in bytes. */ -#define BITVEC_SZ 512 +#define BITVEC_SZ (sizeof(void*)*128) /* 512 on 32bit. 1024 on 64bit */ /* Round the union size down to the nearest pointer boundary, since that's how ** it will be aligned within the Bitvec struct. */ #define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*)) @@ -144,12 +142,11 @@ return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0; } else{ u32 h = BITVEC_HASH(i++); while( p->u.aHash[h] ){ if( p->u.aHash[h]==i ) return 1; - h++; - if( h>=BITVEC_NINT ) h = 0; + h = (h+1) % BITVEC_NINT; } return 0; } } @@ -165,11 +162,11 @@ ** and that the value for "i" is within range of the Bitvec object. ** Otherwise the behavior is undefined. */ int sqlite3BitvecSet(Bitvec *p, u32 i){ u32 h; - assert( p!=0 ); + if( p==0 ) return SQLITE_OK; assert( i>0 ); assert( i<=p->iSize ); i--; while((p->iSize > BITVEC_NBIT) && p->iDivisor) { u32 bin = i/p->iDivisor; @@ -235,11 +232,11 @@ ** ** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage ** that BitvecClear can use to rebuilt its hash table. */ void sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){ - assert( p!=0 ); + if( p==0 ) return; assert( i>0 ); i--; while( p->iDivisor ){ u32 bin = i/p->iDivisor; i = i%p->iDivisor; @@ -346,10 +343,14 @@ pV = sqlite3_malloc( (sz+7)/8 + 1 ); pTmpSpace = sqlite3_malloc(BITVEC_SZ); if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; memset(pV, 0, (sz+7)/8 + 1); + /* NULL pBitvec tests */ + sqlite3BitvecSet(0, 1); + sqlite3BitvecClear(0, 1, pTmpSpace); + /* Run the program */ pc = 0; while( (op = aOp[pc])!=0 ){ switch( op ){ case 1: Index: src/btmutex.c ================================================================== --- src/btmutex.c +++ src/btmutex.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** -** $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 ** big and we want to break it down some. This packaged seemed like ** a good breakout. */ @@ -195,11 +193,13 @@ if( p && p->sharable ){ p->wantToLock++; if( !p->locked ){ assert( p->wantToLock==1 ); while( p->pPrev ) p = p->pPrev; - while( p->locked && p->pNext ) p = p->pNext; + /* Reason for ALWAYS: There must be at least on unlocked Btree in + ** the chain. Otherwise the !p->locked test above would have failed */ + while( p->locked && ALWAYS(p->pNext) ) p = p->pNext; for(pLater = p->pNext; pLater; pLater=pLater->pNext){ if( pLater->locked ){ unlockBtreeMutex(pLater); } } @@ -306,12 +306,16 @@ assert( !p->locked || p->wantToLock>0 ); /* We should already hold a lock on the database connection */ assert( sqlite3_mutex_held(p->db->mutex) ); + /* The Btree is sharable because only sharable Btrees are entered + ** into the array in the first place. */ + assert( p->sharable ); + p->wantToLock++; - if( !p->locked && p->sharable ){ + if( !p->locked ){ lockBtreeMutex(p); } } } @@ -322,18 +326,18 @@ int i; for(i=0; inMutex; i++){ Btree *p = pArray->aBtree[i]; /* Some basic sanity checking */ assert( i==0 || pArray->aBtree[i-1]->pBtpBt ); - assert( p->locked || !p->sharable ); + assert( p->locked ); assert( p->wantToLock>0 ); /* We should already hold a lock on the database connection */ assert( sqlite3_mutex_held(p->db->mutex) ); p->wantToLock--; - if( p->wantToLock==0 && p->locked ){ + if( p->wantToLock==0 ){ unlockBtreeMutex(p); } } } Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -7,12 +7,10 @@ ** 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. ** ************************************************************************* -** $Id: btree.c,v 1.645 2009/06/26 16:32:13 shane Exp $ -** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. ** Including a description of file format and an overview of operation. */ #include "btreeInt.h" @@ -65,15 +63,10 @@ return SQLITE_OK; } #endif -/* -** Forward declaration -*/ -static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64); - #ifdef SQLITE_OMIT_SHARED_CACHE /* ** The functions querySharedCacheTableLock(), setSharedCacheTableLock(), ** and clearAllSharedCacheTableLocks() @@ -84,15 +77,137 @@ ** So define the lock related functions as no-ops. */ #define querySharedCacheTableLock(a,b,c) SQLITE_OK #define setSharedCacheTableLock(a,b,c) SQLITE_OK #define clearAllSharedCacheTableLocks(a) + #define downgradeAllSharedCacheTableLocks(a) + #define hasSharedCacheTableLock(a,b,c,d) 1 + #define hasReadConflicts(a, b) 0 #endif #ifndef SQLITE_OMIT_SHARED_CACHE + +#ifdef SQLITE_DEBUG /* -** Query to see if btree handle p may obtain a lock of type eLock +**** This function is only used as part of an assert() statement. *** +** +** Check to see if pBtree holds the required locks to read or write to the +** table with root page iRoot. Return 1 if it does and 0 if not. +** +** For example, when writing to a table with root-page iRoot via +** Btree connection pBtree: +** +** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); +** +** When writing to an index that resides in a sharable database, the +** caller should have first obtained a lock specifying the root page of +** the corresponding table. This makes things a bit more complicated, +** as this module treats each table as a separate structure. To determine +** the table corresponding to the index being written, this +** function has to search through the database schema. +** +** Instead of a lock on the table/index rooted at page iRoot, the caller may +** hold a write-lock on the schema table (root page 1). This is also +** acceptable. +*/ +static int hasSharedCacheTableLock( + Btree *pBtree, /* Handle that must hold lock */ + Pgno iRoot, /* Root page of b-tree */ + int isIndex, /* True if iRoot is the root of an index b-tree */ + int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */ +){ + Schema *pSchema = (Schema *)pBtree->pBt->pSchema; + Pgno iTab = 0; + BtLock *pLock; + + /* If this database is not shareable, or if the client is reading + ** and has the read-uncommitted flag set, then no lock is required. + ** Return true immediately. + */ + if( (pBtree->sharable==0) + || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted)) + ){ + return 1; + } + + /* If the client is reading or writing an index and the schema is + ** not loaded, then it is too difficult to actually check to see if + ** the correct locks are held. So do not bother - just return true. + ** This case does not come up very often anyhow. + */ + if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){ + return 1; + } + + /* Figure out the root-page that the lock should be held on. For table + ** b-trees, this is just the root page of the b-tree being read or + ** written. For index b-trees, it is the root page of the associated + ** table. */ + if( isIndex ){ + HashElem *p; + for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ + Index *pIdx = (Index *)sqliteHashData(p); + if( pIdx->tnum==(int)iRoot ){ + iTab = pIdx->pTable->tnum; + } + } + }else{ + iTab = iRoot; + } + + /* Search for the required lock. Either a write-lock on root-page iTab, a + ** write-lock on the schema table, or (if the client is reading) a + ** read-lock on iTab will suffice. Return 1 if any of these are found. */ + for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ + if( pLock->pBtree==pBtree + && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) + && pLock->eLock>=eLockType + ){ + return 1; + } + } + + /* Failed to find the required lock. */ + return 0; +} +#endif /* SQLITE_DEBUG */ + +#ifdef SQLITE_DEBUG +/* +**** This function may be used as part of assert() statements only. **** +** +** Return true if it would be illegal for pBtree to write into the +** table or index rooted at iRoot because other shared connections are +** simultaneously reading that same table or index. +** +** It is illegal for pBtree to write if some other Btree object that +** shares the same BtShared object is currently reading or writing +** the iRoot table. Except, if the other Btree object has the +** read-uncommitted flag set, then it is OK for the other object to +** have a read cursor. +** +** For example, before writing to any part of the table or index +** rooted at page iRoot, one should call: +** +** assert( !hasReadConflicts(pBtree, iRoot) ); +*/ +static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ + BtCursor *p; + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ + if( p->pgnoRoot==iRoot + && p->pBtree!=pBtree + && 0==(p->pBtree->db->flags & SQLITE_ReadUncommitted) + ){ + return 1; + } + } + return 0; +} +#endif /* #ifdef SQLITE_DEBUG */ + +/* +** Query to see if Btree handle p may obtain a lock of type eLock ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return ** SQLITE_OK if the lock may be obtained (by calling ** setSharedCacheTableLock()), or SQLITE_LOCKED if not. */ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ @@ -100,19 +215,20 @@ BtLock *pIter; assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); + assert( !(p->db->flags&SQLITE_ReadUncommitted)||eLock==WRITE_LOCK||iTab==1 ); /* If requesting a write-lock, then the Btree must have an open write ** transaction on this file. And, obviously, for this to be so there ** must be an open write transaction on the file itself. */ assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); - /* This is a no-op if the shared-cache is not enabled */ + /* This routine is a no-op if the shared-cache is not enabled */ if( !p->sharable ){ return SQLITE_OK; } /* If some other connection is holding an exclusive lock, the @@ -121,51 +237,29 @@ if( pBt->pWriter!=p && pBt->isExclusive ){ sqlite3ConnectionBlocked(p->db, pBt->pWriter->db); return SQLITE_LOCKED_SHAREDCACHE; } - /* This (along with setSharedCacheTableLock()) is where - ** the ReadUncommitted flag is dealt with. - ** If the caller is querying for a read-lock on any table - ** other than the sqlite_master table (table 1) and if the ReadUncommitted - ** flag is set, then the lock granted even if there are write-locks - ** on the table. If a write-lock is requested, the ReadUncommitted flag - ** is not considered. - ** - ** In function setSharedCacheTableLock(), if a read-lock is demanded and the - ** ReadUncommitted flag is set, no entry is added to the locks list - ** (BtShared.pLock). - ** - ** To summarize: If the ReadUncommitted flag is set, then read cursors - ** on non-schema tables do not create or respect table locks. The locking - ** procedure for a write-cursor does not change. - */ - if( - 0==(p->db->flags&SQLITE_ReadUncommitted) || - eLock==WRITE_LOCK || - iTab==MASTER_ROOT - ){ - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - /* The condition (pIter->eLock!=eLock) in the following if(...) - ** statement is a simplification of: - ** - ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) - ** - ** since we know that if eLock==WRITE_LOCK, then no other connection - ** may hold a WRITE_LOCK on any table in this file (since there can - ** only be a single writer). - */ - assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); - assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); - if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ - sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); - if( eLock==WRITE_LOCK ){ - assert( p==pBt->pWriter ); - pBt->isPending = 1; - } - return SQLITE_LOCKED_SHAREDCACHE; - } + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + /* The condition (pIter->eLock!=eLock) in the following if(...) + ** statement is a simplification of: + ** + ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) + ** + ** since we know that if eLock==WRITE_LOCK, then no other connection + ** may hold a WRITE_LOCK on any table in this file (since there can + ** only be a single writer). + */ + assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); + assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); + if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ + sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); + if( eLock==WRITE_LOCK ){ + assert( p==pBt->pWriter ); + pBt->isPending = 1; + } + return SQLITE_LOCKED_SHAREDCACHE; } } return SQLITE_OK; } #endif /* !SQLITE_OMIT_SHARED_CACHE */ @@ -174,12 +268,21 @@ /* ** Add a lock on the table with root-page iTable to the shared-btree used ** by Btree handle p. Parameter eLock must be either READ_LOCK or ** WRITE_LOCK. ** -** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and -** SQLITE_NOMEM may also be returned. +** This function assumes the following: +** +** (a) The specified Btree object p is connected to a sharable +** database (one with the BtShared.sharable flag set), and +** +** (b) No other Btree objects hold a lock that conflicts +** with the requested lock (i.e. querySharedCacheTableLock() has +** already been called and returned SQLITE_OK). +** +** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM +** is returned if a malloc attempt fails. */ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pLock = 0; BtLock *pIter; @@ -186,30 +289,20 @@ assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); - /* This is a no-op if the shared-cache is not enabled */ - if( !p->sharable ){ - return SQLITE_OK; - } - - assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); - - /* If the read-uncommitted flag is set and a read-lock is requested on - ** a non-schema table, then the lock is always granted. Return early - ** without adding an entry to the BtShared.pLock list. See - ** comment in function querySharedCacheTableLock() for more info - ** on handling the ReadUncommitted flag. - */ - if( - (p->db->flags&SQLITE_ReadUncommitted) && - (eLock==READ_LOCK) && - iTable!=MASTER_ROOT - ){ - return SQLITE_OK; - } + /* A connection with the read-uncommitted flag set will never try to + ** obtain a read-lock using this function. The only read-lock obtained + ** by a connection in read-uncommitted mode is on the sqlite_master + ** table, and that lock is obtained in BtreeBeginTrans(). */ + assert( 0==(p->db->flags&SQLITE_ReadUncommitted) || eLock==WRITE_LOCK ); + + /* This function should only be called on a sharable b-tree after it + ** has been determined that no other b-tree holds a conflicting lock. */ + assert( p->sharable ); + assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); /* First search the list for an existing lock on this table. */ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->iTable==iTable && pIter->pBtree==p ){ pLock = pIter; @@ -245,13 +338,13 @@ #endif /* !SQLITE_OMIT_SHARED_CACHE */ #ifndef SQLITE_OMIT_SHARED_CACHE /* ** Release all the table locks (locks obtained via calls to -** the setSharedCacheTableLock() procedure) held by Btree handle p. +** the setSharedCacheTableLock() procedure) held by Btree object p. ** -** This function assumes that handle p has an open read or write +** This function assumes that Btree p has an open read or write ** transaction. If it does not, then the BtShared.isPending variable ** may be incorrectly cleared. */ static void clearAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; @@ -265,11 +358,14 @@ BtLock *pLock = *ppIter; assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree ); assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; - sqlite3_free(pLock); + assert( pLock->iTable!=1 || pLock==&p->lock ); + if( pLock->iTable!=1 ){ + sqlite3_free(pLock); + } }else{ ppIter = &pLock->pNext; } } @@ -277,11 +373,11 @@ if( pBt->pWriter==p ){ pBt->pWriter = 0; pBt->isExclusive = 0; pBt->isPending = 0; }else if( pBt->nTransaction==2 ){ - /* This function is called when connection p is concluding its + /* This function is called when Btree p is concluding its ** transaction. If there currently exists a writer, and p is not ** that writer, then the number of locks held by connections other ** than the writer must be about to drop to zero. In this case ** set the isPending flag to 0. ** @@ -289,18 +385,38 @@ ** be zero already. So this next line is harmless in that case. */ pBt->isPending = 0; } } + +/* +** This function changes all write-locks held by Btree p into read-locks. +*/ +static void downgradeAllSharedCacheTableLocks(Btree *p){ + BtShared *pBt = p->pBt; + if( pBt->pWriter==p ){ + BtLock *pLock; + pBt->pWriter = 0; + pBt->isExclusive = 0; + pBt->isPending = 0; + for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ + assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); + pLock->eLock = READ_LOCK; + } + } +} + #endif /* SQLITE_OMIT_SHARED_CACHE */ static void releasePage(MemPage *pPage); /* Forward reference */ /* -** Verify that the cursor holds a mutex on the BtShared +***** This routine is used inside of assert() only **** +** +** Verify that the cursor holds the mutex on its BtShared */ -#ifndef NDEBUG +#ifdef SQLITE_DEBUG static int cursorHoldsMutex(BtCursor *p){ return sqlite3_mutex_held(p->pBt->mutex); } #endif @@ -324,14 +440,45 @@ assert( sqlite3_mutex_held(pBt->mutex) ); for(p=pBt->pCursor; p; p=p->pNext){ invalidateOverflowCache(p); } } + +/* +** This function is called before modifying the contents of a table +** to invalidate any incrblob cursors that are open on the +** row or one of the rows being modified. +** +** If argument isClearTable is true, then the entire contents of the +** table is about to be deleted. In this case invalidate all incrblob +** cursors open on any row within the table with root-page pgnoRoot. +** +** Otherwise, if argument isClearTable is false, then the row with +** rowid iRow is being replaced or deleted. In this case invalidate +** only those incrblob cursors open on that specific row. +*/ +static void invalidateIncrblobCursors( + Btree *pBtree, /* The database file to check */ + i64 iRow, /* The rowid that might be changing */ + int isClearTable /* True if all rows are being deleted */ +){ + BtCursor *p; + BtShared *pBt = pBtree->pBt; + assert( sqlite3BtreeHoldsMutex(pBtree) ); + for(p=pBt->pCursor; p; p=p->pNext){ + if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){ + p->eState = CURSOR_INVALID; + } + } +} + #else + /* Stub functions when INCRBLOB is omitted */ #define invalidateOverflowCache(x) #define invalidateAllOverflowCache(x) -#endif + #define invalidateIncrblobCursors(x,y,z) +#endif /* SQLITE_OMIT_INCRBLOB */ /* ** Set bit pgno of the BtShared.pHasContent bitvec. This is called ** when a page that previously contained data becomes a free-list leaf ** page. @@ -360,24 +507,24 @@ ** to restore the database to its original configuration. ** ** The solution is the BtShared.pHasContent bitvec. Whenever a page is ** moved to become a free-list leaf page, the corresponding bit is ** set in the bitvec. Whenever a leaf page is extracted from the free-list, -** optimization 2 above is ommitted if the corresponding bit is already +** optimization 2 above is omitted if the corresponding bit is already ** set in BtShared.pHasContent. The contents of the bitvec are cleared ** at the end of every transaction. */ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ int rc = SQLITE_OK; if( !pBt->pHasContent ){ - int nPage; - rc = sqlite3PagerPagecount(pBt->pPager, &nPage); - if( rc==SQLITE_OK ){ - pBt->pHasContent = sqlite3BitvecCreate((u32)nPage); - if( !pBt->pHasContent ){ - rc = SQLITE_NOMEM; - } + int nPage = 100; + sqlite3PagerPagecount(pBt->pPager, &nPage); + /* If sqlite3PagerPagecount() fails there is no harm because the + ** nPage variable is unchanged from its default value of 100 */ + pBt->pHasContent = sqlite3BitvecCreate((u32)nPage); + if( !pBt->pHasContent ){ + rc = SQLITE_NOMEM; } } if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ rc = sqlite3BitvecSet(pBt->pHasContent, pgno); } @@ -406,27 +553,31 @@ } /* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. +** +** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID) +** prior to calling this routine. */ static int saveCursorPosition(BtCursor *pCur){ int rc; assert( CURSOR_VALID==pCur->eState ); assert( 0==pCur->pKey ); assert( cursorHoldsMutex(pCur) ); rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); + assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ /* If this is an intKey table, then the above call to BtreeKeySize() ** stores the integer key in pCur->nKey. In this case this value is ** all that is required. Otherwise, if pCur is not open on an intKey ** table, then malloc space for and store the pCur->nKey bytes of key ** data. */ - if( rc==SQLITE_OK && 0==pCur->apPage[0]->intKey){ + if( 0==pCur->apPage[0]->intKey ){ void *pKey = sqlite3Malloc( (int)pCur->nKey ); if( pKey ){ rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey); if( rc==SQLITE_OK ){ pCur->pKey = pKey; @@ -452,12 +603,12 @@ invalidateOverflowCache(pCur); return rc; } /* -** Save the positions of all cursors except pExcept open on the table -** with root-page iRoot. Usually, this is called just before cursor +** Save the positions of all cursors (except pExcept) that are open on +** the table with root-page iRoot. Usually, this is called just before cursor ** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). */ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ BtCursor *p; assert( sqlite3_mutex_held(pBt->mutex) ); @@ -481,27 +632,58 @@ assert( cursorHoldsMutex(pCur) ); sqlite3_free(pCur->pKey); pCur->pKey = 0; pCur->eState = CURSOR_INVALID; } + +/* +** In this version of BtreeMoveto, pKey is a packed index record +** such as is generated by the OP_MakeRecord opcode. Unpack the +** record and then call BtreeMovetoUnpacked() to do the work. +*/ +static int btreeMoveto( + BtCursor *pCur, /* Cursor open on the btree to be searched */ + const void *pKey, /* Packed key if the btree is an index */ + i64 nKey, /* Integer key for tables. Size of pKey for indices */ + int bias, /* Bias search to the high end */ + int *pRes /* Write search results here */ +){ + int rc; /* Status code */ + UnpackedRecord *pIdxKey; /* Unpacked index key */ + char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ + + if( pKey ){ + assert( nKey==(i64)(int)nKey ); + pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, + aSpace, sizeof(aSpace)); + if( pIdxKey==0 ) return SQLITE_NOMEM; + }else{ + pIdxKey = 0; + } + rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); + if( pKey ){ + sqlite3VdbeDeleteUnpackedRecord(pIdxKey); + } + return rc; +} /* ** Restore the cursor to the position it was in (or as close to as possible) ** when saveCursorPosition() was called. Note that this call deletes the ** saved position info stored by saveCursorPosition(), so there can be ** at most one effective restoreCursorPosition() call after each ** saveCursorPosition(). */ -int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur){ +static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; assert( cursorHoldsMutex(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ - return pCur->skip; + return pCur->skipNext; } pCur->eState = CURSOR_INVALID; - rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip); + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skipNext); if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); } @@ -508,11 +690,11 @@ return rc; } #define restoreCursorPosition(p) \ (p->eState>=CURSOR_REQUIRESEEK ? \ - sqlite3BtreeRestoreCursorPosition(p) : \ + btreeRestoreCursorPosition(p) : \ SQLITE_OK) /* ** Determine whether or not a cursor has moved from the position it ** was last placed at. Cursors can move when the row they are pointing @@ -527,11 +709,11 @@ rc = restoreCursorPosition(pCur); if( rc ){ *pHasMoved = 1; return rc; } - if( pCur->eState!=CURSOR_VALID || pCur->skip!=0 ){ + if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){ *pHasMoved = 1; }else{ *pHasMoved = 0; } return SQLITE_OK; @@ -559,49 +741,57 @@ /* ** Write an entry into the pointer map. ** ** This routine updates the pointer map entry for page number 'key' ** so that it maps to type 'eType' and parent page number 'pgno'. -** An error code is returned if something goes wrong, otherwise SQLITE_OK. +** +** If *pRC is initially non-zero (non-SQLITE_OK) then this routine is +** a no-op. If an error occurs, the appropriate error code is written +** into *pRC. */ -static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){ +static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ DbPage *pDbPage; /* The pointer map page */ u8 *pPtrmap; /* The pointer map data */ Pgno iPtrmap; /* The pointer map page number */ int offset; /* Offset in pointer map page */ - int rc; + int rc; /* Return code from subfunctions */ + + if( *pRC ) return; assert( sqlite3_mutex_held(pBt->mutex) ); /* The master-journal page number must never be used as a pointer map page */ assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); assert( pBt->autoVacuum ); if( key==0 ){ - return SQLITE_CORRUPT_BKPT; + *pRC = SQLITE_CORRUPT_BKPT; + return; } iPtrmap = PTRMAP_PAGENO(pBt, key); rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage); if( rc!=SQLITE_OK ){ - return rc; + *pRC = rc; + return; } offset = PTRMAP_PTROFFSET(iPtrmap, key); if( offset<0 ){ - return SQLITE_CORRUPT_BKPT; + *pRC = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; } pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); - rc = sqlite3PagerWrite(pDbPage); + *pRC= rc = sqlite3PagerWrite(pDbPage); if( rc==SQLITE_OK ){ pPtrmap[offset] = eType; put4byte(&pPtrmap[offset+1], parent); } } +ptrmap_exit: sqlite3PagerUnref(pDbPage); - return rc; } /* ** Read an entry from the pointer map. ** @@ -634,12 +824,13 @@ if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_BKPT; return SQLITE_OK; } #else /* if defined SQLITE_OMIT_AUTOVACUUM */ - #define ptrmapPut(w,x,y,z) SQLITE_OK + #define ptrmapPut(w,x,y,z,rc) #define ptrmapGet(w,x,y,z) SQLITE_OK + #define ptrmapPutOvflPtr(x, y, rc) #endif /* ** Given a btree page and a cell index (0 means the first cell on ** the page, 1 means the second cell, and so forth) return a pointer @@ -650,11 +841,11 @@ #define findCell(P,I) \ ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)]))) /* ** This a more complex version of findCell() that works for -** pages that do contain overflow cells. See insert +** pages that do contain overflow cells. */ static u8 *findOverflowCell(MemPage *pPage, int iCell){ int i; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); for(i=pPage->nOverflow-1; i>=0; i--){ @@ -672,18 +863,18 @@ return findCell(pPage, iCell); } /* ** Parse a cell content block and fill in the CellInfo structure. There -** are two versions of this function. sqlite3BtreeParseCell() takes a -** cell index as the second argument and sqlite3BtreeParseCellPtr() +** are two versions of this function. btreeParseCell() takes a +** cell index as the second argument and btreeParseCellPtr() ** takes a pointer to the body of the cell as its second argument. ** ** Within this file, the parseCell() macro can be called instead of -** sqlite3BtreeParseCellPtr(). Using some compilers, this will be faster. +** btreeParseCellPtr(). Using some compilers, this will be faster. */ -void sqlite3BtreeParseCellPtr( +static void btreeParseCellPtr( MemPage *pPage, /* Page containing the cell */ u8 *pCell, /* Pointer to the cell text. */ CellInfo *pInfo /* Fill in this structure */ ){ u16 n; /* Number bytes in cell content header */ @@ -708,10 +899,12 @@ n += getVarint32(&pCell[n], nPayload); pInfo->nKey = nPayload; } pInfo->nPayload = nPayload; pInfo->nHeader = n; + testcase( nPayload==pPage->maxLocal ); + testcase( nPayload==pPage->maxLocal+1 ); if( likely(nPayload<=pPage->maxLocal) ){ /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. */ int nSize; /* Total size of cell content in bytes */ @@ -737,10 +930,12 @@ int surplus; /* Overflow payload available for local storage */ minLocal = pPage->minLocal; maxLocal = pPage->maxLocal; surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4); + testcase( surplus==maxLocal ); + testcase( surplus==maxLocal+1 ); if( surplus <= maxLocal ){ pInfo->nLocal = (u16)surplus; }else{ pInfo->nLocal = (u16)minLocal; } @@ -747,12 +942,12 @@ pInfo->iOverflow = (u16)(pInfo->nLocal + n); pInfo->nSize = pInfo->iOverflow + 4; } } #define parseCell(pPage, iCell, pInfo) \ - sqlite3BtreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) -void sqlite3BtreeParseCell( + btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) +static void btreeParseCell( MemPage *pPage, /* Page containing the cell */ int iCell, /* The cell index. First cell is 0 */ CellInfo *pInfo /* Fill in this structure */ ){ parseCell(pPage, iCell, pInfo); @@ -772,11 +967,11 @@ /* The value returned by this function should always be the same as ** the (CellInfo.nSize) value found by doing a full parse of the ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of ** this function verifies that this invariant is not violated. */ CellInfo debuginfo; - sqlite3BtreeParseCellPtr(pPage, pCell, &debuginfo); + btreeParseCellPtr(pPage, pCell, &debuginfo); #endif if( pPage->intKey ){ u8 *pEnd; if( pPage->hasData ){ @@ -792,13 +987,17 @@ while( (*pIter++)&0x80 && pItermaxLocal ); + testcase( nSize==pPage->maxLocal+1 ); if( nSize>pPage->maxLocal ){ int minLocal = pPage->minLocal; nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); + testcase( nSize==pPage->maxLocal+1 ); if( nSize>pPage->maxLocal ){ nSize = minLocal; } nSize += 4; } @@ -810,11 +1009,14 @@ } assert( nSize==debuginfo.nSize ); return (u16)nSize; } -#ifndef NDEBUG + +#ifdef SQLITE_DEBUG +/* This variation on cellSizePtr() is used inside of assert() statements +** only. */ static u16 cellSize(MemPage *pPage, int iCell){ return cellSizePtr(pPage, findCell(pPage, iCell)); } #endif @@ -822,20 +1024,20 @@ /* ** If the cell pCell, part of page pPage contains a pointer ** to an overflow page, insert an entry into the pointer-map ** for the overflow page. */ -static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){ +static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){ CellInfo info; + if( *pRC ) return; assert( pCell!=0 ); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload ); if( info.iOverflow ){ Pgno ovfl = get4byte(&pCell[info.iOverflow]); - return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno); + ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } - return SQLITE_OK; } #endif /* @@ -845,19 +1047,21 @@ ** pointer array and the cell content area. */ static int defragmentPage(MemPage *pPage){ int i; /* Loop counter */ int pc; /* Address of a i-th cell */ - int addr; /* Offset of first byte after cell pointer array */ int hdr; /* Offset to the page header */ int size; /* Size of a cell */ int usableSize; /* Number of usable bytes on a page */ int cellOffset; /* Offset to the cell pointer array */ int cbrk; /* Offset to the cell content area */ int nCell; /* Number of cells on the page */ unsigned char *data; /* The page data */ unsigned char *temp; /* Temp area for cell content */ + int iCellFirst; /* First allowable cell index */ + int iCellLast; /* Last possible cell index */ + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt!=0 ); assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE ); assert( pPage->nOverflow==0 ); @@ -870,88 +1074,112 @@ assert( nCell==get2byte(&data[hdr+3]) ); usableSize = pPage->pBt->usableSize; cbrk = get2byte(&data[hdr+5]); memcpy(&temp[cbrk], &data[cbrk], usableSize - cbrk); cbrk = usableSize; + iCellFirst = cellOffset + 2*nCell; + iCellLast = usableSize - 4; for(i=0; i=usableSize ){ + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); +#if !defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) + /* These conditions have already been verified in btreeInitPage() + ** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined + */ + if( pciCellLast ){ return SQLITE_CORRUPT_BKPT; } +#endif + assert( pc>=iCellFirst && pc<=iCellLast ); size = cellSizePtr(pPage, &temp[pc]); cbrk -= size; - if( cbrkusableSize ){ +#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) + if( cbrkusableSize ){ return SQLITE_CORRUPT_BKPT; } - assert( cbrk+size<=usableSize && cbrk>=0 ); +#endif + assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); + testcase( cbrk+size==usableSize ); + testcase( pc+size==usableSize ); memcpy(&data[cbrk], &temp[pc], size); put2byte(pAddr, cbrk); } - assert( cbrk>=cellOffset+2*nCell ); + assert( cbrk>=iCellFirst ); put2byte(&data[hdr+5], cbrk); data[hdr+1] = 0; data[hdr+2] = 0; data[hdr+7] = 0; - addr = cellOffset+2*nCell; - memset(&data[addr], 0, cbrk-addr); + memset(&data[iCellFirst], 0, cbrk-iCellFirst); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - if( cbrk-addr!=pPage->nFree ){ + if( cbrk-iCellFirst!=pPage->nFree ){ return SQLITE_CORRUPT_BKPT; } return SQLITE_OK; } /* ** Allocate nByte bytes of space from within the B-Tree page passed -** as the first argument. Return the index into pPage->aData[] of the -** first byte of allocated space. -** -** The caller guarantees that the space between the end of the cell-offset -** array and the start of the cell-content area is at least nByte bytes -** in size. So this routine can never fail. -** -** If there are already 60 or more bytes of fragments within the page, -** the page is defragmented before returning. If this were not done there -** is a chance that the number of fragmented bytes could eventually -** overflow the single-byte field of the page-header in which this value -** is stored. -*/ -static int allocateSpace(MemPage *pPage, int nByte){ +** as the first argument. Write into *pIdx the index into pPage->aData[] +** of the first byte of allocated space. Return either SQLITE_OK or +** an error code (usually SQLITE_CORRUPT). +** +** The caller guarantees that there is sufficient space to make the +** allocation. This routine might need to defragment in order to bring +** all the space together, however. This routine will avoid using +** the first two bytes past the cell pointer area since presumably this +** allocation is being made in order to insert a new cell, so we will +** also end up needing a new cell pointer. +*/ +static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ u8 * const data = pPage->aData; /* Local cache of pPage->aData */ int nFrag; /* Number of fragmented bytes on pPage */ - int top; + int top; /* First byte of cell content area */ + int gap; /* First byte of gap between cell pointers and cell content */ + int rc; /* Integer return code */ assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( nByte>=0 ); /* Minimum cell size is 4 */ assert( pPage->nFree>=nByte ); assert( pPage->nOverflow==0 ); - - /* Assert that the space between the cell-offset array and the - ** cell-content area is greater than nByte bytes. - */ - assert( nByte <= ( - get2byte(&data[hdr+5])-(hdr+8+(pPage->leaf?0:4)+2*get2byte(&data[hdr+3])) - )); + assert( nBytepBt->usableSize-8 ); nFrag = data[hdr+7]; + assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); + gap = pPage->cellOffset + 2*pPage->nCell; + top = get2byte(&data[hdr+5]); + if( gap>top ) return SQLITE_CORRUPT_BKPT; + testcase( gap+2==top ); + testcase( gap+1==top ); + testcase( gap==top ); + if( nFrag>=60 ){ - defragmentPage(pPage); - }else{ + /* Always defragment highly fragmented pages */ + rc = defragmentPage(pPage); + if( rc ) return rc; + top = get2byte(&data[hdr+5]); + }else if( gap+2<=top ){ /* Search the freelist looking for a free slot big enough to satisfy ** the request. The allocation is made from the first free slot in ** the list that is large enough to accomadate it. */ int pc, addr; for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){ int size = get2byte(&data[pc+2]); /* Size of free slot */ if( size>=nByte ){ int x = size - nByte; + testcase( x==4 ); + testcase( x==3 ); if( x<4 ){ /* Remove the slot from the free-list. Update the number of ** fragmented bytes within the page. */ memcpy(&data[addr], &data[pc], 2); data[hdr+7] = (u8)(nFrag + x); @@ -958,21 +1186,39 @@ }else{ /* The slot remains on the free-list. Reduce its size to account ** for the portion used by the new allocation. */ put2byte(&data[pc+2], x); } - return pc + x; + *pIdx = pc + x; + return SQLITE_OK; } } } + + /* Check to make sure there is enough space in the gap to satisfy + ** the allocation. If not, defragment. + */ + testcase( gap+2+nByte==top ); + if( gap+2+nByte>top ){ + rc = defragmentPage(pPage); + if( rc ) return rc; + top = get2byte(&data[hdr+5]); + assert( gap+nByte<=top ); + } + /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. + ** and the cell content area. The btreeInitPage() call has already + ** validated the freelist. Given that the freelist is valid, there + ** is no way that the allocation can extend off the end of the page. + ** The assert() below verifies the previous sentence. */ - top = get2byte(&data[hdr+5]) - nByte; + top -= nByte; put2byte(&data[hdr+5], top); - return top; + assert( top+nByte <= pPage->pBt->usableSize ); + *pIdx = top; + return SQLITE_OK; } /* ** Return a section of the pPage->aData to the freelist. ** The first byte of the new free block is pPage->aDisk[start] @@ -981,15 +1227,16 @@ ** Most of the effort here is involved in coalesing adjacent ** free blocks into a single big free block. */ static int freeSpace(MemPage *pPage, int start, int size){ int addr, pbegin, hdr; + int iLast; /* Largest possible freeblock offset */ unsigned char *data = pPage->aData; assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) ); + assert( start>=pPage->hdrOffset+6+pPage->childPtrSize ); assert( (start + size)<=pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( size>=0 ); /* Minimum cell size is 4 */ #ifdef SQLITE_SECURE_DELETE @@ -996,43 +1243,52 @@ /* Overwrite deleted information with zeros when the SECURE_DELETE ** option is enabled at compile-time */ memset(&data[start], 0, size); #endif - /* Add the space back into the linked list of freeblocks */ + /* Add the space back into the linked list of freeblocks. Note that + ** even though the freeblock list was checked by btreeInitPage(), + ** btreeInitPage() did not detect overlapping cells or + ** freeblocks that overlapped cells. Nor does it detect when the + ** cell content area exceeds the value in the page header. If these + ** situations arise, then subsequent insert operations might corrupt + ** the freelist. So we do need to check for corruption while scanning + ** the freelist. + */ hdr = pPage->hdrOffset; addr = hdr + 1; + iLast = pPage->pBt->usableSize - 4; + assert( start<=iLast ); while( (pbegin = get2byte(&data[addr]))0 ){ - assert( pbegin<=pPage->pBt->usableSize-4 ); - if( pbegin<=addr ) { + if( pbeginpPage->pBt->usableSize-4 ) { + if( pbegin>iLast ){ return SQLITE_CORRUPT_BKPT; } assert( pbegin>addr || pbegin==0 ); put2byte(&data[addr], start); put2byte(&data[start], pbegin); put2byte(&data[start+2], size); pPage->nFree = pPage->nFree + (u16)size; /* Coalesce adjacent free blocks */ - addr = pPage->hdrOffset + 1; + addr = hdr + 1; while( (pbegin = get2byte(&data[addr]))>0 ){ int pnext, psize, x; assert( pbegin>addr ); assert( pbegin<=pPage->pBt->usableSize-4 ); pnext = get2byte(&data[pbegin]); psize = get2byte(&data[pbegin+2]); if( pbegin + psize + 3 >= pnext && pnext>0 ){ int frag = pnext - (pbegin+psize); - if( (frag<0) || (frag>(int)data[pPage->hdrOffset+7]) ){ + if( (frag<0) || (frag>(int)data[hdr+7]) ){ return SQLITE_CORRUPT_BKPT; } - data[pPage->hdrOffset+7] -= (u8)frag; + data[hdr+7] -= (u8)frag; x = get2byte(&data[pnext]); put2byte(&data[pbegin], x); x = pnext + get2byte(&data[pnext+2]) - pbegin; put2byte(&data[pbegin+2], x); }else{ @@ -1096,11 +1352,11 @@ ** not contain a well-formed database page, then return ** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not ** guarantee that the page is well-formed. It only shows that ** we failed to detect any corruption. */ -int sqlite3BtreeInitPage(MemPage *pPage){ +static int btreeInitPage(MemPage *pPage){ assert( pPage->pBt!=0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) ); assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) ); @@ -1113,10 +1369,12 @@ BtShared *pBt; /* The main btree structure */ u16 usableSize; /* Amount of usable space on each page */ u16 cellOffset; /* Offset from start of page to first cell pointer */ u16 nFree; /* Number of unused bytes on the page */ u16 top; /* First byte of the cell content area */ + int iCellFirst; /* First allowable cell or freeblock offset */ + int iCellLast; /* Last possible cell or freeblock offset */ pBt = pPage->pBt; hdr = pPage->hdrOffset; data = pPage->aData; @@ -1130,54 +1388,58 @@ pPage->nCell = get2byte(&data[hdr+3]); if( pPage->nCell>MX_CELL(pBt) ){ /* To many cells for a single page. The page must be corrupt */ return SQLITE_CORRUPT_BKPT; } + testcase( pPage->nCell==MX_CELL(pBt) ); - /* A malformed database page might cause use to read past the end + /* A malformed database page might cause us to read past the end ** of page when parsing a cell. ** ** The following block of code checks early to see if a cell extends ** past the end of a page boundary and causes SQLITE_CORRUPT to be ** returned if it does. */ + iCellFirst = cellOffset + 2*pPage->nCell; + iCellLast = usableSize - 4; #if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) { - int iCellFirst; /* First allowable cell index */ - int iCellLast; /* Last possible cell index */ int i; /* Index into the cell pointer array */ int sz; /* Size of a cell */ - iCellFirst = cellOffset + 2*pPage->nCell; - iCellLast = usableSize - 4; if( !pPage->leaf ) iCellLast--; for(i=0; inCell; i++){ pc = get2byte(&data[cellOffset+i*2]); + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); if( pciCellLast ){ return SQLITE_CORRUPT_BKPT; } sz = cellSizePtr(pPage, &data[pc]); + testcase( pc+sz==usableSize ); if( pc+sz>usableSize ){ return SQLITE_CORRUPT_BKPT; } } + if( !pPage->leaf ) iCellLast++; } #endif /* Compute the total free space on the page */ pc = get2byte(&data[hdr+1]); nFree = data[hdr+7] + top; while( pc>0 ){ u16 next, size; - if( pc>usableSize-4 ){ - /* Free block is off the page */ + if( pciCellLast ){ + /* Start of free block is off the page */ return SQLITE_CORRUPT_BKPT; } next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); - if( next>0 && next<=pc+size+3 ){ - /* Free blocks must be in accending order */ + if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){ + /* Free blocks must be in ascending order. And the last byte of + ** the free-block must lie on the database page. */ return SQLITE_CORRUPT_BKPT; } nFree = nFree + size; pc = next; } @@ -1190,32 +1452,11 @@ ** area, according to the page header, lies within the page. */ if( nFree>usableSize ){ return SQLITE_CORRUPT_BKPT; } - pPage->nFree = nFree - (cellOffset + 2*pPage->nCell); - -#if 0 - /* Check that all the offsets in the cell offset array are within range. - ** - ** Omitting this consistency check and using the pPage->maskPage mask - ** to prevent overrunning the page buffer in findCell() results in a - ** 2.5% performance gain. - */ - { - u8 *pOff; /* Iterator used to check all cell offsets are in range */ - u8 *pEnd; /* Pointer to end of cell offset array */ - u8 mask; /* Mask of bits that must be zero in MSB of cell offsets */ - mask = ~(((u8)(pBt->pageSize>>8))-1); - pEnd = &data[cellOffset + pPage->nCell*2]; - for(pOff=&data[cellOffset]; pOff!=pEnd && !((*pOff)&mask); pOff+=2); - if( pOff!=pEnd ){ - return SQLITE_CORRUPT_BKPT; - } - } -#endif - + pPage->nFree = (u16)(nFree - iCellFirst); pPage->isInit = 1; } return SQLITE_OK; } @@ -1275,11 +1516,11 @@ ** to fetch the content. Just fill in the content with zeros for now. ** If in the future we call sqlite3PagerWrite() on this page, that ** means we have started to be concerned about content and the disk ** read should occur at that point. */ -int sqlite3BtreeGetPage( +static int btreeGetPage( BtShared *pBt, /* The btree */ Pgno pgno, /* Number of the page to fetch */ MemPage **ppPage, /* Return the page in this parameter */ int noContent /* Do not load page content if true */ ){ @@ -1320,58 +1561,48 @@ assert( rc==SQLITE_OK || nPage==-1 ); return (Pgno)nPage; } /* -** Get a page from the pager and initialize it. This routine -** is just a convenience wrapper around separate calls to -** sqlite3BtreeGetPage() and sqlite3BtreeInitPage(). +** Get a page from the pager and initialize it. This routine is just a +** convenience wrapper around separate calls to btreeGetPage() and +** btreeInitPage(). +** +** If an error occurs, then the value *ppPage is set to is undefined. It +** may remain unchanged, or it may be set to an invalid value. */ static int getAndInitPage( BtShared *pBt, /* The database file */ Pgno pgno, /* Number of the page to get */ MemPage **ppPage /* Write the page pointer here */ ){ int rc; - MemPage *pPage; - + TESTONLY( Pgno iLastPg = pagerPagecount(pBt); ) assert( sqlite3_mutex_held(pBt->mutex) ); - if( pgno==0 ){ - return SQLITE_CORRUPT_BKPT; - } - - /* It is often the case that the page we want is already in cache. - ** If so, get it directly. This saves us from having to call - ** pagerPagecount() to make sure pgno is within limits, which results - ** in a measureable performance improvements. - */ - *ppPage = pPage = btreePageLookup(pBt, pgno); - if( pPage ){ - /* Page is already in cache */ - rc = SQLITE_OK; - }else{ - /* Page not in cache. Acquire it. */ - if( pgno>pagerPagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; - } - rc = sqlite3BtreeGetPage(pBt, pgno, ppPage, 0); - if( rc ) return rc; - pPage = *ppPage; - } - if( !pPage->isInit ){ - rc = sqlite3BtreeInitPage(pPage); - } - if( rc!=SQLITE_OK ){ - releasePage(pPage); - *ppPage = 0; - } + + rc = btreeGetPage(pBt, pgno, ppPage, 0); + if( rc==SQLITE_OK ){ + rc = btreeInitPage(*ppPage); + if( rc!=SQLITE_OK ){ + releasePage(*ppPage); + } + } + + /* If the requested page number was either 0 or greater than the page + ** number of the last page in the database, this function should return + ** SQLITE_CORRUPT or some other error (i.e. SQLITE_FULL). Check that this + ** is the case. */ + assert( (pgno>0 && pgno<=iLastPg) || rc!=SQLITE_OK ); + testcase( pgno==0 ); + testcase( pgno==iLastPg ); + return rc; } /* ** Release a MemPage. This should be called once for each prior -** call to sqlite3BtreeGetPage. +** call to btreeGetPage. */ static void releasePage(MemPage *pPage){ if( pPage ){ assert( pPage->nOverflow==0 || sqlite3PagerPageRefcount(pPage->pDbPage)>1 ); assert( pPage->aData ); @@ -1399,15 +1630,15 @@ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); pPage->isInit = 0; if( sqlite3PagerPageRefcount(pData)>1 ){ /* pPage might not be a btree page; it might be an overflow page ** or ptrmap page or a free page. In those cases, the following - ** call to sqlite3BtreeInitPage() will likely return SQLITE_CORRUPT. + ** call to btreeInitPage() will likely return SQLITE_CORRUPT. ** But no harm is done by this. And it is very important that - ** sqlite3BtreeInitPage() be called on every btree page so we make + ** btreeInitPage() be called on every btree page so we make ** the call for every page that comes in for re-initing. */ - sqlite3BtreeInitPage(pPage); + btreeInitPage(pPage); } } } /* @@ -1471,23 +1702,26 @@ if( !p ){ return SQLITE_NOMEM; } p->inTrans = TRANS_NONE; p->db = db; +#ifndef SQLITE_OMIT_SHARED_CACHE + p->lock.pBtree = p; + p->lock.iTable = 1; +#endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* ** If this Btree is a candidate for shared cache, try to find an ** existing BtShared object that we can share with */ if( isMemdb==0 && zFilename && zFilename[0] ){ - if( sqlite3GlobalConfig.sharedCacheEnabled ){ + if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = sqlite3Malloc(nFullPathname); sqlite3_mutex *mutexShared; p->sharable = 1; - db->flags |= SQLITE_SharedCache; if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; } sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); @@ -1546,11 +1780,11 @@ if( pBt==0 ){ rc = SQLITE_NOMEM; goto btree_open_out; } rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, - EXTRA_SIZE, flags, vfsFlags); + EXTRA_SIZE, flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; @@ -1557,11 +1791,10 @@ } pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; - sqlite3PagerSetReiniter(pBt->pPager, pageReinit); pBt->pCursor = 0; pBt->pPage1 = 0; pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); pBt->pageSize = get2byte(&zDbHeader[16]); if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE @@ -1982,11 +2215,13 @@ MemPage *pPage1; int nPage; assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); - rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0); + rc = sqlite3PagerSharedLock(pBt->pPager); + if( rc!=SQLITE_OK ) return rc; + rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ @@ -2035,12 +2270,11 @@ pBt->usableSize = (u16)usableSize; pBt->pageSize = (u16)pageSize; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); - if( rc ) goto page1_init_failed; - return SQLITE_OK; + return rc; } if( usableSize<480 ){ goto page1_init_failed; } pBt->pageSize = (u16)pageSize; @@ -2076,67 +2310,47 @@ releasePage(pPage1); pBt->pPage1 = 0; return rc; } -/* -** This routine works like lockBtree() except that it also invokes the -** busy callback if there is lock contention. -*/ -static int lockBtreeWithRetry(Btree *pRef){ - int rc = SQLITE_OK; - - assert( sqlite3BtreeHoldsMutex(pRef) ); - if( pRef->inTrans==TRANS_NONE ){ - u8 inTransaction = pRef->pBt->inTransaction; - btreeIntegrity(pRef); - rc = sqlite3BtreeBeginTrans(pRef, 0); - pRef->pBt->inTransaction = inTransaction; - pRef->inTrans = TRANS_NONE; - if( rc==SQLITE_OK ){ - pRef->pBt->nTransaction--; - } - btreeIntegrity(pRef); - } - return rc; -} - - /* ** If there are no outstanding cursors and we are not in the middle ** of a transaction but there is a read lock on the database, then ** this routine unrefs the first page of the database file which ** has the effect of releasing the read lock. ** -** If there are any outstanding cursors, this routine is a no-op. -** ** If there is a transaction in progress, this routine is a no-op. */ static void unlockBtreeIfUnused(BtShared *pBt){ assert( sqlite3_mutex_held(pBt->mutex) ); - if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ + assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE ); + if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ assert( pBt->pPage1->aData ); assert( sqlite3PagerRefcount(pBt->pPager)==1 ); assert( pBt->pPage1->aData ); releasePage(pBt->pPage1); pBt->pPage1 = 0; } } /* -** Create a new database by initializing the first page of the -** file. +** If pBt points to an empty file then convert that empty file +** into a new empty database by initializing the first page of +** the database. */ static int newDatabase(BtShared *pBt){ MemPage *pP1; unsigned char *data; int rc; int nPage; assert( sqlite3_mutex_held(pBt->mutex) ); + /* The database size has already been measured and cached, so failure + ** is impossible here. If the original size measurement failed, then + ** processing aborts before entering this routine. */ rc = sqlite3PagerPagecount(pBt->pPager, &nPage); - if( rc!=SQLITE_OK || nPage>0 ){ + if( NEVER(rc!=SQLITE_OK) || nPage>0 ){ return rc; } pP1 = pBt->pPage1; assert( pP1!=0 ); data = pP1->aData; @@ -2242,10 +2456,16 @@ rc = SQLITE_LOCKED_SHAREDCACHE; goto trans_begun; } #endif + /* Any read-only or read-write transaction implies a read-lock on + ** page 1. So if some other shared-cache client already has a write-lock + ** on page 1, the transaction cannot be opened. */ + rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); + if( SQLITE_OK!=rc ) goto trans_begun; + do { /* Call lockBtree() until either pBt->pPage1 is populated or ** lockBtree() returns something other than SQLITE_OK. lockBtree() ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after ** reading page 1 it discovers that the page-size of the database @@ -2272,10 +2492,18 @@ btreeInvokeBusyHandler(pBt) ); if( rc==SQLITE_OK ){ if( p->inTrans==TRANS_NONE ){ pBt->nTransaction++; +#ifndef SQLITE_OMIT_SHARED_CACHE + if( p->sharable ){ + assert( p->lock.pBtree==p && p->lock.iTable==1 ); + p->lock.eLock = READ_LOCK; + p->lock.pNext = pBt->pLock; + pBt->pLock = &p->lock; + } +#endif } p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); if( p->inTrans>pBt->inTransaction ){ pBt->inTransaction = p->inTrans; } @@ -2317,46 +2545,41 @@ BtShared *pBt = pPage->pBt; u8 isInitOrig = pPage->isInit; Pgno pgno = pPage->pgno; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - rc = sqlite3BtreeInitPage(pPage); + rc = btreeInitPage(pPage); if( rc!=SQLITE_OK ){ goto set_child_ptrmaps_out; } nCell = pPage->nCell; for(i=0; ileaf ){ Pgno childPgno = get4byte(pCell); - rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); - if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out; + ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); } } if( !pPage->leaf ){ Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); + ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); } set_child_ptrmaps_out: pPage->isInit = isInitOrig; return rc; } /* -** Somewhere on pPage, which is guaranteed to be a btree page, not an overflow -** page, is a pointer to page iFrom. Modify this pointer so that it points to -** iTo. Parameter eType describes the type of pointer to be modified, as -** follows: +** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so +** that it points to iTo. Parameter eType describes the type of pointer to +** be modified, as follows: ** ** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child ** page of pPage. ** ** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow @@ -2377,18 +2600,18 @@ }else{ u8 isInitOrig = pPage->isInit; int i; int nCell; - sqlite3BtreeInitPage(pPage); + btreeInitPage(pPage); nCell = pPage->nCell; for(i=0; ipgno +** can be written to. The caller has already promised not to write to that +** page. */ static int relocatePage( BtShared *pBt, /* Btree */ MemPage *pDbPage, /* Open page to move */ u8 eType, /* Pointer map 'type' entry for pDbPage */ Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ Pgno iFreePage, /* The location to move pDbPage to */ - int isCommit + int isCommit /* isCommit flag passed to sqlite3PagerMovepage */ ){ MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ Pgno iDbPage = pDbPage->pgno; Pager *pPager = pBt->pPager; int rc; @@ -2460,11 +2688,11 @@ return rc; } }else{ Pgno nextOvfl = get4byte(pDbPage->aData); if( nextOvfl!=0 ){ - rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage); + ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage, &rc); if( rc!=SQLITE_OK ){ return rc; } } } @@ -2472,11 +2700,11 @@ /* Fix the database pointer on page iPtrPage that pointed at iDbPage so ** that it points at iFreePage. Also fix the pointer map entry for ** iPtrPage. */ if( eType!=PTRMAP_ROOTPAGE ){ - rc = sqlite3BtreeGetPage(pBt, iPtrPage, &pPtrPage, 0); + rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pPtrPage->pDbPage); if( rc!=SQLITE_OK ){ @@ -2484,11 +2712,11 @@ return rc; } rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); releasePage(pPtrPage); if( rc==SQLITE_OK ){ - rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); + ptrmapPut(pBt, iFreePage, eType, iPtrPage, &rc); } } return rc; } @@ -2502,15 +2730,18 @@ ** ** More specificly, this function attempts to re-organize the ** database so that the last page of the file currently in use ** is no longer in use. ** -** If the nFin parameter is non-zero, the implementation assumes +** If the nFin parameter is non-zero, this function assumes ** that the caller will keep calling incrVacuumStep() until ** it returns SQLITE_DONE or an error, and that nFin is the ** number of pages the database file will contain after this -** process is complete. +** process is complete. If nFin is zero, it is assumed that +** incrVacuumStep() will be called a finite amount of times +** which may or may not empty the freelist. A full autovacuum +** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0. */ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno nFreeList; /* Number of pages still on the free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); @@ -2552,11 +2783,11 @@ } } else { Pgno iFreePg; /* Index of free page to move pLastPg to */ MemPage *pLastPg; - rc = sqlite3BtreeGetPage(pBt, iLastPg, &pLastPg, 0); + rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); if( rc!=SQLITE_OK ){ return rc; } /* If nFin is zero, this loop runs exactly once and page pLastPg @@ -2591,11 +2822,11 @@ if( nFin==0 ){ iLastPg--; while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ if( PTRMAP_ISPAGE(pBt, iLastPg) ){ MemPage *pPg; - int rc = sqlite3BtreeGetPage(pBt, iLastPg, &pPg, 0); + int rc = btreeGetPage(pBt, iLastPg, &pPg, 0); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); @@ -2650,27 +2881,29 @@ assert( sqlite3_mutex_held(pBt->mutex) ); invalidateAllOverflowCache(pBt); assert(pBt->autoVacuum); if( !pBt->incrVacuum ){ - Pgno nFin; - Pgno nFree; - Pgno nPtrmap; - Pgno iFree; - const int pgsz = pBt->pageSize; - Pgno nOrig = pagerPagecount(pBt); + Pgno nFin; /* Number of pages in database after autovacuuming */ + Pgno nFree; /* Number of pages on the freelist initially */ + Pgno nPtrmap; /* Number of PtrMap pages to be freed */ + Pgno iFree; /* The next page to be freed */ + int nEntry; /* Number of entries on one ptrmap page */ + Pgno nOrig; /* Database size before freeing */ + nOrig = pagerPagecount(pBt); if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ /* It is not possible to create a database for which the final page ** is either a pointer-map page or the pending-byte page. If one ** is encountered, this indicates corruption. */ return SQLITE_CORRUPT_BKPT; } nFree = get4byte(&pBt->pPage1->aData[36]); - nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+pgsz/5)/(pgsz/5); + nEntry = pBt->usableSize/5; + nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; nFin = nOrig - nFree - nPtrmap; if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinpPager, zMaster, 0); sqlite3BtreeLeave(p); } return rc; } + +/* +** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback() +** at the conclusion of a transaction. +*/ +static void btreeEndTransaction(Btree *p){ + BtShared *pBt = p->pBt; + assert( sqlite3BtreeHoldsMutex(p) ); + + btreeClearHasContent(pBt); + if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){ + /* If there are other active statements that belong to this database + ** handle, downgrade to a read-only transaction. The other statements + ** may still be reading from the database. */ + downgradeAllSharedCacheTableLocks(p); + p->inTrans = TRANS_READ; + }else{ + /* If the handle had any kind of transaction open, decrement the + ** transaction count of the shared btree. If the transaction count + ** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused() + ** call below will unlock the pager. */ + if( p->inTrans!=TRANS_NONE ){ + clearAllSharedCacheTableLocks(p); + pBt->nTransaction--; + if( 0==pBt->nTransaction ){ + pBt->inTransaction = TRANS_NONE; + } + } + + /* Set the current transaction state to TRANS_NONE and unlock the + ** pager if this call closed the only read or write transaction. */ + p->inTrans = TRANS_NONE; + unlockBtreeIfUnused(pBt); + } + + btreeIntegrity(p); +} /* ** Commit the transaction currently in progress. ** ** This routine implements the second phase of a 2-phase commit. The @@ -2781,31 +3051,11 @@ return rc; } pBt->inTransaction = TRANS_READ; } - /* If the handle has any kind of transaction open, decrement the transaction - ** count of the shared btree. If the transaction count reaches 0, set - ** the shared state to TRANS_NONE. The unlockBtreeIfUnused() call below - ** will unlock the pager. - */ - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - /* Set the current transaction state to TRANS_NONE and unlock - ** the pager if this call closed the only read or write transaction. - */ - btreeClearHasContent(pBt); - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - - btreeIntegrity(p); + btreeEndTransaction(p); sqlite3BtreeLeave(p); return SQLITE_OK; } /* @@ -2865,11 +3115,11 @@ sqlite3BtreeEnter(pBtree); for(p=pBtree->pBt->pCursor; p; p=p->pNext){ int i; sqlite3BtreeClearCursor(p); p->eState = CURSOR_FAULT; - p->skip = errCode; + p->skipNext = errCode; for(i=0; i<=p->iPage; i++){ releasePage(p->apPage[i]); p->apPage[i] = 0; } } @@ -2914,33 +3164,20 @@ if( rc2!=SQLITE_OK ){ rc = rc2; } /* The rollback may have destroyed the pPage1->aData value. So - ** call sqlite3BtreeGetPage() on page 1 again to make + ** call btreeGetPage() on page 1 again to make ** sure pPage1->aData is set correctly. */ - if( sqlite3BtreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ + if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); pBt->inTransaction = TRANS_READ; } - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - assert( pBt->nTransaction>0 ); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - btreeClearHasContent(pBt); - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - - btreeIntegrity(p); + btreeEndTransaction(p); sqlite3BtreeLeave(p); return rc; } /* @@ -3012,12 +3249,14 @@ return rc; } /* ** Create a new cursor for the BTree whose root is on the page -** iTable. The act of acquiring a cursor gets a read lock on -** the database file. +** iTable. If a read-only cursor is requested, it is assumed that +** the caller already has at least a read-only transaction open +** on the database already. If a write-cursor is requested, then +** the caller is assumed to have an open write transaction. ** ** If wrFlag==0, then the cursor can only be used for reading. ** If wrFlag==1, then the cursor can be used for reading or for ** writing if other conditions for writing are also met. These ** are the conditions that must be met in order for writing to @@ -3047,52 +3286,38 @@ int iTable, /* Root page of table to open */ int wrFlag, /* 1 to write. 0 read-only */ struct KeyInfo *pKeyInfo, /* First arg to comparison function */ BtCursor *pCur /* Space for new cursor */ ){ - int rc; - Pgno nPage; - BtShared *pBt = p->pBt; + BtShared *pBt = p->pBt; /* Shared b-tree handle */ assert( sqlite3BtreeHoldsMutex(p) ); assert( wrFlag==0 || wrFlag==1 ); - if( wrFlag ){ - assert( !pBt->readOnly ); - if( NEVER(pBt->readOnly) ){ - return SQLITE_READONLY; - } - rc = checkForReadConflicts(p, iTable, 0, 0); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; - } - } - - if( pBt->pPage1==0 ){ - rc = lockBtreeWithRetry(p); - if( rc!=SQLITE_OK ){ - return rc; - } - } - pCur->pgnoRoot = (Pgno)iTable; - rc = sqlite3PagerPagecount(pBt->pPager, (int *)&nPage); - if( rc!=SQLITE_OK ){ - return rc; - } - if( iTable==1 && nPage==0 ){ - rc = SQLITE_EMPTY; - goto create_cursor_exception; - } - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); - if( rc!=SQLITE_OK ){ - goto create_cursor_exception; + + /* The following assert statements verify that if this is a sharable + ** b-tree database, the connection is holding the required table locks, + ** and that no other connection has any open cursor that conflicts with + ** this lock. */ + assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) ); + assert( wrFlag==0 || !hasReadConflicts(p, iTable) ); + + /* Assert that the caller has opened the required transaction. */ + assert( p->inTrans>TRANS_NONE ); + assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); + assert( pBt->pPage1 && pBt->pPage1->aData ); + + if( NEVER(wrFlag && pBt->readOnly) ){ + return SQLITE_READONLY; + } + if( iTable==1 && pagerPagecount(pBt)==0 ){ + return SQLITE_EMPTY; } /* Now that no other errors can occur, finish filling in the BtCursor - ** variables, link the cursor into the BtShared list and set *ppCur (the - ** output argument to this function). - */ + ** variables and link the cursor into the BtShared list. */ + pCur->pgnoRoot = (Pgno)iTable; + pCur->iPage = -1; pCur->pKeyInfo = pKeyInfo; pCur->pBtree = p; pCur->pBt = pBt; pCur->wrFlag = (u8)wrFlag; pCur->pNext = pBt->pCursor; @@ -3100,17 +3325,11 @@ pCur->pNext->pPrev = pCur; } pBt->pCursor = pCur; pCur->eState = CURSOR_INVALID; pCur->cachedRowid = 0; - return SQLITE_OK; - -create_cursor_exception: - releasePage(pCur->apPage[0]); - unlockBtreeIfUnused(pBt); - return rc; } int sqlite3BtreeCursor( Btree *p, /* The btree */ int iTable, /* Root page of table to open */ int wrFlag, /* 1 to write. 0 read-only */ @@ -3194,50 +3413,17 @@ sqlite3BtreeLeave(pBtree); } return SQLITE_OK; } -#ifdef SQLITE_TEST -/* -** Make a temporary cursor by filling in the fields of pTempCur. -** The temporary cursor is not on the cursor list for the Btree. -*/ -void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){ - int i; - assert( cursorHoldsMutex(pCur) ); - memcpy(pTempCur, pCur, sizeof(BtCursor)); - pTempCur->pNext = 0; - pTempCur->pPrev = 0; - for(i=0; i<=pTempCur->iPage; i++){ - sqlite3PagerRef(pTempCur->apPage[i]->pDbPage); - } - assert( pTempCur->pKey==0 ); -} -#endif /* SQLITE_TEST */ - -#ifdef SQLITE_TEST -/* -** Delete a temporary cursor such as was made by the CreateTemporaryCursor() -** function above. -*/ -void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){ - int i; - assert( cursorHoldsMutex(pCur) ); - for(i=0; i<=pCur->iPage; i++){ - sqlite3PagerUnref(pCur->apPage[i]->pDbPage); - } - sqlite3_free(pCur->pKey); -} -#endif /* SQLITE_TEST */ - /* ** Make sure the BtCursor* given in the argument has a valid ** BtCursor.info structure. If it is not already valid, call -** sqlite3BtreeParseCell() to fill it in. +** btreeParseCell() to fill it in. ** ** BtCursor.info is a cache of the information in the current cell. -** Using this cache reduces the number of calls to sqlite3BtreeParseCell(). +** Using this cache reduces the number of calls to btreeParseCell(). ** ** 2007-06-25: There is a bug in some versions of MSVC that cause the ** compiler to crash when getCellInfo() is implemented as a macro. ** But there is a measureable speed advantage to using the macro on gcc ** (when less compiler optimizations like -Os or -O0 are used and the @@ -3247,11 +3433,11 @@ #ifndef NDEBUG static void assertCellInfo(BtCursor *pCur){ CellInfo info; int iPage = pCur->iPage; memset(&info, 0, sizeof(info)); - sqlite3BtreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); + btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); } #else #define assertCellInfo(x) #endif @@ -3258,11 +3444,11 @@ #ifdef _MSC_VER /* Use a real function in MSVC to work around bugs in that compiler. */ static void getCellInfo(BtCursor *pCur){ if( pCur->info.nSize==0 ){ int iPage = pCur->iPage; - sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); + btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); pCur->validNKey = 1; }else{ assertCellInfo(pCur); } } @@ -3269,65 +3455,70 @@ #else /* if not _MSC_VER */ /* Use a macro in all other compilers so that the function is inlined */ #define getCellInfo(pCur) \ if( pCur->info.nSize==0 ){ \ int iPage = pCur->iPage; \ - sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ + btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ pCur->validNKey = 1; \ }else{ \ assertCellInfo(pCur); \ } #endif /* _MSC_VER */ + +#ifndef NDEBUG /* The next routine used only within assert() statements */ +/* +** Return true if the given BtCursor is valid. A valid cursor is one +** that is currently pointing to a row in a (non-empty) table. +** This is a verification routine is used only within assert() statements. +*/ +int sqlite3BtreeCursorIsValid(BtCursor *pCur){ + return pCur && pCur->eState==CURSOR_VALID; +} +#endif /* NDEBUG */ /* ** Set *pSize to the size of the buffer needed to hold the value of ** the key for the current entry. If the cursor is not pointing ** to a valid entry, *pSize is set to 0. ** ** For a table with the INTKEY flag set, this routine returns the key ** itself, not the number of bytes in the key. +** +** The caller must position the cursor prior to invoking this routine. +** +** This routine cannot fail. It always returns SQLITE_OK. */ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ - int rc; - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); - if( pCur->eState==CURSOR_INVALID ){ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nKey; - } - } - return rc; + assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); + if( pCur->eState!=CURSOR_VALID ){ + *pSize = 0; + }else{ + getCellInfo(pCur); + *pSize = pCur->info.nKey; + } + return SQLITE_OK; } /* ** Set *pSize to the number of bytes of data in the entry the -** cursor currently points to. Always return SQLITE_OK. -** Failure is not possible. If the cursor is not currently -** pointing to an entry (which can happen, for example, if -** the database is empty) then *pSize is set to 0. +** cursor currently points to. +** +** The caller must guarantee that the cursor is pointing to a non-NULL +** valid entry. In other words, the calling procedure must guarantee +** that the cursor has Cursor.eState==CURSOR_VALID. +** +** Failure is not possible. This function always returns SQLITE_OK. +** It might just as well be a procedure (returning void) but we continue +** to return an integer result code for historical reasons. */ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ - int rc; - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); - if( pCur->eState==CURSOR_INVALID ){ - /* Not pointing at a valid entry - set *pSize to 0. */ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nData; - } - } - return rc; + assert( pCur->eState==CURSOR_VALID ); + getCellInfo(pCur); + *pSize = pCur->info.nData; + return SQLITE_OK; } /* ** Given the page number of an overflow page in the database (parameter ** ovfl), this function finds the page number of the next page in the @@ -3346,12 +3537,12 @@ ** on *ppPage to free the reference. In no reference was obtained (because ** the pointer-map was used to obtain the value for *pPgnoNext), then ** *ppPage is set to zero. */ static int getOverflowPage( - BtShared *pBt, - Pgno ovfl, /* Overflow page */ + BtShared *pBt, /* The database file */ + Pgno ovfl, /* Current overflow page number */ MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ Pgno *pPgnoNext /* OUT: Next overflow page number */ ){ Pgno next = 0; MemPage *pPage = 0; @@ -3384,14 +3575,15 @@ } } } #endif + assert( next==0 || rc==SQLITE_DONE ); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetPage(pBt, ovfl, &pPage, 0); - assert(rc==SQLITE_OK || pPage==0); - if( next==0 && rc==SQLITE_OK ){ + rc = btreeGetPage(pBt, ovfl, &pPage, 0); + assert( rc==SQLITE_OK || pPage==0 ); + if( rc==SQLITE_OK ){ next = get4byte(pPage->aData); } } *pPgnoNext = next; @@ -3443,14 +3635,12 @@ ** buffer pBuf). ** ** A total of "amt" bytes are read or written beginning at "offset". ** Data is read to or from the buffer pBuf. ** -** This routine does not make a distinction between key and data. -** It just reads or writes bytes from the payload area. Data might -** appear on the main page or be scattered out on multiple overflow -** pages. +** The content being read or written might appear on the main page +** or be scattered out on multiple overflow pages. ** ** If the BtCursor.isIncrblobHandle flag is set, and the current ** cursor entry uses one or more overflow pages, this function ** allocates space for and lazily popluates the overflow page-list ** cache array (BtCursor.aOverflow). Subsequent calls use this @@ -3468,11 +3658,10 @@ static int accessPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ u32 offset, /* Begin reading this far into payload */ u32 amt, /* Read this many bytes */ unsigned char *pBuf, /* Write the bytes into this buffer */ - int skipKey, /* offset begins at data if this is true */ int eOp /* zero to read. non-zero to write. */ ){ unsigned char *aPayload; int rc = SQLITE_OK; u32 nKey; @@ -3487,14 +3676,11 @@ getCellInfo(pCur); aPayload = pCur->info.pCell + pCur->info.nHeader; nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey); - if( skipKey ){ - offset += nKey; - } - if( offset+amt > nKey+pCur->info.nData + if( NEVER(offset+amt > nKey+pCur->info.nData) || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ){ /* Trying to read or write past the end of the data is an error */ return SQLITE_CORRUPT_BKPT; } @@ -3528,11 +3714,13 @@ ** (the cache is lazily populated). */ if( pCur->isIncrblobHandle && !pCur->aOverflow ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl); - if( nOvfl && !pCur->aOverflow ){ + /* nOvfl is always positive. If it were zero, fetchPayload would have + ** been used instead of this routine. */ + if( ALWAYS(nOvfl) && !pCur->aOverflow ){ rc = SQLITE_NOMEM; } } /* If the overflow page-list cache has been allocated and the @@ -3601,30 +3789,24 @@ /* ** Read part of the key associated with cursor pCur. Exactly ** "amt" bytes will be transfered into pBuf[]. The transfer ** begins at "offset". +** +** The caller must ensure that pCur is pointing to a valid row +** in the table. ** ** Return SQLITE_OK on success or an error code if anything goes ** wrong. An error is returned if "offset+amt" is larger than ** the available payload. */ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - int rc; - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); - if( pCur->apPage[0]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - rc = accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0, 0); - } - return rc; + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); + assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); + return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); } /* ** Read part of the data associated with cursor pCur. Exactly ** "amt" bytes will be transfered into pBuf[]. The transfer @@ -3647,11 +3829,11 @@ rc = restoreCursorPosition(pCur); if( rc==SQLITE_OK ){ assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - rc = accessPayload(pCur, offset, amt, pBuf, 1, 0); + rc = accessPayload(pCur, offset, amt, pBuf, 0); } return rc; } /* @@ -3686,11 +3868,14 @@ assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); assert( pCur->eState==CURSOR_VALID ); assert( cursorHoldsMutex(pCur) ); pPage = pCur->apPage[pCur->iPage]; assert( pCur->aiIdx[pCur->iPage]nCell ); - getCellInfo(pCur); + if( NEVER(pCur->info.nSize==0) ){ + btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage], + &pCur->info); + } aPayload = pCur->info.pCell; aPayload += pCur->info.nHeader; if( pPage->intKey ){ nKey = 0; }else{ @@ -3699,13 +3884,11 @@ if( skipKey ){ aPayload += nKey; nLocal = pCur->info.nLocal - nKey; }else{ nLocal = pCur->info.nLocal; - if( nLocal>nKey ){ - nLocal = nKey; - } + assert( nLocal<=nKey ); } *pAmt = nLocal; return aPayload; } @@ -3723,30 +3906,37 @@ ** ** These routines is used to get quick access to key and data ** in the common case where no overflow pages are used. */ const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ + const void *p = 0; assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); - if( pCur->eState==CURSOR_VALID ){ - return (const void*)fetchPayload(pCur, pAmt, 0); + if( ALWAYS(pCur->eState==CURSOR_VALID) ){ + p = (const void*)fetchPayload(pCur, pAmt, 0); } - return 0; + return p; } const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ + const void *p = 0; assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); - if( pCur->eState==CURSOR_VALID ){ - return (const void*)fetchPayload(pCur, pAmt, 1); + if( ALWAYS(pCur->eState==CURSOR_VALID) ){ + p = (const void*)fetchPayload(pCur, pAmt, 1); } - return 0; + return p; } /* ** Move the cursor down to a new child page. The newPgno argument is the ** page number of the child page to move to. +** +** This function returns SQLITE_CORRUPT if the page-header flags field of +** the new child page does not match the flags field of the parent (i.e. +** if an intkey page appears to be the parent of a non-intkey page, or +** vice-versa). */ static int moveToChild(BtCursor *pCur, u32 newPgno){ int rc; int i = pCur->iPage; MemPage *pNewPage; @@ -3764,11 +3954,11 @@ pCur->aiIdx[i+1] = 0; pCur->iPage++; pCur->info.nSize = 0; pCur->validNKey = 0; - if( pNewPage->nCell<1 ){ + if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){ return SQLITE_CORRUPT_BKPT; } return SQLITE_OK; } @@ -3798,11 +3988,11 @@ ** pCur->idx is set to the cell index that contains the pointer ** to the page we are coming from. If we are coming from the ** right-most child page then pCur->idx is set to one more than ** the largest cell index. */ -void sqlite3BtreeMoveToParent(BtCursor *pCur){ +static void moveToParent(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); assert( pCur->apPage[pCur->iPage] ); assertParentIndex( @@ -3815,11 +4005,29 @@ pCur->info.nSize = 0; pCur->validNKey = 0; } /* -** Move the cursor to the root page +** Move the cursor to point to the root page of its b-tree structure. +** +** If the table has a virtual root page, then the cursor is moved to point +** to the virtual root page instead of the actual root page. A table has a +** virtual root page when the actual root page contains no cells and a +** single child page. This can only happen with the table rooted at page 1. +** +** If the b-tree structure is empty, the cursor state is set to +** CURSOR_INVALID. Otherwise, the cursor is set to point to the first +** cell located on the root (or virtual root) page and the cursor state +** is set to CURSOR_VALID. +** +** If this function returns successfully, it may be assumed that the +** page-header flags indicate that the [virtual] root-page is the expected +** kind of b-tree page (i.e. if when opening the cursor the caller did not +** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D, +** indicating a table b-tree, or if the caller did specify a KeyInfo +** structure the flags byte is set to 0x02 or 0x0A, indicating an index +** b-tree). */ static int moveToRoot(BtCursor *pCur){ MemPage *pRoot; int rc = SQLITE_OK; Btree *p = pCur->pBtree; @@ -3829,43 +4037,59 @@ assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); if( pCur->eState>=CURSOR_REQUIRESEEK ){ if( pCur->eState==CURSOR_FAULT ){ - return pCur->skip; + assert( pCur->skipNext!=SQLITE_OK ); + return pCur->skipNext; } sqlite3BtreeClearCursor(pCur); } if( pCur->iPage>=0 ){ int i; for(i=1; i<=pCur->iPage; i++){ releasePage(pCur->apPage[i]); } + pCur->iPage = 0; }else{ - if( - SQLITE_OK!=(rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0])) - ){ + rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); + if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; return rc; } + pCur->iPage = 0; + + /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor + ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is + ** NULL, the caller expects a table b-tree. If this is not the case, + ** return an SQLITE_CORRUPT error. */ + assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 ); + if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){ + return SQLITE_CORRUPT_BKPT; + } } + /* Assert that the root page is of the correct type. This must be the + ** case as the call to this function that loaded the root-page (either + ** this call or a previous invocation) would have detected corruption + ** if the assumption were not true, and it is not possible for the flags + ** byte to have been modified while this cursor is holding a reference + ** to the page. */ pRoot = pCur->apPage[0]; assert( pRoot->pgno==pCur->pgnoRoot ); - pCur->iPage = 0; + assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey ); + pCur->aiIdx[0] = 0; pCur->info.nSize = 0; pCur->atLast = 0; pCur->validNKey = 0; if( pRoot->nCell==0 && !pRoot->leaf ){ Pgno subpage; if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; - assert( pRoot->pgno==1 ); subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); - assert( subpage>0 ); pCur->eState = CURSOR_VALID; rc = moveToChild(pCur, subpage); }else{ pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID); } @@ -4025,10 +4249,12 @@ ){ int rc; assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); + assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); /* If the cursor is already positioned at the point we are trying ** to move to, then just return without doing any work */ if( pCur->eState==CURSOR_VALID && pCur->validNKey && pCur->apPage[0]->intKey @@ -4047,10 +4273,11 @@ if( rc ){ return rc; } assert( pCur->apPage[pCur->iPage] ); assert( pCur->apPage[pCur->iPage]->isInit ); + assert( pCur->apPage[pCur->iPage]->nCell>0 || pCur->eState==CURSOR_INVALID ); if( pCur->eState==CURSOR_INVALID ){ *pRes = -1; assert( pCur->apPage[pCur->iPage]->nCell==0 ); return SQLITE_OK; } @@ -4057,17 +4284,22 @@ assert( pCur->apPage[0]->intKey || pIdxKey ); for(;;){ int lwr, upr; Pgno chldPg; MemPage *pPage = pCur->apPage[pCur->iPage]; - int c = -1; /* pRes return if table is empty must be -1 */ + int c; + + /* pPage->nCell must be greater than zero. If this is the root-page + ** the cursor would have been INVALID above and this for(;;) loop + ** not run. If this is not the root-page, then the moveToChild() routine + ** would have already detected db corruption. Similarly, pPage must + ** be the right kind (index or table) of b-tree page. Otherwise + ** a moveToChild() or moveToRoot() call would have detected corruption. */ + assert( pPage->nCell>0 ); + assert( pPage->intKey==(pIdxKey==0) ); lwr = 0; upr = pPage->nCell-1; - if( (!pPage->intKey && pIdxKey==0) || upr<0 ){ - rc = SQLITE_CORRUPT_BKPT; - goto moveto_finish; - } if( biasRight ){ pCur->aiIdx[pCur->iPage] = (u16)upr; }else{ pCur->aiIdx[pCur->iPage] = (u16)((upr+lwr)/2); } @@ -4120,21 +4352,24 @@ ** this case the whole cell needs to be parsed, a buffer allocated ** and accessPayload() used to retrieve the record into the ** buffer before VdbeRecordCompare() can be called. */ void *pCellKey; u8 * const pCellBody = pCell - pPage->childPtrSize; - sqlite3BtreeParseCellPtr(pPage, pCellBody, &pCur->info); + btreeParseCellPtr(pPage, pCellBody, &pCur->info); nCell = (int)pCur->info.nKey; pCellKey = sqlite3Malloc( nCell ); if( pCellKey==0 ){ rc = SQLITE_NOMEM; goto moveto_finish; } - rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0, 0); + rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); + if( rc ){ + sqlite3_free(pCellKey); + goto moveto_finish; + } c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); - if( rc ) goto moveto_finish; } } if( c==0 ){ if( pPage->intKey && !pPage->leaf ){ lwr = idx; @@ -4165,11 +4400,11 @@ }else{ chldPg = get4byte(findCell(pPage, lwr)); } if( chldPg==0 ){ assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - if( pRes ) *pRes = c; + *pRes = c; rc = SQLITE_OK; goto moveto_finish; } pCur->aiIdx[pCur->iPage] = (u16)lwr; pCur->info.nSize = 0; @@ -4176,42 +4411,10 @@ pCur->validNKey = 0; rc = moveToChild(pCur, chldPg); if( rc ) goto moveto_finish; } moveto_finish: - return rc; -} - -/* -** In this version of BtreeMoveto, pKey is a packed index record -** such as is generated by the OP_MakeRecord opcode. Unpack the -** record and then call BtreeMovetoUnpacked() to do the work. -*/ -int sqlite3BtreeMoveto( - BtCursor *pCur, /* Cursor open on the btree to be searched */ - const void *pKey, /* Packed key if the btree is an index */ - i64 nKey, /* Integer key for tables. Size of pKey for indices */ - int bias, /* Bias search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; /* Status code */ - UnpackedRecord *pIdxKey; /* Unpacked index key */ - char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ - - - if( pKey ){ - assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, - aSpace, sizeof(aSpace)); - if( pIdxKey==0 ) return SQLITE_NOMEM; - }else{ - pIdxKey = 0; - } - rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); - if( pKey ){ - sqlite3VdbeDeleteUnpackedRecord(pIdxKey); - } return rc; } /* @@ -4248,16 +4451,16 @@ assert( pRes!=0 ); if( CURSOR_INVALID==pCur->eState ){ *pRes = 1; return SQLITE_OK; } - if( pCur->skip>0 ){ - pCur->skip = 0; + if( pCur->skipNext>0 ){ + pCur->skipNext = 0; *pRes = 0; return SQLITE_OK; } - pCur->skip = 0; + pCur->skipNext = 0; pPage = pCur->apPage[pCur->iPage]; idx = ++pCur->aiIdx[pCur->iPage]; assert( pPage->isInit ); assert( idx<=pPage->nCell ); @@ -4276,11 +4479,11 @@ if( pCur->iPage==0 ){ *pRes = 1; pCur->eState = CURSOR_INVALID; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); pPage = pCur->apPage[pCur->iPage]; }while( pCur->aiIdx[pCur->iPage]>=pPage->nCell ); *pRes = 0; if( pPage->intKey ){ rc = sqlite3BtreeNext(pCur, pRes); @@ -4316,16 +4519,16 @@ pCur->atLast = 0; if( CURSOR_INVALID==pCur->eState ){ *pRes = 1; return SQLITE_OK; } - if( pCur->skip<0 ){ - pCur->skip = 0; + if( pCur->skipNext<0 ){ + pCur->skipNext = 0; *pRes = 0; return SQLITE_OK; } - pCur->skip = 0; + pCur->skipNext = 0; pPage = pCur->apPage[pCur->iPage]; assert( pPage->isInit ); if( !pPage->leaf ){ int idx = pCur->aiIdx[pCur->iPage]; @@ -4339,11 +4542,11 @@ if( pCur->iPage==0 ){ pCur->eState = CURSOR_INVALID; *pRes = 1; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); } pCur->info.nSize = 0; pCur->validNKey = 0; pCur->aiIdx[pCur->iPage]--; @@ -4396,11 +4599,12 @@ assert( sqlite3_mutex_held(pBt->mutex) ); pPage1 = pBt->pPage1; mxPage = pagerPagecount(pBt); n = get4byte(&pPage1->aData[36]); - if( n>mxPage ){ + testcase( n==mxPage-1 ); + if( n>=mxPage ){ return SQLITE_CORRUPT_BKPT; } if( n>0 ){ /* There are pages on the freelist. Reuse one of those pages. */ Pgno iTrunk; @@ -4440,14 +4644,15 @@ if( pPrevTrunk ){ iTrunk = get4byte(&pPrevTrunk->aData[0]); }else{ iTrunk = get4byte(&pPage1->aData[32]); } + testcase( iTrunk==mxPage ); if( iTrunk>mxPage ){ rc = SQLITE_CORRUPT_BKPT; }else{ - rc = sqlite3BtreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); } if( rc ){ pTrunk = 0; goto end_allocate_page; } @@ -4498,11 +4703,12 @@ Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); if( iNewTrunk>mxPage ){ rc = SQLITE_CORRUPT_BKPT; goto end_allocate_page; } - rc = sqlite3BtreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); + testcase( iNewTrunk==mxPage ); + rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); if( rc!=SQLITE_OK ){ goto end_allocate_page; } rc = sqlite3PagerWrite(pNewTrunk->pDbPage); if( rc!=SQLITE_OK ){ @@ -4553,34 +4759,29 @@ }else{ closest = 0; } iPage = get4byte(&aData[8+closest*4]); + testcase( iPage==mxPage ); if( iPage>mxPage ){ rc = SQLITE_CORRUPT_BKPT; goto end_allocate_page; } + testcase( iPage==mxPage ); if( !searchList || iPage==nearby ){ int noContent; - Pgno nPage; *pPgno = iPage; - nPage = pagerPagecount(pBt); - if( iPage>nPage ){ - /* Free page off the end of the file */ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; - } TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" ": %d more free pages\n", *pPgno, closest+1, k, pTrunk->pgno, n-1)); if( closestpDbPage) ); noContent = !btreeGetHasContent(pBt, *pPgno); - rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, noContent); + rc = btreeGetPage(pBt, *pPgno, ppPage, noContent); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); } @@ -4608,11 +4809,11 @@ ** becomes a new pointer-map page, the second is used by the caller. */ MemPage *pPg = 0; TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno)); assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = sqlite3BtreeGetPage(pBt, *pPgno, &pPg, 0); + rc = btreeGetPage(pBt, *pPgno, &pPg, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); } if( rc ) return rc; @@ -4620,11 +4821,11 @@ if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ (*pPgno)++; } } #endif assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, 0); + rc = btreeGetPage(pBt, *pPgno, ppPage, 0); if( rc ) return rc; rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ releasePage(*ppPage); } @@ -4687,11 +4888,11 @@ #ifdef SQLITE_SECURE_DELETE /* If the SQLITE_SECURE_DELETE compile-time option is enabled, then ** always fully overwrite deleted information with zeros. */ - if( (!pPage && (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0))) + if( (!pPage && (rc = btreeGetPage(pBt, iPage, &pPage, 0))) || (rc = sqlite3PagerWrite(pPage->pDbPage)) ){ goto freepage_out; } memset(pPage->aData, 0, pPage->pBt->pageSize); @@ -4699,11 +4900,11 @@ /* If the database supports auto-vacuum, write an entry in the pointer-map ** to indicate that the page is free. */ if( ISAUTOVACUUM ){ - rc = ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0); + ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); if( rc ) goto freepage_out; } /* Now manipulate the actual database free-list structure. There are two ** possibilities. If the free-list is currently empty, or if the first @@ -4711,34 +4912,35 @@ ** new free-list trunk page. Otherwise, it will become a leaf of the ** first trunk page in the current free-list. This block tests if it ** is possible to add the page as a new free-list leaf. */ if( nFree!=0 ){ - int nLeaf; /* Initial number of leaf cells on trunk page */ + u32 nLeaf; /* Initial number of leaf cells on trunk page */ iTrunk = get4byte(&pPage1->aData[32]); - rc = sqlite3BtreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); if( rc!=SQLITE_OK ){ goto freepage_out; } nLeaf = get4byte(&pTrunk->aData[4]); - if( nLeaf<0 ){ + assert( pBt->usableSize>32 ); + if( nLeaf > (u32)pBt->usableSize/4 - 2 ){ rc = SQLITE_CORRUPT_BKPT; goto freepage_out; } - if( nLeafusableSize/4 - 8 ){ + if( nLeaf < (u32)pBt->usableSize/4 - 8 ){ /* In this case there is room on the trunk page to insert the page ** being freed as a new leaf. ** ** Note that the trunk page is not really full until it contains ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have ** coded. But due to a coding error in versions of SQLite prior to ** 3.6.0, databases with freelist trunk pages holding more than ** usableSize/4 - 8 entries will be reported as corrupt. In order ** to maintain backwards compatibility with older versions of SQLite, - ** we will contain to restrict the number of entries to usableSize/4 - 8 + ** we will continue to restrict the number of entries to usableSize/4 - 8 ** for now. At some point in the future (once everyone has upgraded ** to 3.6.0 or later) we should consider fixing the conditional above ** to read "usableSize/4-2" instead of "usableSize/4-8". */ rc = sqlite3PagerWrite(pTrunk->pDbPage); @@ -4761,13 +4963,15 @@ ** the page being freed as a leaf page of the first trunk in the free-list. ** Possibly because the free-list is empty, or possibly because the ** first trunk in the free-list is full. Either way, the page being freed ** will become the new first trunk page in the free-list. */ - if( ((!pPage) && (0 != (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0)))) - || (0 != (rc = sqlite3PagerWrite(pPage->pDbPage))) - ){ + if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){ + goto freepage_out; + } + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc!=SQLITE_OK ){ goto freepage_out; } put4byte(pPage->aData, iTrunk); put4byte(&pPage->aData[4], 0); put4byte(&pPage1->aData[32], iPage); @@ -4779,12 +4983,14 @@ } releasePage(pPage); releasePage(pTrunk); return rc; } -static int freePage(MemPage *pPage){ - return freePage2(pPage->pBt, pPage, pPage->pgno); +static void freePage(MemPage *pPage, int *pRC){ + if( (*pRC)==SQLITE_OK ){ + *pRC = freePage2(pPage->pBt, pPage, pPage->pgno); + } } /* ** Free any overflow pages associated with the given Cell. */ @@ -4795,11 +5001,11 @@ int rc; int nOvfl; u16 ovflPageSize; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); if( info.iOverflow==0 ){ return SQLITE_OK; /* No overflow pages. Return without doing anything */ } ovflPgno = get4byte(&pCell[info.iOverflow]); assert( pBt->usableSize > 4 ); @@ -4878,11 +5084,11 @@ nHeader += putVarint(&pCell[nHeader], nData+nZero); }else{ nData = nZero = 0; } nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); assert( info.nHeader==nHeader ); assert( info.nKey==nKey ); assert( info.nData==(u32)(nData+nZero) ); /* Fill in the payload */ @@ -4890,12 +5096,12 @@ if( pPage->intKey ){ pSrc = pData; nSrc = nData; nData = 0; }else{ - if( nKey>0x7fffffff || pKey==0 ){ - return SQLITE_CORRUPT; + if( NEVER(nKey>0x7fffffff || pKey==0) ){ + return SQLITE_CORRUPT_BKPT; } nPayload += (int)nKey; pSrc = pKey; nSrc = (int)nKey; } @@ -4928,11 +5134,11 @@ ** may misinterpret the uninitialised values and delete the ** wrong pages from the database. */ if( pBt->autoVacuum && rc==SQLITE_OK ){ u8 eType = (pgnoPtrmap?PTRMAP_OVERFLOW2:PTRMAP_OVERFLOW1); - rc = ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap); + ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, &rc); if( rc ){ releasePage(pOvfl); } } #endif @@ -4997,40 +5203,46 @@ ** the cell content has been copied someplace else. This routine just ** removes the reference to the cell from pPage. ** ** "sz" must be the number of bytes in the cell. */ -static int dropCell(MemPage *pPage, int idx, int sz){ +static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ int i; /* Loop counter */ int pc; /* Offset to cell content of cell being deleted */ u8 *data; /* pPage->aData */ u8 *ptr; /* Used to move bytes around within data[] */ int rc; /* The return code */ + int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ + + if( *pRC ) return; assert( idx>=0 && idxnCell ); assert( sz==cellSize(pPage, idx) ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); data = pPage->aData; ptr = &data[pPage->cellOffset + 2*idx]; pc = get2byte(ptr); - if( (pchdrOffset+6+(pPage->leaf?0:4)) - || (pc+sz>pPage->pBt->usableSize) ){ - return SQLITE_CORRUPT_BKPT; + hdr = pPage->hdrOffset; + testcase( pc==get2byte(&data[hdr+5]) ); + testcase( pc+sz==pPage->pBt->usableSize ); + if( pc < get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){ + *pRC = SQLITE_CORRUPT_BKPT; + return; } rc = freeSpace(pPage, pc, sz); - if( rc!=SQLITE_OK ){ - return rc; + if( rc ){ + *pRC = rc; + return; } for(i=idx+1; inCell; i++, ptr+=2){ ptr[0] = ptr[2]; ptr[1] = ptr[3]; } pPage->nCell--; - put2byte(&data[pPage->hdrOffset+3], pPage->nCell); + put2byte(&data[hdr+3], pPage->nCell); pPage->nFree += 2; - return SQLITE_OK; } /* ** Insert a new cell on pPage at cell index "i". pCell points to the ** content of the cell. @@ -5046,29 +5258,30 @@ ** If nSkip is non-zero, then do not copy the first nSkip bytes of the ** cell. The caller will overwrite them after this function returns. If ** nSkip is non-zero, then pCell may not point to an invalid memory location ** (but pCell+nSkip is always valid). */ -static int insertCell( +static void insertCell( MemPage *pPage, /* Page into which we are copying */ int i, /* New cell becomes the i-th cell of the page */ u8 *pCell, /* Content of the new cell */ int sz, /* Bytes of content in pCell */ u8 *pTemp, /* Temp storage space for pCell, if needed */ - Pgno iChild /* If non-zero, replace first 4 bytes with this value */ + Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ + int *pRC /* Read and write return code from here */ ){ int idx; /* Where to write new cell content in data[] */ int j; /* Loop counter */ - int top; /* First byte of content for any cell in data[] */ int end; /* First byte past the last cell pointer in data[] */ int ins; /* Index in data[] where new cell pointer is inserted */ - int hdr; /* Offset into data[] of the page header */ int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); + + if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 ); assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) ); assert( sz==cellSizePtr(pPage, pCell) ); @@ -5086,56 +5299,45 @@ pPage->aOvfl[j].pCell = pCell; pPage->aOvfl[j].idx = (u16)i; }else{ int rc = sqlite3PagerWrite(pPage->pDbPage); if( rc!=SQLITE_OK ){ - return rc; + *pRC = rc; + return; } assert( sqlite3PagerIswriteable(pPage->pDbPage) ); data = pPage->aData; - hdr = pPage->hdrOffset; - top = get2byte(&data[hdr+5]); cellOffset = pPage->cellOffset; - end = cellOffset + 2*pPage->nCell + 2; + end = cellOffset + 2*pPage->nCell; ins = cellOffset + 2*i; - if( end > top - sz ){ - rc = defragmentPage(pPage); - if( rc!=SQLITE_OK ){ - return rc; - } - top = get2byte(&data[hdr+5]); - assert( end + sz <= top ); - } - idx = allocateSpace(pPage, sz); - assert( idx>0 ); - assert( end <= get2byte(&data[hdr+5]) ); - if (idx+sz > pPage->pBt->usableSize) { - return SQLITE_CORRUPT_BKPT; - } + rc = allocateSpace(pPage, sz, &idx); + if( rc ){ *pRC = rc; return; } + /* The allocateSpace() routine guarantees the following two properties + ** if it returns success */ + assert( idx >= end+2 ); + assert( idx+sz <= pPage->pBt->usableSize ); pPage->nCell++; - pPage->nFree = pPage->nFree - (u16)(2 + sz); + pPage->nFree -= (u16)(2 + sz); memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); if( iChild ){ put4byte(&data[idx], iChild); } - for(j=end-2, ptr=&data[j]; j>ins; j-=2, ptr-=2){ + for(j=end, ptr=&data[j]; j>ins; j-=2, ptr-=2){ ptr[0] = ptr[-2]; ptr[1] = ptr[-1]; } put2byte(&data[ins], idx); - put2byte(&data[hdr+3], pPage->nCell); + put2byte(&data[pPage->hdrOffset+3], pPage->nCell); #ifndef SQLITE_OMIT_AUTOVACUUM if( pPage->pBt->autoVacuum ){ /* The cell may contain a pointer to an overflow page. If so, write ** the entry for the overflow page into the pointer map. */ - return ptrmapPutOvflPtr(pPage, pCell); + ptrmapPutOvflPtr(pPage, pCell, pRC); } #endif } - - return SQLITE_OK; } /* ** Add a list of cells to a page. The page should be initially empty. ** The cells are guaranteed to fit on the page. @@ -5197,11 +5399,11 @@ ** This version of balance() handles the common special case where ** a new entry is being inserted on the extreme right-end of the ** tree, in other words, when the new entry will become the largest ** entry in the tree. ** -** Instead of trying balance the 3 right-most leaf pages, just add +** Instead of trying to balance the 3 right-most leaf pages, just add ** a new page to the right-hand side and put the one new entry in ** that page. This leaves the right side of the tree somewhat ** unbalanced. But odds are that we will be inserting new entries ** at the end soon afterwards so the nearly empty page will quickly ** fill up. On average. @@ -5254,13 +5456,13 @@ ** That is Ok, at this point the parent page is guaranteed to ** be marked as dirty. Returning an error code will cause a ** rollback, undoing any changes made to the parent page. */ if( ISAUTOVACUUM ){ - rc = ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno); - if( szCell>pNew->minLocal && rc==SQLITE_OK ){ - rc = ptrmapPutOvflPtr(pNew, pCell); + ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); + if( szCell>pNew->minLocal ){ + ptrmapPutOvflPtr(pNew, pCell, &rc); } } /* Create a divider cell to insert into pParent. The divider cell ** consists of a 4-byte page number (the page number of pPage) and @@ -5280,11 +5482,12 @@ while( (*(pCell++)&0x80) && pCellnCell,pSpace,(int)(pOut-pSpace),0,pPage->pgno); + insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), + 0, pPage->pgno, &rc); /* Set the right-child pointer of pParent to point to the new page. */ put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); /* Release the reference to the new page. */ @@ -5313,11 +5516,11 @@ for(j=0; jnCell; j++){ CellInfo info; u8 *z; z = findCell(pPage, j); - sqlite3BtreeParseCellPtr(pPage, z, &info); + btreeParseCellPtr(pPage, z, &info); if( info.iOverflow ){ Pgno ovfl = get4byte(&z[info.iOverflow]); ptrmapGet(pBt, ovfl, &e, &n); assert( n==pPage->pgno && e==PTRMAP_OVERFLOW1 ); } @@ -5346,47 +5549,50 @@ ** map entries are also updated so that the parent page is page pTo. ** ** If pFrom is currently carrying any overflow cells (entries in the ** MemPage.aOvfl[] array), they are not copied to pTo. ** -** Before returning, page pTo is reinitialized using sqlite3BtreeInitPage(). +** Before returning, page pTo is reinitialized using btreeInitPage(). ** ** The performance of this function is not critical. It is only used by ** the balance_shallower() and balance_deeper() procedures, neither of ** which are called often under normal circumstances. */ -static int copyNodeContent(MemPage *pFrom, MemPage *pTo){ - BtShared * const pBt = pFrom->pBt; - u8 * const aFrom = pFrom->aData; - u8 * const aTo = pTo->aData; - int const iFromHdr = pFrom->hdrOffset; - int const iToHdr = ((pTo->pgno==1) ? 100 : 0); - int rc = SQLITE_OK; - int iData; - - assert( pFrom->isInit ); - assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); - - /* Copy the b-tree node content from page pFrom to page pTo. */ - iData = get2byte(&aFrom[iFromHdr+5]); - memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); - memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); - - /* Reinitialize page pTo so that the contents of the MemPage structure - ** match the new data. The initialization of pTo "cannot" fail, as the - ** data copied from pFrom is known to be valid. */ - pTo->isInit = 0; - TESTONLY(rc = ) sqlite3BtreeInitPage(pTo); - assert( rc==SQLITE_OK ); - - /* If this is an auto-vacuum database, update the pointer-map entries - ** for any b-tree or overflow pages that pTo now contains the pointers to. */ - if( ISAUTOVACUUM ){ - rc = setChildPtrmaps(pTo); - } - return rc; +static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ + if( (*pRC)==SQLITE_OK ){ + BtShared * const pBt = pFrom->pBt; + u8 * const aFrom = pFrom->aData; + u8 * const aTo = pTo->aData; + int const iFromHdr = pFrom->hdrOffset; + int const iToHdr = ((pTo->pgno==1) ? 100 : 0); + TESTONLY(int rc;) + int iData; + + + assert( pFrom->isInit ); + assert( pFrom->nFree>=iToHdr ); + assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); + + /* Copy the b-tree node content from page pFrom to page pTo. */ + iData = get2byte(&aFrom[iFromHdr+5]); + memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); + memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); + + /* Reinitialize page pTo so that the contents of the MemPage structure + ** match the new data. The initialization of pTo "cannot" fail, as the + ** data copied from pFrom is known to be valid. */ + pTo->isInit = 0; + TESTONLY(rc = ) btreeInitPage(pTo); + assert( rc==SQLITE_OK ); + + /* If this is an auto-vacuum database, update the pointer-map entries + ** for any b-tree or overflow pages that pTo now contains the pointers to. + */ + if( ISAUTOVACUUM ){ + *pRC = setChildPtrmaps(pTo); + } + } } /* ** This routine redistributes cells on the iParentIdx'th child of pParent ** (hereafter "the page") and up to 2 siblings so that all pages have about the @@ -5414,13 +5620,13 @@ ** If this routine fails for any reason, it might leave the database ** in a corrupted state. So if this routine fails, the database should ** be rolled back. ** ** The third argument to this function, aOvflSpace, is a pointer to a -** buffer page-size bytes in size. If, in inserting cells into the parent -** page (pParent), the parent page becomes overfull, this buffer is -** used to store the parents overflow cells. Because this function inserts +** buffer big enough to hold one page. If while inserting cells into the parent +** page (pParent) the parent page becomes overfull, this buffer is +** used to store the parent's overflow cells. Because this function inserts ** a maximum of four divider cells into the parent page, and the maximum ** size of a cell stored within an internal node is always less than 1/4 ** of the page-size, the aOvflSpace[] buffer is guaranteed to be large ** enough for all overflow cells. ** @@ -5470,11 +5676,12 @@ #endif /* At this point pParent may have at most one overflow cell. And if ** this overflow cell is present, it must be the cell with ** index iParentIdx. This scenario comes about when this function - ** is called (indirectly) from sqlite3BtreeDelete(). */ + ** is called (indirectly) from sqlite3BtreeDelete(). + */ assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); assert( pParent->nOverflow==0 || pParent->aOvfl[0].idx==iParentIdx ); if( !aOvflSpace ){ return SQLITE_NOMEM; @@ -5486,12 +5693,13 @@ ** if there are fewer than NN siblings on the other side. If pParent ** has NB or fewer children then all children of pParent are taken. ** ** This loop also drops the divider cells from the parent page. This ** way, the remainder of the function does not have to deal with any - ** overflow cells in the parent page, as if one existed it has already - ** been removed. */ + ** overflow cells in the parent page, since if any existed they will + ** have already been removed. + */ i = pParent->nOverflow + pParent->nCell; if( i<2 ){ nxDiv = 0; nOld = i+1; }else{ @@ -5512,17 +5720,17 @@ } pgno = get4byte(pRight); while( 1 ){ rc = getAndInitPage(pBt, pgno, &apOld[i]); if( rc ){ - memset(apOld, 0, i*sizeof(MemPage*)); + memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; } nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; if( (i--)==0 ) break; - if( pParent->nOverflow && i+nxDiv==pParent->aOvfl[0].idx ){ + if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){ apDiv[i] = pParent->aOvfl[0].pCell; pgno = get4byte(apDiv[i]); szNew[i] = cellSizePtr(pParent, apDiv[i]); pParent->nOverflow = 0; }else{ @@ -5544,11 +5752,11 @@ ** is allocated. */ #ifdef SQLITE_SECURE_DELETE memcpy(&aOvflSpace[apDiv[i]-pParent->aData], apDiv[i], szNew[i]); apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; #endif - dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i]); + dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); } } /* Make nMaxCells a multiple of 4 in order to preserve 8-byte ** alignment */ @@ -5738,11 +5946,11 @@ apNew[i] = pNew; nNew++; /* Set the pointer-map entry for the new sibling page. */ if( ISAUTOVACUUM ){ - rc = ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno); + ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); if( rc!=SQLITE_OK ){ goto balance_cleanup; } } } @@ -5749,11 +5957,11 @@ } /* Free any old pages that were not reused as new pages. */ while( ipageSize/4 ); assert( iOvflSpace<=pBt->pageSize ); - rc = insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno); + insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc); if( rc!=SQLITE_OK ) goto balance_cleanup; assert( sqlite3PagerIswriteable(pParent->pDbPage) ); j++; nxDiv++; @@ -5897,13 +6105,12 @@ ** image. */ assert( nNew==1 ); assert( apNew[0]->nFree == (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) ); - if( SQLITE_OK==(rc = copyNodeContent(apNew[0], pParent)) ){ - rc = freePage(apNew[0]); - } + copyNodeContent(apNew[0], pParent, &rc); + freePage(apNew[0], &rc); }else if( ISAUTOVACUUM ){ /* Fix the pointer-map entries for all the cells that were shifted around. ** There are several different types of pointer-map entries that need to ** be dealt with by this routine. Some of these have been set already, but ** many have not. The following is a summary: @@ -5939,11 +6146,11 @@ int nOverflow = pOld->nOverflow; int iNextOld = pOld->nCell + nOverflow; int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1); j = 0; /* Current 'old' sibling page */ k = 0; /* Current 'new' sibling page */ - for(i=0; ipgno!=pNew->pgno ){ if( !leafCorrection ){ - rc = ptrmapPut(pBt, get4byte(apCell[i]), PTRMAP_BTREE, pNew->pgno); + ptrmapPut(pBt, get4byte(apCell[i]), PTRMAP_BTREE, pNew->pgno, &rc); } - if( szCell[i]>pNew->minLocal && rc==SQLITE_OK ){ - rc = ptrmapPutOvflPtr(pNew, apCell[i]); + if( szCell[i]>pNew->minLocal ){ + ptrmapPutOvflPtr(pNew, apCell[i], &rc); } } } if( !leafCorrection ){ - for(i=0; rc==SQLITE_OK && iaData[8]), PTRMAP_BTREE, apNew[i]->pgno); + for(i=0; iaData[8]); + ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc); } } #if 0 /* The ptrmapCheckPages() contains assert() statements that verify that @@ -6048,26 +6254,29 @@ ** an error code is returned and *ppChild is set to 0. */ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ int rc; /* Return value from subprocedures */ MemPage *pChild = 0; /* Pointer to a new child page */ - Pgno pgnoChild; /* Page number of the new child page */ + Pgno pgnoChild = 0; /* Page number of the new child page */ BtShared *pBt = pRoot->pBt; /* The BTree */ assert( pRoot->nOverflow>0 ); assert( sqlite3_mutex_held(pBt->mutex) ); /* Make pRoot, the root page of the b-tree, writable. Allocate a new ** page that will become the new right-child of pPage. Copy the contents ** of the node stored on pRoot into the new child page. */ - if( SQLITE_OK!=(rc = sqlite3PagerWrite(pRoot->pDbPage)) - || SQLITE_OK!=(rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0)) - || SQLITE_OK!=(rc = copyNodeContent(pRoot, pChild)) - || (ISAUTOVACUUM && - SQLITE_OK!=(rc = ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno))) - ){ + rc = sqlite3PagerWrite(pRoot->pDbPage); + if( rc==SQLITE_OK ){ + rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); + copyNodeContent(pRoot, pChild, &rc); + if( ISAUTOVACUUM ){ + ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); + } + } + if( rc ){ *ppChild = 0; releasePage(pChild); return rc; } assert( sqlite3PagerIswriteable(pChild->pDbPage) ); @@ -6208,79 +6417,10 @@ sqlite3PageFree(pFree); } return rc; } -/* -** This routine checks all cursors that point to table pgnoRoot. -** If any of those cursors were opened with wrFlag==0 in a different -** database connection (a database connection that shares the pager -** cache with the current connection) and that other connection -** is not in the ReadUncommmitted state, then this routine returns -** SQLITE_LOCKED. -** -** As well as cursors with wrFlag==0, cursors with -** isIncrblobHandle==1 are also considered 'read' cursors because -** incremental blob cursors are used for both reading and writing. -** -** When pgnoRoot is the root page of an intkey table, this function is also -** responsible for invalidating incremental blob cursors when the table row -** on which they are opened is deleted or modified. Cursors are invalidated -** according to the following rules: -** -** 1) When BtreeClearTable() is called to completely delete the contents -** of a B-Tree table, pExclude is set to zero and parameter iRow is -** set to non-zero. In this case all incremental blob cursors open -** on the table rooted at pgnoRoot are invalidated. -** -** 2) When BtreeInsert(), BtreeDelete() or BtreePutData() is called to -** modify a table row via an SQL statement, pExclude is set to the -** write cursor used to do the modification and parameter iRow is set -** to the integer row id of the B-Tree entry being modified. Unless -** pExclude is itself an incremental blob cursor, then all incremental -** blob cursors open on row iRow of the B-Tree are invalidated. -** -** 3) If both pExclude and iRow are set to zero, no incremental blob -** cursors are invalidated. -*/ -static int checkForReadConflicts( - Btree *pBtree, /* The database file to check */ - Pgno pgnoRoot, /* Look for read cursors on this btree */ - BtCursor *pExclude, /* Ignore this cursor */ - i64 iRow /* The rowid that might be changing */ -){ - BtCursor *p; - BtShared *pBt = pBtree->pBt; - sqlite3 *db = pBtree->db; - assert( sqlite3BtreeHoldsMutex(pBtree) ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p==pExclude ) continue; - if( p->pgnoRoot!=pgnoRoot ) continue; -#ifndef SQLITE_OMIT_INCRBLOB - if( p->isIncrblobHandle && ( - (!pExclude && iRow) - || (pExclude && !pExclude->isIncrblobHandle && p->info.nKey==iRow) - )){ - p->eState = CURSOR_INVALID; - } -#endif - if( p->eState!=CURSOR_VALID ) continue; - if( p->wrFlag==0 -#ifndef SQLITE_OMIT_INCRBLOB - || p->isIncrblobHandle -#endif - ){ - sqlite3 *dbOther = p->pBtree->db; - assert(dbOther); - if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){ - sqlite3ConnectionBlocked(db, dbOther); - return SQLITE_LOCKED_SHAREDCACHE; - } - } - } - return SQLITE_OK; -} /* ** Insert a new record into the BTree. The key is given by (pKey,nKey) ** and the data is given by (pData,nData). The cursor is used only to ** define what table the record should be inserted into. The cursor @@ -6288,74 +6428,86 @@ ** ** For an INTKEY table, only the nKey value of the key is used. pKey is ** ignored. For a ZERODATA table, the pData and nData are both ignored. ** ** If the seekResult parameter is non-zero, then a successful call to -** sqlite3BtreeMoveto() to seek cursor pCur to (pKey, nKey) has already +** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already ** been performed. seekResult is the search result returned (a negative ** number if pCur points at an entry that is smaller than (pKey, nKey), or ** a positive value if pCur points at an etry that is larger than ** (pKey, nKey)). ** -** If the seekResult parameter is 0, then cursor pCur may point to any -** entry or to no entry at all. In this case this function has to seek +** If the seekResult parameter is non-zero, then the caller guarantees that +** cursor pCur is pointing at the existing copy of a row that is to be +** overwritten. If the seekResult parameter is 0, then cursor pCur may +** point to any entry or to no entry at all and so this function has to seek ** the cursor before the new key can be inserted. */ int sqlite3BtreeInsert( BtCursor *pCur, /* Insert data into the table of this cursor */ const void *pKey, i64 nKey, /* The key of the new record */ const void *pData, int nData, /* The data of the new record */ int nZero, /* Number of extra 0 bytes to append to data */ int appendBias, /* True if this is likely an append */ - int seekResult /* Result of prior sqlite3BtreeMoveto() call */ + int seekResult /* Result of prior MovetoUnpacked() call */ ){ int rc; - int loc = seekResult; - int szNew; + int loc = seekResult; /* -1: before desired location +1: after */ + int szNew = 0; int idx; MemPage *pPage; Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; unsigned char *oldCell; unsigned char *newCell = 0; + + if( pCur->eState==CURSOR_FAULT ){ + assert( pCur->skipNext!=SQLITE_OK ); + return pCur->skipNext; + } assert( cursorHoldsMutex(pCur) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( !pBt->readOnly ); - assert( pCur->wrFlag ); - rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey); - if( rc ){ - /* The table pCur points to has a read lock */ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; - } - if( pCur->eState==CURSOR_FAULT ){ - return pCur->skip; + assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE && !pBt->readOnly ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + + /* Assert that the caller has been consistent. If this cursor was opened + ** expecting an index b-tree, then the caller should be inserting blob + ** keys with no associated data. If the cursor was opened expecting an + ** intkey table, the caller should be inserting integer keys with a + ** blob of associated data. */ + assert( (pKey==0)==(pCur->pKeyInfo==0) ); + + /* If this is an insert into a table b-tree, invalidate any incrblob + ** cursors open on the row being replaced (assuming this is a replace + ** operation - if it is not, the following is a no-op). */ + if( pCur->pKeyInfo==0 ){ + invalidateIncrblobCursors(p, nKey, 0); } /* Save the positions of any other cursors open on this table. ** - ** In some cases, the call to sqlite3BtreeMoveto() below is a no-op. For + ** In some cases, the call to btreeMoveto() below is a no-op. For ** example, when inserting data into a table with auto-generated integer ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the ** integer key to use. It then calls this function to actually insert the - ** data into the intkey B-Tree. In this case sqlite3BtreeMoveto() recognizes + ** data into the intkey B-Tree. In this case btreeMoveto() recognizes ** that the cursor is already where it needs to be and returns without ** doing any work. To avoid thwarting these optimizations, it is important ** not to clear the cursor here. */ - if( - SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || (!loc && - SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc)) - )){ - return rc; + rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); + if( rc ) return rc; + if( !loc ){ + rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc); + if( rc ) return rc; } assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); pPage = pCur->apPage[pCur->iPage]; assert( pPage->intKey || nKey>=0 ); assert( pPage->leaf || !pPage->intKey ); + TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", pCur->pgnoRoot, nKey, nData, pPage->pgno, loc==0 ? "overwrite" : "new entry")); assert( pPage->isInit ); allocateTempSpace(pBt); @@ -6377,22 +6529,19 @@ if( !pPage->leaf ){ memcpy(newCell, oldCell, 4); } szOld = cellSizePtr(pPage, oldCell); rc = clearCell(pPage, oldCell); + dropCell(pPage, idx, szOld, &rc); if( rc ) goto end_insert; - rc = dropCell(pPage, idx, szOld); - if( rc!=SQLITE_OK ) { - goto end_insert; - } }else if( loc<0 && pPage->nCell>0 ){ assert( pPage->leaf ); idx = ++pCur->aiIdx[pCur->iPage]; }else{ assert( pPage->leaf ); } - rc = insertCell(pPage, idx, newCell, szNew, 0, 0); + insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); /* If no error has occured and pPage has an overflow cell, call balance() ** to redistribute the cells within the tree. Since balance() may move ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey @@ -6446,20 +6595,23 @@ assert( cursorHoldsMutex(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); assert( pCur->wrFlag ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + assert( !hasReadConflicts(p, pCur->pgnoRoot) ); + if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) || NEVER(pCur->eState!=CURSOR_VALID) ){ return SQLITE_ERROR; /* Something has gone awry. */ } - rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; /* The table pCur points to has a read lock */ + /* If this is a delete operation to remove a row from a table b-tree, + ** invalidate any incrblob cursors open on the row being deleted. */ + if( pCur->pKeyInfo==0 ){ + invalidateIncrblobCursors(p, pCur->info.nKey, 0); } iCellDepth = pCur->iPage; iCellIdx = pCur->aiIdx[iCellDepth]; pPage = pCur->apPage[iCellDepth]; @@ -6472,26 +6624,26 @@ ** of the 'next' entry, as the previous entry is always a part of the ** sub-tree headed by the child page of the cell being deleted. This makes ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ int notUsed; - if( SQLITE_OK!=(rc = sqlite3BtreePrevious(pCur, ¬Used)) ){ - return rc; - } + rc = sqlite3BtreePrevious(pCur, ¬Used); + if( rc ) return rc; } /* Save the positions of any other cursors open on this table before ** making any modifications. Make the page containing the entry to be ** deleted writable. Then free any overflow pages associated with the - ** entry and finally remove the cell itself from within the page. */ - if( SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) - || SQLITE_OK!=(rc = sqlite3PagerWrite(pPage->pDbPage)) - || SQLITE_OK!=(rc = clearCell(pPage, pCell)) - || SQLITE_OK!=(rc = dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell))) - ){ - return rc; - } + ** entry and finally remove the cell itself from within the page. + */ + rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); + if( rc ) return rc; + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc ) return rc; + rc = clearCell(pPage, pCell); + dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), &rc); + if( rc ) return rc; /* If the cell deleted was not located on a leaf page, then the cursor ** is currently pointing to the largest entry in the sub-tree headed ** by the child-page of the cell that was just deleted from an internal ** node. The cell from the leaf node needs to be moved to the internal @@ -6507,16 +6659,14 @@ assert( MX_CELL_SIZE(pBt)>=nCell ); allocateTempSpace(pBt); pTmp = pBt->pTmpSpace; - if( SQLITE_OK!=(rc = sqlite3PagerWrite(pLeaf->pDbPage)) - || SQLITE_OK!=(rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n)) - || SQLITE_OK!=(rc = dropCell(pLeaf, pLeaf->nCell-1, nCell)) - ){ - return rc; - } + rc = sqlite3PagerWrite(pLeaf->pDbPage); + insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); + dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); + if( rc ) return rc; } /* Balance the tree. If the entry deleted was located on a leaf page, ** then the cursor still points to that page. In this case the first ** call to balance() repairs the tree, and the if(...) condition is @@ -6586,14 +6736,11 @@ /* Read the value of meta[3] from the database to determine where the ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ - rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); - if( rc!=SQLITE_OK ){ - return rc; - } + sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); pgnoRoot++; /* The new root-page may not be allocated on a pointer-map page, or the ** PENDING_BYTE page. */ @@ -6617,17 +6764,17 @@ ** the new table (assuming an error did not occur). But we were ** allocated pgnoMove. If required (i.e. if it was not allocated ** by extending the file), the current page at position pgnoMove ** is already journaled. */ - u8 eType; - Pgno iPtrPage; + u8 eType = 0; + Pgno iPtrPage = 0; releasePage(pPageMove); /* Move the page currently at pgnoRoot to pgnoMove. */ - rc = sqlite3BtreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ @@ -6644,11 +6791,11 @@ /* Obtain the page at pgnoRoot */ if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3BtreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3PagerWrite(pRoot->pDbPage); if( rc!=SQLITE_OK ){ @@ -6658,11 +6805,11 @@ }else{ pRoot = pPageMove; } /* Update the pointer-map and meta-data with the new root-page number. */ - rc = ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0); + ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); if( rc ){ releasePage(pRoot); return rc; } rc = sqlite3BtreeUpdateMeta(p, 4, pgnoRoot); @@ -6698,11 +6845,11 @@ BtShared *pBt, /* The BTree that contains the table */ Pgno pgno, /* Page number to clear */ int freePageFlag, /* Deallocate page if true */ int *pnChange ){ - MemPage *pPage = 0; + MemPage *pPage; int rc; unsigned char *pCell; int i; assert( sqlite3_mutex_held(pBt->mutex) ); @@ -6709,11 +6856,11 @@ if( pgno>pagerPagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } rc = getAndInitPage(pBt, pgno, &pPage); - if( rc ) goto cleardatabasepage_out; + if( rc ) return rc; for(i=0; inCell; i++){ pCell = findCell(pPage, i); if( !pPage->leaf ){ rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); if( rc ) goto cleardatabasepage_out; @@ -6727,11 +6874,11 @@ }else if( pnChange ){ assert( pPage->intKey ); *pnChange += pPage->nCell; } if( freePageFlag ){ - rc = freePage(pPage); + freePage(pPage, &rc); }else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){ zeroPage(pPage, pPage->aData[0] | PTF_LEAF); } cleardatabasepage_out: @@ -6755,15 +6902,18 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){ int rc; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); assert( p->inTrans==TRANS_WRITE ); - if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){ - /* nothing to do */ - }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ - /* nothing to do */ - }else{ + + /* Invalidate all incrblob cursors open on table iTable (assuming iTable + ** is the root of a table b-tree - if it is not, the following call is + ** a no-op). */ + invalidateIncrblobCursors(p, 0, 1); + + rc = saveAllCursors(pBt, (Pgno)iTable, 0); + if( SQLITE_OK==rc ){ rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); } sqlite3BtreeLeave(p); return rc; } @@ -6799,17 +6949,19 @@ /* It is illegal to drop a table if any cursors are open on the ** database. This is because in auto-vacuum mode the backend may ** need to move another root-page to fill a gap left by the deleted ** root page. If an open cursor was using this page a problem would ** occur. + ** + ** This error is caught long before control reaches this point. */ - if( pBt->pCursor ){ + if( NEVER(pBt->pCursor) ){ sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db); return SQLITE_LOCKED_SHAREDCACHE; } - rc = sqlite3BtreeGetPage(pBt, (Pgno)iTable, &pPage, 0); + rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); if( rc ) return rc; rc = sqlite3BtreeClearTable(p, iTable, 0); if( rc ){ releasePage(pPage); return rc; @@ -6817,26 +6969,22 @@ *piMoved = 0; if( iTable>1 ){ #ifdef SQLITE_OMIT_AUTOVACUUM - rc = freePage(pPage); + freePage(pPage, &rc); releasePage(pPage); #else if( pBt->autoVacuum ){ Pgno maxRootPgno; - rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); - if( rc!=SQLITE_OK ){ - releasePage(pPage); - return rc; - } + sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); if( iTable==maxRootPgno ){ /* If the table being dropped is the table with the largest root-page ** number in the database, put the root page on the free list. */ - rc = freePage(pPage); + freePage(pPage, &rc); releasePage(pPage); if( rc!=SQLITE_OK ){ return rc; } }else{ @@ -6844,24 +6992,22 @@ ** number in the database. So move the page that does into the ** gap left by the deleted root-page. */ MemPage *pMove; releasePage(pPage); - rc = sqlite3BtreeGetPage(pBt, maxRootPgno, &pMove, 0); + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); if( rc!=SQLITE_OK ){ return rc; } rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); releasePage(pMove); if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3BtreeGetPage(pBt, maxRootPgno, &pMove, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = freePage(pMove); + pMove = 0; + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); + freePage(pMove, &rc); releasePage(pMove); if( rc!=SQLITE_OK ){ return rc; } *piMoved = maxRootPgno; @@ -6871,26 +7017,27 @@ ** is the old value less one, less one more if that happens to ** be a root-page number, less one again if that is the ** PENDING_BYTE_PAGE. */ maxRootPgno--; - if( maxRootPgno==PENDING_BYTE_PAGE(pBt) ){ - maxRootPgno--; - } - if( maxRootPgno==PTRMAP_PAGENO(pBt, maxRootPgno) ){ + while( maxRootPgno==PENDING_BYTE_PAGE(pBt) + || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ maxRootPgno--; } assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); }else{ - rc = freePage(pPage); + freePage(pPage, &rc); releasePage(pPage); } #endif }else{ - /* If sqlite3BtreeDropTable was called on page 1. */ + /* If sqlite3BtreeDropTable was called on page 1. + ** This really never should happen except in a corrupt + ** database. + */ zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); releasePage(pPage); } return rc; } @@ -6902,84 +7049,40 @@ return rc; } /* +** This function may only be called if the b-tree connection already +** has a read or write transaction open on the database. +** ** Read the meta-information out of a database file. Meta[0] ** is the number of free pages currently in the database. Meta[1] ** through meta[15] are available for use by higher layers. Meta[0] ** is read-only, the others are read/write. ** ** The schema layer numbers meta values differently. At the schema ** layer (and the SetCookie and ReadCookie opcodes) the number of ** free pages is not visible. So Cookie[0] is the same as Meta[1]. */ -int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ - DbPage *pDbPage = 0; - int rc; - unsigned char *pP1; +void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - - /* Reading a meta-data value requires a read-lock on page 1 (and hence - ** the sqlite_master table. We grab this lock regardless of whether or - ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page - ** 1 is treated as a special case by querySharedCacheTableLock() - ** and setSharedCacheTableLock()). - */ - rc = querySharedCacheTableLock(p, 1, READ_LOCK); - if( rc!=SQLITE_OK ){ - sqlite3BtreeLeave(p); - return rc; - } - + assert( p->inTrans>TRANS_NONE ); + assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); + assert( pBt->pPage1 ); assert( idx>=0 && idx<=15 ); - if( pBt->pPage1 ){ - /* The b-tree is already holding a reference to page 1 of the database - ** file. In this case the required meta-data value can be read directly - ** from the page data of this reference. This is slightly faster than - ** requesting a new reference from the pager layer. - */ - pP1 = (unsigned char *)pBt->pPage1->aData; - }else{ - /* The b-tree does not have a reference to page 1 of the database file. - ** Obtain one from the pager layer. - */ - rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage); - if( rc ){ - sqlite3BtreeLeave(p); - return rc; - } - pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage); - } - *pMeta = get4byte(&pP1[36 + idx*4]); - - /* If the b-tree is not holding a reference to page 1, then one was - ** requested from the pager layer in the above block. Release it now. - */ - if( !pBt->pPage1 ){ - sqlite3PagerUnref(pDbPage); - } - - /* If autovacuumed is disabled in this build but we are trying to - ** access an autovacuumed database, then make the database readonly. - */ + + *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); + + /* If auto-vacuum is disabled in this build and this is an auto-vacuum + ** database, mark the database as read-only. */ #ifdef SQLITE_OMIT_AUTOVACUUM if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1; #endif - /* If there is currently an open transaction, grab a read-lock - ** on page 1 of the database file. This is done to make sure that - ** no other connection can modify the meta value just read from - ** the database until the transaction is concluded. - */ - if( p->inTrans>0 ){ - rc = setSharedCacheTableLock(p, 1, READ_LOCK); - } sqlite3BtreeLeave(p); - return rc; } /* ** Write meta-information back into the database. Meta[0] is ** read-only and may not be written. @@ -7006,27 +7109,10 @@ } sqlite3BtreeLeave(p); return rc; } -/* -** Return the flag byte at the beginning of the page that the cursor -** is currently pointing to. -*/ -int sqlite3BtreeFlags(BtCursor *pCur){ - /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call - ** restoreCursorPosition() here. - */ - MemPage *pPage; - restoreCursorPosition(pCur); - pPage = pCur->apPage[pCur->iPage]; - assert( cursorHoldsMutex(pCur) ); - assert( pPage!=0 ); - assert( pPage->pBt==pCur->pBt ); - return pPage->aData[pPage->hdrOffset]; -} - #ifndef SQLITE_OMIT_BTREECOUNT /* ** The first argument, pCur, is a cursor opened on some b-tree. Count the ** number of entries in the b-tree and write the result to *pnEntry. ** @@ -7070,11 +7156,11 @@ if( pCur->iPage==0 ){ /* All pages of the b-tree have been visited. Return successfully. */ *pnEntry = nEntry; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); pCur->aiIdx[pCur->iPage]++; pPage = pCur->apPage[pCur->iPage]; } @@ -7297,20 +7383,23 @@ */ pBt = pCheck->pBt; usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage, zParentContext) ) return 0; - if( (rc = sqlite3BtreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1; + if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ checkAppendMsg(pCheck, zContext, "unable to get the page. error code=%d", rc); return 0; } - if( (rc = sqlite3BtreeInitPage(pPage))!=0 ){ + + /* Clear MemPage.isInit to make sure the corruption detection code in + ** btreeInitPage() is executed. */ + pPage->isInit = 0; + if( (rc = btreeInitPage(pPage))!=0 ){ assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ checkAppendMsg(pCheck, zContext, - "sqlite3BtreeInitPage() returns error code %d", rc); + "btreeInitPage() returns error code %d", rc); releasePage(pPage); return 0; } /* Check out all the cells. @@ -7324,11 +7413,11 @@ /* Check payload overflow pages */ sqlite3_snprintf(sizeof(zContext), zContext, "On tree page %d cell %d: ", iPage, i); pCell = findCell(pPage,i); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); sz = info.nData; if( !pPage->intKey ) sz += (int)info.nKey; assert( sz==info.nPayload ); if( (sz>info.nLocal) && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize]) @@ -7378,44 +7467,40 @@ hit = sqlite3PageMalloc( pBt->pageSize ); if( hit==0 ){ pCheck->mallocFailed = 1; }else{ u16 contentOffset = get2byte(&data[hdr+5]); - if (contentOffset > usableSize) { - checkAppendMsg(pCheck, 0, - "Corruption detected in header on page %d",iPage,0); - goto check_page_abort; - } + assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ memset(hit+contentOffset, 0, usableSize-contentOffset); memset(hit, 1, contentOffset); nCell = get2byte(&data[hdr+3]); cellStart = hdr + 12 - 4*pPage->leaf; for(i=0; i=usableSize || pc<0 ){ + if( (pc+size-1)>=usableSize ){ checkAppendMsg(pCheck, 0, "Corruption detected in cell %d on page %d",i,iPage,0); }else{ for(j=pc+size-1; j>=pc; j--) hit[j]++; } } - for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i=usableSize || i<0 ){ - checkAppendMsg(pCheck, 0, - "Corruption detected in cell %d on page %d",i,iPage,0); - }else{ - for(j=i+size-1; j>=i; j--) hit[j]++; - } - i = get2byte(&data[i]); + i = get2byte(&data[hdr+1]); + while( i>0 ){ + int size, j; + assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */ + size = get2byte(&data[i+2]); + assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */ + for(j=i+size-1; j>=i; j--) hit[j]++; + j = get2byte(&data[i]); + assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */ + assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */ + i = j; } for(i=cnt=0; i1 ){ @@ -7424,17 +7509,15 @@ break; } } if( cnt!=data[hdr+7] ){ checkAppendMsg(pCheck, 0, - "Fragmented space is %d byte reported as %d on page %d", + "Fragmentation of %d bytes reported as %d on page %d", cnt, data[hdr+7], iPage); } } -check_page_abort: - if (hit) sqlite3PageFree(hit); - + sqlite3PageFree(hit); releasePage(pPage); return depth+1; } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ @@ -7441,10 +7524,13 @@ #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* ** This routine does a complete check of the given BTree file. aRoot[] is ** an array of pages numbers were each page number is the root page of ** a table. nRoot is the number of entries in aRoot. +** +** A read-only or read-write transaction must be opened before calling +** this function. ** ** Write the number of error seen in *pnErr. Except for some memory ** allocation errors, an error message held in memory obtained from ** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is ** returned. If a memory allocation error occurs, NULL is returned. @@ -7461,31 +7547,25 @@ IntegrityCk sCheck; BtShared *pBt = p->pBt; char zErr[100]; sqlite3BtreeEnter(p); + assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); nRef = sqlite3PagerRefcount(pBt->pPager); - if( lockBtreeWithRetry(p)!=SQLITE_OK ){ - *pnErr = 1; - sqlite3BtreeLeave(p); - return sqlite3DbStrDup(0, "cannot acquire a read lock on the database"); - } sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; sCheck.nPage = pagerPagecount(sCheck.pBt); sCheck.mxErr = mxErr; sCheck.nErr = 0; sCheck.mallocFailed = 0; *pnErr = 0; if( sCheck.nPage==0 ){ - unlockBtreeIfUnused(pBt); sqlite3BtreeLeave(p); return 0; } sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); if( !sCheck.anRef ){ - unlockBtreeIfUnused(pBt); *pnErr = 1; sqlite3BtreeLeave(p); return 0; } for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; } @@ -7536,11 +7616,10 @@ /* Make sure this analysis did not leave any unref() pages. ** This is an internal consistency check; an integrity check ** of the integrity check. */ - unlockBtreeIfUnused(pBt); if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){ checkAppendMsg(&sCheck, 0, "Outstanding page count goes from %d to %d during this analysis", nRef, sqlite3PagerRefcount(pBt->pPager) ); @@ -7661,14 +7740,16 @@ ** lock is a write lock if isWritelock is true or a read lock ** if it is false. */ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ int rc = SQLITE_OK; + assert( p->inTrans!=TRANS_NONE ); if( p->sharable ){ u8 lockType = READ_LOCK + isWriteLock; assert( READ_LOCK+1==WRITE_LOCK ); assert( isWriteLock==0 || isWriteLock==1 ); + sqlite3BtreeEnter(p); rc = querySharedCacheTableLock(p, iTab, lockType); if( rc==SQLITE_OK ){ rc = setSharedCacheTableLock(p, iTab, lockType); } @@ -7681,47 +7762,47 @@ #ifndef SQLITE_OMIT_INCRBLOB /* ** Argument pCsr must be a cursor opened for writing on an ** INTKEY table currently pointing at a valid table entry. ** This function modifies the data stored as part of that entry. -** Only the data content may only be modified, it is not possible -** to change the length of the data stored. +** +** Only the data content may only be modified, it is not possible to +** change the length of the data stored. If this function is called with +** parameters that attempt to write past the end of the existing data, +** no modifications are made and SQLITE_CORRUPT is returned. */ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ int rc; - assert( cursorHoldsMutex(pCsr) ); assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); - assert(pCsr->isIncrblobHandle); + assert( pCsr->isIncrblobHandle ); - restoreCursorPosition(pCsr); + rc = restoreCursorPosition(pCsr); + if( rc!=SQLITE_OK ){ + return rc; + } assert( pCsr->eState!=CURSOR_REQUIRESEEK ); if( pCsr->eState!=CURSOR_VALID ){ return SQLITE_ABORT; } - /* Check some preconditions: + /* Check some assumptions: ** (a) the cursor is open for writing, - ** (b) there is no read-lock on the table being modified and - ** (c) the cursor points at a valid row of an intKey table. + ** (b) there is a read/write transaction open, + ** (c) the connection holds a write-lock on the table (if required), + ** (d) there are no conflicting read-locks, and + ** (e) the cursor points at a valid row of an intKey table. */ if( !pCsr->wrFlag ){ return SQLITE_READONLY; } - assert( !pCsr->pBt->readOnly - && pCsr->pBt->inTransaction==TRANS_WRITE ); - rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0); - if( rc!=SQLITE_OK ){ - /* The table pCur points to has a read lock */ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; - } - if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){ - return SQLITE_ERROR; - } - - return accessPayload(pCsr, offset, amt, (unsigned char *)z, 0, 1); + assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE ); + assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); + assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); + assert( pCsr->apPage[pCsr->iPage]->intKey ); + + return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); } /* ** Set a flag on this cursor to cache the locations of pages from the ** overflow list for the current row. This is used by cursors opened Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -10,12 +10,10 @@ ** ************************************************************************* ** This header file defines the interface that the sqlite B-Tree file ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. -** -** @(#) $Id: btree.h,v 1.116 2009/06/03 11:25:07 danielk1977 Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ /* TODO: This definition is just included so other modules compile. It @@ -95,12 +93,12 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInReadTrans(Btree*); int sqlite3BtreeIsInBackup(Btree*); void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); -int sqlite3BtreeSchemaLocked(Btree *); -int sqlite3BtreeLockTable(Btree *, int, u8); +int sqlite3BtreeSchemaLocked(Btree *pBtree); +int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); int sqlite3BtreeSavepoint(Btree *, int, int); const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); int sqlite3BtreeCopyFile(Btree *, Btree *); @@ -116,11 +114,11 @@ int sqlite3BtreeDropTable(Btree*, int, int*); int sqlite3BtreeClearTable(Btree*, int, int*); void sqlite3BtreeTripAllCursors(Btree*, int); -int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); +void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); /* ** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta ** should be one of the following values. The integer values are assigned @@ -150,17 +148,10 @@ BtCursor *pCursor /* Space to write cursor structure */ ); int sqlite3BtreeCursorSize(void); int sqlite3BtreeCloseCursor(BtCursor*); -int sqlite3BtreeMoveto( - BtCursor*, - const void *pKey, - i64 nKey, - int bias, - int *pRes -); int sqlite3BtreeMovetoUnpacked( BtCursor*, UnpackedRecord *pUnKey, i64 intKey, int bias, @@ -173,11 +164,10 @@ int nZero, int bias, int seekResult); int sqlite3BtreeFirst(BtCursor*, int *pRes); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int *pRes); int sqlite3BtreeEof(BtCursor*); -int sqlite3BtreeFlags(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int *pRes); int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); @@ -190,10 +180,14 @@ struct Pager *sqlite3BtreePager(Btree*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); + +#ifndef NDEBUG +int sqlite3BtreeCursorIsValid(BtCursor*); +#endif #ifndef SQLITE_OMIT_BTREECOUNT int sqlite3BtreeCount(BtCursor *, i64 *); #endif Index: src/btreeInt.h ================================================================== --- src/btreeInt.h +++ src/btreeInt.h @@ -7,12 +7,10 @@ ** 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. ** ************************************************************************* -** $Id: btreeInt.h,v 1.49 2009/06/24 05:40:34 danielk1977 Exp $ -** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: ** "Sorting And Searching", pages 473-480. Addison-Wesley @@ -46,13 +44,13 @@ ** ** FORMAT DETAILS ** ** The file is divided into pages. The first page is called page 1, ** the second is page 2, and so forth. A page number of zero indicates -** "no such page". The page size can be anything between 512 and 65536. -** Each page can be either a btree page, a freelist page or an overflow -** page. +** "no such page". The page size can be any power of 2 between 512 and 32768. +** Each page can be either a btree page, a freelist page, an overflow +** page, or a pointer-map page. ** ** The first page is always a btree page. The first 100 bytes of the first ** page contain a special header (the "file header") that describes the file. ** The format of the file header is as follows: ** @@ -300,21 +298,39 @@ ** to the end. EXTRA_SIZE is the number of bytes of space needed to hold ** that extra information. */ #define EXTRA_SIZE sizeof(MemPage) +/* +** A linked list of the following structures is stored at BtShared.pLock. +** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor +** is opened on the table with root page BtShared.iTable. Locks are removed +** from this list when a transaction is committed or rolled back, or when +** a btree handle is closed. +*/ +struct BtLock { + Btree *pBtree; /* Btree handle holding this lock */ + Pgno iTable; /* Root page of table */ + u8 eLock; /* READ_LOCK or WRITE_LOCK */ + BtLock *pNext; /* Next in BtShared.pLock list */ +}; + +/* Candidate values for BtLock.eLock */ +#define READ_LOCK 1 +#define WRITE_LOCK 2 + /* A Btree handle ** ** A database connection contains a pointer to an instance of ** this object for every database file that it has open. This structure ** is opaque to the database connection. The database connection cannot ** see the internals of this structure and only deals with pointers to ** this structure. ** ** For some database files, the same underlying database cache might be -** shared between multiple connections. In that case, each contection -** has it own pointer to this object. But each instance of this object +** shared between multiple connections. In that case, each connection +** has it own instance of this object. But each instance of this object ** points to the same BtShared object. The database cache and the ** schema associated with the database file are all contained within ** the BtShared object. ** ** All fields in this structure are accessed under sqlite3.mutex. @@ -331,10 +347,13 @@ u8 locked; /* True if db currently has pBt locked */ int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ int nBackup; /* Number of backup operations reading this btree */ Btree *pNext; /* List of other sharable Btrees from the same db */ Btree *pPrev; /* Back pointer of the same list */ +#ifndef SQLITE_OMIT_SHARED_CACHE + BtLock lock; /* Object used to lock page 1 */ +#endif }; /* ** Btree.inTrans may take one of the following values. ** @@ -448,11 +467,11 @@ ** b-tree within a database file. ** ** The entry is identified by its MemPage and the index in ** MemPage.aCell[] of the entry. ** -** When a single database file can shared by two more database connections, +** A single database file can shared by two more database connections, ** but cursors cannot be shared. Each cursor is associated with a ** particular database connection identified BtCursor.pBtree.db. ** ** Fields in this structure are accessed under the BtShared.mutex ** found at self->pBt->mutex. @@ -469,11 +488,11 @@ u8 atLast; /* Cursor pointing to the last entry */ u8 validNKey; /* True if info.nKey is valid */ u8 eState; /* One of the CURSOR_XXX constants (see below) */ void *pKey; /* Saved key that was cursor's last known position */ i64 nKey; /* Size of pKey, or last integer key */ - int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */ + int skipNext; /* Prev() is noop if negative. Next() is noop if positive */ #ifndef SQLITE_OMIT_INCRBLOB u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ Pgno *aOverflow; /* Cache of overflow page locations */ #endif i16 iPage; /* Index of current page in apPage */ @@ -514,28 +533,10 @@ /* ** The database page the PENDING_BYTE occupies. This page is never used. */ # define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) -/* -** A linked list of the following structures is stored at BtShared.pLock. -** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor -** is opened on the table with root page BtShared.iTable. Locks are removed -** from this list when a transaction is committed or rolled back, or when -** a btree handle is closed. -*/ -struct BtLock { - Btree *pBtree; /* Btree handle holding this lock */ - Pgno iTable; /* Root page of table */ - u8 eLock; /* READ_LOCK or WRITE_LOCK */ - BtLock *pNext; /* Next in BtShared.pLock list */ -}; - -/* Candidate values for BtLock.eLock */ -#define READ_LOCK 1 -#define WRITE_LOCK 2 - /* ** These macros define the location of the pointer-map entry for a ** database page. The first argument to each is the number of usable ** bytes on each page of the database (often 1024). The second is the ** page number to look up in the pointer map. @@ -633,20 +634,5 @@ */ #define get2byte(x) ((x)[0]<<8 | (x)[1]) #define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v)) #define get4byte sqlite3Get4byte #define put4byte sqlite3Put4byte - -/* -** Internal routines that should be accessed by the btree layer only. -*/ -int sqlite3BtreeGetPage(BtShared*, Pgno, MemPage**, int); -int sqlite3BtreeInitPage(MemPage *pPage); -void sqlite3BtreeParseCellPtr(MemPage*, u8*, CellInfo*); -void sqlite3BtreeParseCell(MemPage*, int, CellInfo*); -int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur); -void sqlite3BtreeMoveToParent(BtCursor *pCur); - -#ifdef SQLITE_TEST -void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur); -void sqlite3BtreeReleaseTempCursor(BtCursor *pCur); -#endif Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -19,12 +19,10 @@ ** DROP INDEX ** creating ID lists ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK -** -** $Id: build.c,v 1.554 2009/06/25 11:50:21 drh Exp $ */ #include "sqliteInt.h" /* ** This routine is called when a new SQL statement is beginning to @@ -62,35 +60,36 @@ int iDb, /* Index of the database containing the table to lock */ int iTab, /* Root page number of the table to be locked */ u8 isWriteLock, /* True for a write lock */ const char *zName /* Name of the table to be locked */ ){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); int i; int nBytes; TableLock *p; - assert( iDb>=0 ); - for(i=0; inTableLock; i++){ - p = &pParse->aTableLock[i]; + + for(i=0; inTableLock; i++){ + p = &pToplevel->aTableLock[i]; if( p->iDb==iDb && p->iTab==iTab ){ p->isWriteLock = (p->isWriteLock || isWriteLock); return; } } - nBytes = sizeof(TableLock) * (pParse->nTableLock+1); - pParse->aTableLock = - sqlite3DbReallocOrFree(pParse->db, pParse->aTableLock, nBytes); - if( pParse->aTableLock ){ - p = &pParse->aTableLock[pParse->nTableLock++]; + nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); + pToplevel->aTableLock = + sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); + if( pToplevel->aTableLock ){ + p = &pToplevel->aTableLock[pToplevel->nTableLock++]; p->iDb = iDb; p->iTab = iTab; p->isWriteLock = isWriteLock; p->zName = zName; }else{ - pParse->nTableLock = 0; - pParse->db->mallocFailed = 1; + pToplevel->nTableLock = 0; + pToplevel->db->mallocFailed = 1; } } /* ** Code an OP_TableLock instruction for each table locked by the @@ -135,10 +134,12 @@ /* Begin by generating some termination code at the end of the ** vdbe program */ v = sqlite3GetVdbe(pParse); + assert( !pParse->isMultiWrite + || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); if( v ){ sqlite3VdbeAddOp0(v, OP_Halt); /* The cookie mask contains one bit for each database file open. ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are @@ -160,11 +161,11 @@ } #ifndef SQLITE_OMIT_VIRTUALTABLE { int i; for(i=0; inVtabLock; i++){ - char *vtab = (char *)pParse->apVtabLock[i]->pVtab; + char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); } pParse->nVtabLock = 0; } #endif @@ -191,12 +192,16 @@ #ifdef SQLITE_DEBUG FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; sqlite3VdbeTrace(v, trace); #endif assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ + /* A minimum of one cursor is required if autoincrement is used + * See ticket [a696379c1f08866] */ + if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1; sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem, - pParse->nTab, pParse->explain); + pParse->nTab, pParse->nMaxArg, pParse->explain, + pParse->isMultiWrite && pParse->mayAbort); pParse->rc = SQLITE_DONE; pParse->colNamesSet = 0; }else if( pParse->rc==SQLITE_OK ){ pParse->rc = SQLITE_ERROR; } @@ -340,11 +345,13 @@ /* ** Reclaim the memory used by an index */ static void freeIndex(Index *p){ sqlite3 *db = p->pTable->dbMem; - /* testcase( db==0 ); */ +#ifndef SQLITE_OMIT_ANALYZE + sqlite3DeleteIndexSamples(p); +#endif sqlite3DbFree(db, p->zColAff); sqlite3DbFree(db, p); } /* @@ -422,10 +429,11 @@ } if( iDb>0 ) return; } assert( iDb==0 ); db->flags &= ~SQLITE_InternChanges; + sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); /* If one or more of the auxiliary database files has been closed, ** then remove them from the auxiliary database list. We take the ** opportunity to do this here since we have just deleted all of the @@ -492,11 +500,10 @@ ** memory structures of the indices and foreign keys associated with ** the table. */ void sqlite3DeleteTable(Table *pTable){ Index *pIndex, *pNext; - FKey *pFKey, *pNextFKey; sqlite3 *db; if( pTable==0 ) return; db = pTable->dbMem; testcase( db==0 ); @@ -514,17 +521,12 @@ pNext = pIndex->pNext; assert( pIndex->pSchema==pTable->pSchema ); sqlite3DeleteIndex(pIndex); } -#ifndef SQLITE_OMIT_FOREIGN_KEY - /* Delete all foreign keys associated with this table. */ - for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ - pNextFKey = pFKey->pNextFrom; - sqlite3DbFree(db, pFKey); - } -#endif + /* Delete any foreign keys attached to this table. */ + sqlite3FkDelete(pTable); /* Delete the Table structure itself. */ sqliteResetColumnNames(pTable); sqlite3DbFree(db, pTable->zName); @@ -1162,11 +1164,15 @@ #ifndef SQLITE_OMIT_AUTOINCREMENT sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " "INTEGER PRIMARY KEY"); #endif }else{ - sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); + Index *p; + p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); + if( p ){ + p->autoIndex = 2; + } pList = 0; } primary_key_exit: sqlite3ExprListDelete(pParse->db, pList); @@ -1253,11 +1259,11 @@ u8 initbusy = db->init.busy; CollSeq *pColl; pColl = sqlite3FindCollSeq(db, enc, zName, initbusy); if( !initbusy && (!pColl || !pColl->xCmp) ){ - pColl = sqlite3GetCollSeq(db, pColl, zName); + pColl = sqlite3GetCollSeq(db, enc, pColl, zName); if( !pColl ){ sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); } } @@ -1873,10 +1879,11 @@ */ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb); + sqlite3MayAbort(pParse); #ifndef SQLITE_OMIT_AUTOVACUUM /* OP_Destroy stores an in integer r1. If this integer ** is non-zero, then it is the root page number of a table moved to ** location iTable. The following code modifies the sqlite_master table to ** reflect this. @@ -2000,11 +2007,11 @@ code = SQLITE_DROP_VIEW; } #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( IsVirtual(pTab) ){ code = SQLITE_DROP_VTABLE; - zArg2 = pTab->pMod->zName; + zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName; #endif }else{ if( !OMIT_TEMPDB && iDb==1 ){ code = SQLITE_DROP_TEMP_TABLE; }else{ @@ -2050,10 +2057,11 @@ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ sqlite3VdbeAddOp0(v, OP_VBegin); } #endif + sqlite3FkDropTable(pParse, pName, pTab); /* Drop all triggers associated with the table being dropped. Code ** is generated to remove entries from sqlite_master and/or ** sqlite_temp_master if required. */ @@ -2140,10 +2148,11 @@ int flags /* Conflict resolution algorithms. */ ){ sqlite3 *db = pParse->db; #ifndef SQLITE_OMIT_FOREIGN_KEY FKey *pFKey = 0; + FKey *pNextTo; Table *p = pParse->pNewTable; int nByte; int i; int nCol; char *z; @@ -2214,13 +2223,25 @@ z[n] = 0; z += n+1; } } pFKey->isDeferred = 0; - pFKey->deleteConf = (u8)(flags & 0xff); - pFKey->updateConf = (u8)((flags >> 8 ) & 0xff); - pFKey->insertConf = (u8)((flags >> 16 ) & 0xff); + pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */ + pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */ + + pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash, + pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey + ); + if( pNextTo==pFKey ){ + db->mallocFailed = 1; + goto fk_end; + } + if( pNextTo ){ + assert( pNextTo->pPrevTo==0 ); + pFKey->pNextTo = pNextTo; + pNextTo->pPrevTo = pFKey; + } /* Link the foreign key to the table as the last step. */ p->pFKey = pFKey; pFKey = 0; @@ -2242,11 +2263,11 @@ void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){ #ifndef SQLITE_OMIT_FOREIGN_KEY Table *pTab; FKey *pFKey; if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; - assert( isDeferred==0 || isDeferred==1 ); + assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */ pFKey->isDeferred = (u8)isDeferred; #endif } /* @@ -2314,12 +2335,12 @@ ** opcode use the values stored within seems dangerous. However, since ** we can be sure that no other temp registers have been allocated ** since sqlite3ReleaseTempRange() was called, it is safe to do so. */ sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, 0, - "indexed columns are not unique", P4_STATIC); + sqlite3HaltConstraint( + pParse, OE_Abort, "indexed columns are not unique", P4_STATIC); } sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); @@ -2337,12 +2358,16 @@ ** currently being constructed by a CREATE TABLE statement. ** ** pList is a list of columns to be indexed. pList will be NULL if this ** is a primary key or unique-constraint on the most recent column added ** to the table currently under construction. +** +** If the index is created successfully, return a pointer to the new Index +** structure. This is used by sqlite3AddPrimaryKey() to mark the index +** as the tables primary key (Index.autoIndex==2). */ -void sqlite3CreateIndex( +Index *sqlite3CreateIndex( Parse *pParse, /* All information about this parse */ Token *pName1, /* First part of index name. May be NULL */ Token *pName2, /* Second part of index name. May be NULL */ SrcList *pTblName, /* Table to index. Use pParse->pNewTable if 0 */ ExprList *pList, /* A list of columns to be indexed */ @@ -2350,10 +2375,11 @@ Token *pStart, /* The CREATE token that begins this statement */ Token *pEnd, /* The ")" that closes the CREATE INDEX statement */ int sortOrder, /* Sort order of primary key when pList==NULL */ int ifNotExist /* Omit error if index already exists */ ){ + Index *pRet = 0; /* Pointer to return */ Table *pTab = 0; /* Table to be indexed */ Index *pIndex = 0; /* The index to be created */ char *zName = 0; /* Name of the index */ int nName; /* Number of characters in zName */ int i, j; @@ -2785,10 +2811,11 @@ pOther = pOther->pNext; } pIndex->pNext = pOther->pNext; pOther->pNext = pIndex; } + pRet = pIndex; pIndex = 0; } /* Clean up before exiting */ exit_create_index: @@ -2797,11 +2824,11 @@ sqlite3DbFree(db, pIndex); } sqlite3ExprListDelete(db, pList); sqlite3SrcListDelete(db, pTblName); sqlite3DbFree(db, zName); - return; + return pRet; } /* ** Fill the Index.aiRowEst[] array with default information - information ** to be used when we have not run the ANALYZE command. @@ -3206,16 +3233,19 @@ Expr *pOn, /* The ON clause of a join */ IdList *pUsing /* The USING clause of a join */ ){ struct SrcList_item *pItem; sqlite3 *db = pParse->db; + if( !p && (pOn || pUsing) ){ + sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", + (pOn ? "ON" : "USING") + ); + goto append_from_error; + } p = sqlite3SrcListAppend(db, p, pTable, pDatabase); if( p==0 || NEVER(p->nSrc==0) ){ - sqlite3ExprDelete(db, pOn); - sqlite3IdListDelete(db, pUsing); - sqlite3SelectDelete(db, pSubquery); - return p; + goto append_from_error; } pItem = &p->a[p->nSrc-1]; assert( pAlias!=0 ); if( pAlias->n ){ pItem->zAlias = sqlite3NameFromToken(db, pAlias); @@ -3222,10 +3252,17 @@ } pItem->pSelect = pSubquery; pItem->pOn = pOn; pItem->pUsing = pUsing; return p; + + append_from_error: + assert( p==0 ); + sqlite3ExprDelete(db, pOn); + sqlite3IdListDelete(db, pUsing); + sqlite3SelectDelete(db, pSubquery); + return 0; } /* ** Add an INDEXED BY or NOT INDEXED clause to the most recently added ** element of the source-list passed as the second argument. @@ -3408,30 +3445,30 @@ ** If iDb<0 then code the OP_Goto only - don't set flag to verify the ** schema on any databases. This can be used to position the OP_Goto ** early in the code, before we know if any database tables will be used. */ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ - sqlite3 *db; - Vdbe *v; - int mask; - - v = sqlite3GetVdbe(pParse); - if( v==0 ) return; /* This only happens if there was a prior error */ - db = pParse->db; - if( pParse->cookieGoto==0 ){ - pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; + Parse *pToplevel = sqlite3ParseToplevel(pParse); + + if( pToplevel->cookieGoto==0 ){ + Vdbe *v = sqlite3GetVdbe(pToplevel); + if( v==0 ) return; /* This only happens if there was a prior error */ + pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; } if( iDb>=0 ){ + sqlite3 *db = pToplevel->db; + int mask; + assert( iDbnDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDbcookieMask & mask)==0 ){ - pParse->cookieMask |= mask; - pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; + if( (pToplevel->cookieMask & mask)==0 ){ + pToplevel->cookieMask |= mask; + pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; if( !OMIT_TEMPDB && iDb==1 ){ - sqlite3OpenTempDatabase(pParse); + sqlite3OpenTempDatabase(pToplevel); } } } } @@ -3447,18 +3484,60 @@ ** rollback the whole transaction. For operations where all constraints ** can be checked before any changes are made to the database, it is never ** necessary to undo a write and the checkpoint should not be set. */ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); - pParse->writeMask |= 1<nested==0 ){ - /* Every place where this routine is called with setStatement!=0 has - ** already successfully created a VDBE. */ - assert( pParse->pVdbe ); - sqlite3VdbeAddOp1(pParse->pVdbe, OP_Statement, iDb); + pToplevel->writeMask |= 1<isMultiWrite |= setStatement; +} + +/* +** Indicate that the statement currently under construction might write +** more than one entry (example: deleting one row then inserting another, +** inserting multiple rows in a table, or inserting a row and index entries.) +** If an abort occurs after some of these writes have completed, then it will +** be necessary to undo the completed writes. +*/ +void sqlite3MultiWrite(Parse *pParse){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); + pToplevel->isMultiWrite = 1; +} + +/* +** The code generator calls this routine if is discovers that it is +** possible to abort a statement prior to completion. In order to +** perform this abort without corrupting the database, we need to make +** sure that the statement is protected by a statement transaction. +** +** Technically, we only need to set the mayAbort flag if the +** isMultiWrite flag was previously set. There is a time dependency +** such that the abort must occur after the multiwrite. This makes +** some statements involving the REPLACE conflict resolution algorithm +** go a little faster. But taking advantage of this time dependency +** makes it more difficult to prove that the code is correct (in +** particular, it prevents us from writing an effective +** implementation of sqlite3AssertMayAbort()) and so we have chosen +** to take the safe route and skip the optimization. +*/ +void sqlite3MayAbort(Parse *pParse){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); + pToplevel->mayAbort = 1; +} + +/* +** Code an OP_Halt that causes the vdbe to return an SQLITE_CONSTRAINT +** error. The onError parameter determines which (if any) of the statement +** and/or current transaction is rolled back. +*/ +void sqlite3HaltConstraint(Parse *pParse, int onError, char *p4, int p4type){ + Vdbe *v = sqlite3GetVdbe(pParse); + if( onError==OE_Abort ){ + sqlite3MayAbort(pParse); } + sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type); } /* ** Check to see if pIndex uses the collating sequence pColl. Return ** true if it does and false if it does not. Index: src/callback.c ================================================================== --- src/callback.c +++ src/callback.c @@ -10,27 +10,24 @@ ** ************************************************************************* ** ** This file contains functions used to access the internal hash tables ** of user defined functions and collation sequences. -** -** $Id: callback.c,v 1.42 2009/06/17 00:35:31 drh Exp $ */ #include "sqliteInt.h" /* ** Invoke the 'collation needed' callback to request a collation sequence -** in the database text encoding of name zName, length nName. -** If the collation sequence +** in the encoding enc of name zName, length nName. */ -static void callCollNeeded(sqlite3 *db, const char *zName){ +static void callCollNeeded(sqlite3 *db, int enc, const char *zName){ assert( !db->xCollNeeded || !db->xCollNeeded16 ); if( db->xCollNeeded ){ char *zExternal = sqlite3DbStrDup(db, zName); if( !zExternal ) return; - db->xCollNeeded(db->pCollNeededArg, db, (int)ENC(db), zExternal); + db->xCollNeeded(db->pCollNeededArg, db, enc, zExternal); sqlite3DbFree(db, zExternal); } #ifndef SQLITE_OMIT_UTF16 if( db->xCollNeeded16 ){ char const *zExternal; @@ -69,12 +66,11 @@ } /* ** This function is responsible for invoking the collation factory callback ** or substituting a collation sequence of a different encoding when the -** requested collation sequence is not available in the database native -** encoding. +** requested collation sequence is not available in the desired encoding. ** ** If it is not NULL, then pColl must point to the database native encoding ** collation sequence with name zName, length nName. ** ** The return value is either the collation sequence to be used in database @@ -83,25 +79,26 @@ ** ** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq() */ CollSeq *sqlite3GetCollSeq( sqlite3* db, /* The database connection */ + u8 enc, /* The desired encoding for the collating sequence */ CollSeq *pColl, /* Collating sequence with native encoding, or NULL */ const char *zName /* Collating sequence name */ ){ CollSeq *p; p = pColl; if( !p ){ - p = sqlite3FindCollSeq(db, ENC(db), zName, 0); + p = sqlite3FindCollSeq(db, enc, zName, 0); } if( !p || !p->xCmp ){ /* No collation sequence of this type for this encoding is registered. ** Call the collation factory to see if it can supply us with one. */ - callCollNeeded(db, zName); - p = sqlite3FindCollSeq(db, ENC(db), zName, 0); + callCollNeeded(db, enc, zName); + p = sqlite3FindCollSeq(db, enc, zName, 0); } if( p && !p->xCmp && synthCollSeq(db, p) ){ p = 0; } assert( !p || p->xCmp ); @@ -120,11 +117,12 @@ ** from the main database is substituted, if one is available. */ int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ if( pColl ){ const char *zName = pColl->zName; - CollSeq *p = sqlite3GetCollSeq(pParse->db, pColl, zName); + sqlite3 *db = pParse->db; + CollSeq *p = sqlite3GetCollSeq(db, ENC(db), pColl, zName); if( !p ){ sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); pParse->nErr++; return SQLITE_ERROR; } @@ -421,10 +419,11 @@ Table *pTab = sqliteHashData(pElem); assert( pTab->dbMem==0 ); sqlite3DeleteTable(pTab); } sqlite3HashClear(&temp1); + sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; pSchema->flags &= ~DB_SchemaLoaded; } /* @@ -442,9 +441,10 @@ db->mallocFailed = 1; }else if ( 0==p->file_format ){ sqlite3HashInit(&p->tblHash); sqlite3HashInit(&p->idxHash); sqlite3HashInit(&p->trigHash); + sqlite3HashInit(&p->fkeyHash); p->enc = SQLITE_UTF8; } return p; } Index: src/complete.c ================================================================== --- src/complete.c +++ src/complete.c @@ -13,12 +13,10 @@ ** ** This file contains C code that implements the sqlite3_complete() API. ** This code used to be part of the tokenizer.c source file. But by ** separating it out, the code will be automatically omitted from ** static links that do not use it. -** -** $Id: complete.c,v 1.8 2009/04/28 04:46:42 drh Exp $ */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_COMPLETE /* Index: src/date.c ================================================================== --- src/date.c +++ src/date.c @@ -14,12 +14,10 @@ ** ** There is only one exported symbol in this file - the function ** sqlite3RegisterDateTimeFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: date.c,v 1.107 2009/05/03 20:23:53 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 ** in Greenwich on November 24, 4714 B.C. according to the Gregorian ** calendar system. ** @@ -446,11 +444,11 @@ x.s = s; } x.tz = 0; x.validJD = 0; computeJD(&x); - t = x.iJD/1000 - 21086676*(i64)10000; + t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); #ifdef HAVE_LOCALTIME_R { struct tm sLocal; localtime_r(&t, &sLocal); y.Y = sLocal.tm_year + 1900; @@ -458,11 +456,11 @@ y.D = sLocal.tm_mday; y.h = sLocal.tm_hour; y.m = sLocal.tm_min; y.s = sLocal.tm_sec; } -#elif defined(HAVE_LOCALTIME_S) +#elif defined(HAVE_LOCALTIME_S) && HAVE_LOCALTIME_S { struct tm sLocal; localtime_s(&sLocal, &t); y.Y = sLocal.tm_year + 1900; y.M = sLocal.tm_mon + 1; Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** 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.204 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" /* ** Look up every table that is named in pSrc. If any table is not found, @@ -41,20 +39,30 @@ ** Check to make sure the given table is writable. If it is not ** writable, generate an error message and return 1. If it is ** writable return 0; */ int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ - if( ((pTab->tabFlags & TF_Readonly)!=0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 - && pParse->nested==0) -#ifndef SQLITE_OMIT_VIRTUALTABLE - || (pTab->pMod && pTab->pMod->pModule->xUpdate==0) -#endif + /* A table is not writable under the following circumstances: + ** + ** 1) It is a virtual table and no implementation of the xUpdate method + ** has been provided, or + ** 2) It is a system table (i.e. sqlite_master), this call is not + ** part of a nested parse and writable_schema pragma has not + ** been specified. + ** + ** In either case leave an error message in pParse and return non-zero. + */ + if( ( IsVirtual(pTab) + && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ) + || ( (pTab->tabFlags & TF_Readonly)!=0 + && (pParse->db->flags & SQLITE_WriteSchema)==0 + && pParse->nested==0 ) ){ sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); return 1; } + #ifndef SQLITE_OMIT_VIEW if( !viewOk && pTab->pSelect ){ sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); return 1; } @@ -216,27 +224,21 @@ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int iCur; /* VDBE Cursor number for pTab */ sqlite3 *db; /* Main database structure */ AuthContext sContext; /* Authorization context */ - int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ NameContext sNC; /* Name context to resolve expressions in */ int iDb; /* Database number */ int memCnt = -1; /* Memory cell used for change counting */ int rcauth; /* Value returned by authorization callback */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ 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 */ - int iBeginBeforeTrigger = 0; /* Address of before trigger program */ - int iEndBeforeTrigger = 0; /* Exit of before trigger program */ - u32 old_col_mask = 0; /* Mask of OLD.* columns in use */ - sContext.pParse = 0; + memset(&sContext, 0, sizeof(sContext)); db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto delete_from_cleanup; } assert( pTabList->nSrc==1 ); @@ -261,10 +263,16 @@ #endif #ifdef SQLITE_OMIT_VIEW # undef isView # define isView 0 #endif + + /* If pTab is really a view, make sure it has been initialized. + */ + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto delete_from_cleanup; + } if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto delete_from_cleanup; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -275,22 +283,10 @@ if( rcauth==SQLITE_DENY ){ goto delete_from_cleanup; } assert(!isView || pTrigger); - /* If pTab is really a view, make sure it has been initialized. - */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto delete_from_cleanup; - } - - /* Allocate a cursor used to store the old.* data for a trigger. - */ - if( pTrigger ){ - oldIdx = pParse->nTab++; - } - /* Assign cursor number to the table and all its indices. */ assert( pTabList->nSrc==1 ); iCur = pTabList->a[0].iCursor = pParse->nTab++; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ @@ -308,29 +304,11 @@ v = sqlite3GetVdbe(pParse); if( v==0 ){ goto delete_from_cleanup; } if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb); - - 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, 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, pTrigger, TK_DELETE, 0, - TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0); - iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto); - - sqlite3VdbeJumpHere(v, iGoto); - } + sqlite3BeginWriteOperation(pParse, 1, iDb); /* If we are trying to delete from a view, realize that view into ** a ephemeral table. */ #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) @@ -356,14 +334,16 @@ sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); } #ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. - ** 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 && !pTrigger && !IsVirtual(pTab) ){ + ** It is easier just to erase the whole table. Prior to version 3.6.5, + ** this optimization caused the row change count (the value returned by + ** API function sqlite3_count_changes) to be set incorrectly. */ + if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) + && 0==sqlite3FkRequired(pParse, pTab, 0, 0) + ){ assert( !isView ); sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, pTab->zName, P4_STATIC); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ assert( pIdx->pSchema==pTab->pSchema ); @@ -373,12 +353,12 @@ #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ /* The usual case: There is a WHERE clause so we have to scan through ** the table and pick which records to delete. */ { - int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ + int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ int regRowid; /* Actual register containing rowids */ /* Collect rowids of every row to be deleted. */ sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); @@ -389,87 +369,45 @@ if( db->flags & SQLITE_CountRows ){ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } sqlite3WhereEnd(pWInfo); - /* Open the pseudo-table used to store OLD if there are triggers. - */ - if( pTrigger ){ - sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol); - } - /* Delete every item whose key was written to the list during the ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. - */ + ** because deleting an item can change the scan order. */ end = sqlite3VdbeMakeLabel(v); + /* Unless this is a view, open cursors for the table we are + ** deleting from and all its indices. If this is a view, then the + ** only effect this statement has is to fire the INSTEAD OF + ** triggers. */ if( !isView ){ - /* Open cursors for the table we are deleting from and - ** all its indices. - */ sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); } - /* This is the beginning of the delete loop. If a trigger encounters - ** an IGNORE constraint, it jumps back to here. - */ - if( pTrigger ){ - sqlite3VdbeResolveLabel(v, addr); - } addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - 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 - ** next iteration of the loop through the contents of the fifo. - */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid); - - /* Populate the OLD.* pseudo-table */ - if( old_col_mask ){ - sqlite3VdbeAddOp2(v, OP_RowData, iCur, iData); - }else{ - sqlite3VdbeAddOp2(v, OP_Null, 0, iData); - } - sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, iData, iRowid); - - /* Jump back and run the BEFORE triggers */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger); - sqlite3VdbeJumpHere(v, iEndBeforeTrigger); - } - - if( !isView ){ - /* Delete the row */ + /* Delete the row */ #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - const char *pVtab = (const char *)pTab->pVtab; - sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVtab, P4_VTAB); - }else + if( IsVirtual(pTab) ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); + sqlite3VtabMakeWritable(pParse, pTab); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); + sqlite3MayAbort(pParse); + }else #endif - { - sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, pParse->nested==0); - } - } - - /* If there are row triggers, close all cursors then invoke - ** the AFTER triggers - */ - if( pTrigger ){ - /* Jump back and run the AFTER triggers */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); - sqlite3VdbeJumpHere(v, iEndAfterTrigger); + { + int count = (pParse->nested==0); /* True to count changes */ + sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default); } /* End of the delete loop */ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); sqlite3VdbeResolveLabel(v, end); - /* Close the cursors after the loop if there are no row triggers */ - if( !isView && !IsVirtual(pTab) ){ + /* Close the cursors open on the table and its indexes. */ + if( !isView && !IsVirtual(pTab) ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum); } sqlite3VdbeAddOp1(v, OP_Close, iCur); } @@ -477,20 +415,19 @@ /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ - if( pParse->nested==0 && pParse->trigStack==0 ){ + if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); } - /* - ** Return the number of rows that were deleted. If this routine is + /* Return the number of rows that were deleted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ - if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){ + if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); } @@ -498,49 +435,127 @@ sqlite3AuthContextPop(&sContext); sqlite3SrcListDelete(db, pTabList); sqlite3ExprDelete(db, pWhere); return; } +/* Make sure "isView" and other macros defined above are undefined. Otherwise +** thely may interfere with compilation of other functions in this file +** (or in another file, if this file becomes part of the amalgamation). */ +#ifdef isView + #undef isView +#endif +#ifdef pTrigger + #undef pTrigger +#endif /* ** This routine generates VDBE code that causes a single row of a ** single table to be deleted. ** ** The VDBE must be in a particular state when this routine is called. ** These are the requirements: ** ** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number "base". +** to be deleted, must be opened as cursor number $iCur. ** ** 2. Read/write cursors for all indices of pTab must be open as ** cursor number base+i for the i-th index. ** ** 3. The record number of the row to be deleted must be stored in ** memory cell iRowid. ** -** This routine pops the top of the stack to remove the record number -** and then generates code to remove both the table record and all index -** entries that point to that record. +** This routine generates code to remove both the table record and all +** index entries that point to that record. */ void sqlite3GenerateRowDelete( Parse *pParse, /* Parsing context */ Table *pTab, /* Table containing the row to be deleted */ int iCur, /* Cursor number for the table */ int iRowid, /* Memory cell that contains the rowid to delete */ - int count /* Increment the row change counter */ + int count, /* If non-zero, increment the row change counter */ + Trigger *pTrigger, /* List of triggers to (potentially) fire */ + int onconf /* Default ON CONFLICT policy for triggers */ ){ - int addr; - Vdbe *v; - - v = pParse->pVdbe; - addr = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowid); - sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); - sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); - if( count ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); - } - sqlite3VdbeJumpHere(v, addr); + Vdbe *v = pParse->pVdbe; /* Vdbe */ + int iOld = 0; /* First register in OLD.* array */ + int iLabel; /* Label resolved to end of generated code */ + + /* Vdbe is guaranteed to have been allocated by this stage. */ + assert( v ); + + /* Seek cursor iCur to the row to delete. If this row no longer exists + ** (this can happen if a trigger program has already deleted it), do + ** not attempt to delete it or fire any DELETE triggers. */ + iLabel = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); + + /* If there are any triggers to fire, allocate a range of registers to + ** use for the old.* references in the triggers. */ + if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){ + u32 mask; /* Mask of OLD.* columns in use */ + int iCol; /* Iterator used while populating OLD.* */ + + /* TODO: Could use temporary registers here. Also could attempt to + ** avoid copying the contents of the rowid register. */ + mask = sqlite3TriggerOldmask(pParse, pTrigger, 0, pTab, onconf); + mask |= sqlite3FkOldmask(pParse, pTab); + iOld = pParse->nMem+1; + pParse->nMem += (1 + pTab->nCol); + + /* Populate the OLD.* pseudo-table register array. These values will be + ** used by any BEFORE and AFTER triggers that exist. */ + sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld); + for(iCol=0; iColnCol; iCol++){ + if( mask==0xffffffff || mask&(1<pSelect==0 ){ + sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); + sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); + if( count ){ + sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); + } + } + + /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to + ** handle rows (possibly in other tables) that refer via a foreign key + ** to the row just deleted. */ + sqlite3FkActions(pParse, pTab, 0, iOld); + + /* Invoke AFTER DELETE trigger programs. */ + sqlite3CodeRowTrigger(pParse, pTrigger, + TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel + ); + + /* Jump here if the row had already been deleted before any BEFORE + ** trigger programs were invoked. Or if a trigger program throws a + ** RAISE(IGNORE) exception. */ + sqlite3VdbeResolveLabel(v, iLabel); } /* ** This routine generates VDBE code that causes the deletion of all ** index entries associated with a single row of a single table. @@ -605,21 +620,16 @@ int idx = pIdx->aiColumn[j]; if( idx==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j); }else{ sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j); - sqlite3ColumnDefault(v, pTab, idx); + sqlite3ColumnDefault(v, pTab, idx, -1); } } if( doMakeRec ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); - sqlite3IndexAffinityStr(v, pIdx); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regBase, nCol+1); } sqlite3ReleaseTempRange(pParse, regBase, nCol+1); return regBase; } - -/* Make sure "isView" gets undefined in case this file becomes part of -** the amalgamation - so that subsequent files do not see isView as a -** macro. */ -#undef isView Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. -** -** $Id: expr.c,v 1.446 2009/06/19 18:32:55 drh Exp $ */ #include "sqliteInt.h" /* ** Return the 'affinity' of the expression pExpr if any. @@ -90,11 +88,13 @@ while( ALWAYS(p) ){ int op; pColl = p->pColl; if( pColl ) break; op = p->op; - if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER) && p->pTab!=0 ){ + if( p->pTab!=0 && ( + op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER + )){ /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally ** a TK_COLUMN but was previously evaluated and cached in a register */ const char *zColl; int j = p->iColumn; if( j>=0 ){ @@ -150,11 +150,11 @@ */ static char comparisonAffinity(Expr *pExpr){ char aff; assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT || pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE || - pExpr->op==TK_NE ); + pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT ); assert( pExpr->pLeft ); aff = sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ aff = sqlite3CompareAffinity(pExpr->pRight, aff); }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){ @@ -569,16 +569,16 @@ assert( z!=0 ); assert( z[0]!=0 ); if( z[1]==0 ){ /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); - pExpr->iTable = ++pParse->nVar; + pExpr->iColumn = (ynVar)(++pParse->nVar); }else if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ - int i; - pExpr->iTable = i = atoi((char*)&z[1]); + int i = atoi((char*)&z[1]); + pExpr->iColumn = (ynVar)i; testcase( i==0 ); testcase( i==1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); if( i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ @@ -598,16 +598,16 @@ n = sqlite3Strlen30(z); for(i=0; inVarExpr; i++){ Expr *pE = pParse->apVarExpr[i]; assert( pE!=0 ); if( memcmp(pE->u.zToken, z, n)==0 && pE->u.zToken[n]==0 ){ - pExpr->iTable = pE->iTable; + pExpr->iColumn = pE->iColumn; break; } } if( i>=pParse->nVarExpr ){ - pExpr->iTable = ++pParse->nVar; + pExpr->iColumn = (ynVar)(++pParse->nVar); if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){ pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10; pParse->apVarExpr = sqlite3DbReallocOrFree( db, @@ -884,13 +884,12 @@ sqlite3DbFree(db, pNew); return 0; } pOldItem = p->a; for(i=0; inExpr; i++, pItem++, pOldItem++){ - Expr *pNewExpr; Expr *pOldExpr = pOldItem->pExpr; - pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr, flags); + pItem->pExpr = sqlite3ExprDup(db, pOldExpr, flags); pItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan); pItem->sortOrder = pOldItem->sortOrder; pItem->done = 0; pItem->iCol = pOldItem->iCol; @@ -1370,10 +1369,12 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ int iTab = pParse->nTab++; /* Cursor of the RHS table */ int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */ + + assert( pX->op==TK_IN ); /* Check to see if an existing table or index can be used to ** satisfy the query. This is preferable to generating a new ** ephemeral table. */ @@ -1397,11 +1398,10 @@ */ assert(v); if( iCol<0 ){ int iMem = ++pParse->nMem; int iAddr; - sqlite3VdbeUsesBtree(v, iDb); iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); @@ -1431,13 +1431,10 @@ int iMem = ++pParse->nMem; int iAddr; char *pKey; pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); - iDb = sqlite3SchemaToIndex(db, pIdx->pSchema); - sqlite3VdbeUsesBtree(v, iDb); - iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, pKey,P4_KEYINFO_HANDOFF); @@ -1452,11 +1449,11 @@ } } } if( eType==0 ){ - /* Could not found an existing able or index to use as the RHS b-tree. + /* Could not found an existing table or index to use as the RHS b-tree. ** We will have to generate an ephemeral table to do the job. */ int rMayHaveNull = 0; eType = IN_INDEX_EPH; if( prNotFound ){ @@ -1499,21 +1496,25 @@ ** care of changing this register value to non-NULL if the RHS is NULL-free. ** ** If rMayHaveNull is zero, that means that the subquery is being used ** for membership testing only. There is no need to initialize any ** registers to indicate the presense or absence of NULLs on the RHS. +** +** For a SELECT or EXISTS operator, return the register that holds the +** result. For IN operators or if an error occurs, the return value is 0. */ #ifndef SQLITE_OMIT_SUBQUERY -void sqlite3CodeSubselect( +int sqlite3CodeSubselect( Parse *pParse, /* Parsing context */ Expr *pExpr, /* The IN, SELECT, or EXISTS operator */ int rMayHaveNull, /* Register that records whether NULLs exist in RHS */ int isRowid /* If true, LHS of IN operator is a rowid */ ){ int testAddr = 0; /* One-time test address */ + int rReg = 0; /* Register storing resulting */ Vdbe *v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; + if( NEVER(v==0) ) return 0; sqlite3ExprCachePush(pParse); /* This code must be run in its entirety every time it is encountered ** if any of the following is true: ** @@ -1522,11 +1523,11 @@ ** * We are inside a trigger ** ** If all of the above are false, then we can run this code just once ** save the results, and reuse the same result on subsequent invocations. */ - if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->trigStack ){ + if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){ int mem = ++pParse->nMem; sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); assert( testAddr>0 || pParse->db->mallocFailed ); } @@ -1574,11 +1575,11 @@ assert( !isRowid ); sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); dest.affinity = (u8)affinity; assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ - return; + return 0; } pEList = pExpr->x.pSelect->pEList; if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){ keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pEList->a[0].pExpr); @@ -1605,10 +1606,11 @@ r1 = sqlite3GetTempReg(pParse); r2 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Null, 0, r2); for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ Expr *pE2 = pItem->pExpr; + int iValToIns; /* If the expression is not constant then we will need to ** disable the test that was generated above that makes sure ** this code only executes once. Because for a non-constant ** expression we need to rerun this code each time. @@ -1617,18 +1619,23 @@ sqlite3VdbeChangeToNoop(v, testAddr-1, 2); testAddr = 0; } /* Evaluate the expression and insert it into the temp table */ - r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); - if( isRowid ){ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2); - sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); + if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){ + sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns); }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); - sqlite3ExprCacheAffinityChange(pParse, r3, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2); + r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); + if( isRowid ){ + sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, + sqlite3VdbeCurrentAddr(v)+2); + sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); + }else{ + sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); + sqlite3ExprCacheAffinityChange(pParse, r3, 1); + sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2); + } } } sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r2); } @@ -1668,13 +1675,13 @@ VdbeComment((v, "Init EXISTS result")); } sqlite3ExprDelete(pParse->db, pSel->pLimit); pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &one); if( sqlite3Select(pParse, pSel, &dest) ){ - return; + return 0; } - pExpr->iColumn = dest.iParm; + rReg = dest.iParm; ExprSetIrreducible(pExpr); break; } } @@ -1681,11 +1688,11 @@ if( testAddr ){ sqlite3VdbeJumpHere(v, testAddr-1); } sqlite3ExprCachePop(pParse, 1); - return; + return rReg; } #endif /* SQLITE_OMIT_SUBQUERY */ /* ** Duplicate an 8-byte value @@ -1709,17 +1716,14 @@ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ double value; char *zV; sqlite3AtoF(z, &value); - if( sqlite3IsNaN(value) ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, iMem); - }else{ - if( negateFlag ) value = -value; - zV = dup8bytes(v, (char*)&value); - sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); - } + assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ + if( negateFlag ) value = -value; + zV = dup8bytes(v, (char*)&value); + sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); } } /* @@ -1923,16 +1927,11 @@ if( iColumn<0 ){ sqlite3VdbeAddOp2(v, OP_Rowid, iTable, iReg); }else if( ALWAYS(pTab!=0) ){ int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; sqlite3VdbeAddOp3(v, op, iTable, iColumn, iReg); - sqlite3ColumnDefault(v, pTab, iColumn); -#ifndef SQLITE_OMIT_FLOATING_POINT - if( pTab->aCol[iColumn].affinity==SQLITE_AFF_REAL ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); - } -#endif + sqlite3ColumnDefault(v, pTab, iColumn, iReg); } sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg); return iReg; } @@ -2175,11 +2174,11 @@ assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( pExpr->u.zToken!=0 ); assert( pExpr->u.zToken[0]!=0 ); if( pExpr->u.zToken[1]==0 && (pOp = sqlite3VdbeGetOp(v, -1))->opcode==OP_Variable - && pOp->p1+pOp->p3==pExpr->iTable + && pOp->p1+pOp->p3==pExpr->iColumn && 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 @@ -2186,11 +2185,11 @@ ** repeat count on the prior instruction rather than making a new ** instruction. */ pOp->p3++; }else{ - sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iTable, target, 1); + sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iColumn, target, 1); if( pExpr->u.zToken[1]!=0 ){ sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, 0); } } break; @@ -2254,10 +2253,23 @@ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, inReg, SQLITE_STOREP2); testcase( regFree1==0 ); testcase( regFree2==0 ); break; + } + case TK_IS: + case TK_ISNOT: { + testcase( op==TK_IS ); + testcase( op==TK_ISNOT ); + codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, + pExpr->pRight, &r2, ®Free2); + op = (op==TK_IS) ? TK_EQ : TK_NE; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; } case TK_AND: case TK_OR: case TK_PLUS: case TK_STAR: @@ -2376,14 +2388,40 @@ nFarg = pFarg ? pFarg->nExpr : 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); zId = pExpr->u.zToken; nId = sqlite3Strlen30(zId); pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0); - assert( pDef!=0 ); + if( pDef==0 ){ + sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId); + break; + } + + /* Attempt a direct implementation of the built-in COALESCE() and + ** IFNULL() functions. This avoids unnecessary evalation of + ** arguments past the first non-NULL argument. + */ + if( pDef->flags & SQLITE_FUNC_COALESCE ){ + int endCoalesce = sqlite3VdbeMakeLabel(v); + assert( nFarg>=2 ); + sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target); + for(i=1; ia[i].pExpr, target); + sqlite3ExprCachePop(pParse, 1); + } + sqlite3VdbeResolveLabel(v, endCoalesce); + break; + } + + if( pFarg ){ r1 = sqlite3GetTempRange(pParse, nFarg); + sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ sqlite3ExprCodeExprList(pParse, pFarg, r1, 1); + sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */ }else{ r1 = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* Possibly overload the function if the first argument is @@ -2428,12 +2466,11 @@ #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: case TK_SELECT: { testcase( op==TK_EXISTS ); testcase( op==TK_SELECT ); - sqlite3CodeSubselect(pParse, pExpr, 0, 0); - inReg = pExpr->iColumn; + inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); break; } case TK_IN: { int rNotFound = 0; int rMayHaveNull = 0; @@ -2561,10 +2598,62 @@ } case TK_UPLUS: { inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); break; } + + case TK_TRIGGER: { + /* If the opcode is TK_TRIGGER, then the expression is a reference + ** to a column in the new.* or old.* pseudo-tables available to + ** trigger programs. In this case Expr.iTable is set to 1 for the + ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn + ** is set to the column of the pseudo-table to read, or to -1 to + ** read the rowid field. + ** + ** The expression is implemented using an OP_Param opcode. The p1 + ** parameter is set to 0 for an old.rowid reference, or to (i+1) + ** to reference another column of the old.* pseudo-table, where + ** i is the index of the column. For a new.rowid reference, p1 is + ** set to (n+1), where n is the number of columns in each pseudo-table. + ** For a reference to any other column in the new.* pseudo-table, p1 + ** is set to (n+2+i), where n and i are as defined previously. For + ** example, if the table on which triggers are being fired is + ** declared as: + ** + ** CREATE TABLE t1(a, b); + ** + ** Then p1 is interpreted as follows: + ** + ** p1==0 -> old.rowid p1==3 -> new.rowid + ** p1==1 -> old.a p1==4 -> new.a + ** p1==2 -> old.b p1==5 -> new.b + */ + Table *pTab = pExpr->pTab; + int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; + + assert( pExpr->iTable==0 || pExpr->iTable==1 ); + assert( pExpr->iColumn>=-1 && pExpr->iColumnnCol ); + assert( pTab->iPKey<0 || pExpr->iColumn!=pTab->iPKey ); + assert( p1>=0 && p1<(pTab->nCol*2+2) ); + + sqlite3VdbeAddOp2(v, OP_Param, p1, target); + VdbeComment((v, "%s.%s -> $%d", + (pExpr->iTable ? "new" : "old"), + (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName), + target + )); + + /* If the column has REAL affinity, it may currently be stored as an + ** integer. Use OP_RealAffinity to make sure it is really real. */ + if( pExpr->iColumn>=0 + && pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL + ){ + sqlite3VdbeAddOp1(v, OP_RealAffinity, target); + } + break; + } + /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** @@ -2646,28 +2735,31 @@ sqlite3VdbeResolveLabel(v, endLabel); break; } #ifndef SQLITE_OMIT_TRIGGER case TK_RAISE: { - if( !pParse->trigStack ){ + assert( pExpr->affinity==OE_Rollback + || pExpr->affinity==OE_Abort + || pExpr->affinity==OE_Fail + || pExpr->affinity==OE_Ignore + ); + if( !pParse->pTriggerTab ){ sqlite3ErrorMsg(pParse, "RAISE() may only be used within a trigger-program"); return 0; } - if( pExpr->affinity!=OE_Ignore ){ - assert( pExpr->affinity==OE_Rollback || - pExpr->affinity == OE_Abort || - pExpr->affinity == OE_Fail ); - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0, - pExpr->u.zToken, 0); - } else { - assert( pExpr->affinity == OE_Ignore ); - sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); - sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump); - VdbeComment((v, "raise(IGNORE)")); - } + if( pExpr->affinity==OE_Abort ){ + sqlite3MayAbort(pParse); + } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + if( pExpr->affinity==OE_Ignore ){ + sqlite3VdbeAddOp4( + v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); + }else{ + sqlite3HaltConstraint(pParse, pExpr->affinity, pExpr->u.zToken, 0); + } + break; } #endif } sqlite3ReleaseTempReg(pParse, regFree1); @@ -2739,10 +2831,11 @@ if( ALWAYS(pExpr->op!=TK_REGISTER) ){ int iMem; iMem = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem); pExpr->iTable = iMem; + pExpr->op2 = pExpr->op; pExpr->op = TK_REGISTER; } return inReg; } @@ -2812,10 +2905,11 @@ ** expression. */ static int evalConstExpr(Walker *pWalker, Expr *pExpr){ Parse *pParse = pWalker->pParse; switch( pExpr->op ){ + case TK_IN: case TK_REGISTER: { return WRC_Prune; } case TK_FUNCTION: case TK_AGG_FUNCTION: @@ -2839,10 +2933,11 @@ if( isAppropriateForFactoring(pExpr) ){ int r1 = ++pParse->nMem; int r2; r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); if( NEVER(r1!=r2) ) sqlite3ReleaseTempReg(pParse, r1); + pExpr->op2 = pExpr->op; pExpr->op = TK_REGISTER; pExpr->iTable = r2; return WRC_Prune; } return WRC_Continue; @@ -2967,10 +3062,23 @@ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); testcase( regFree1==0 ); testcase( regFree2==0 ); break; + } + case TK_IS: + case TK_ISNOT: { + testcase( op==TK_IS ); + testcase( op==TK_ISNOT ); + codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, + pExpr->pRight, &r2, ®Free2); + op = (op==TK_IS) ? TK_EQ : TK_NE; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, dest, SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; } case TK_ISNULL: case TK_NOTNULL: { assert( TK_ISNULL==OP_IsNull ); assert( TK_NOTNULL==OP_NotNull ); @@ -3116,10 +3224,23 @@ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); testcase( regFree1==0 ); testcase( regFree2==0 ); break; + } + case TK_IS: + case TK_ISNOT: { + testcase( pExpr->op==TK_IS ); + testcase( pExpr->op==TK_ISNOT ); + codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, + pExpr->pRight, &r2, ®Free2); + op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, dest, SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; } case TK_ISNULL: case TK_NOTNULL: { testcase( op==TK_ISNULL ); testcase( op==TK_NOTNULL ); Index: src/fault.c ================================================================== --- src/fault.c +++ src/fault.c @@ -8,14 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** -** $Id: fault.c,v 1.11 2008/09/02 00:52:52 drh Exp $ -*/ - -/* ** This file contains code to support the concept of "benign" ** malloc failures (when the xMalloc() or xRealloc() method of the ** sqlite3_mem_methods structure fails to allocate a block of memory ** and returns 0). ** ADDED src/fkey.c Index: src/fkey.c ================================================================== --- /dev/null +++ src/fkey.c @@ -0,0 +1,1191 @@ +/* +** +** 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 code used by the compiler to add foreign key +** support to compiled SQL statements. +*/ +#include "sqliteInt.h" + +#ifndef SQLITE_OMIT_FOREIGN_KEY +#ifndef SQLITE_OMIT_TRIGGER + +/* +** Deferred and Immediate FKs +** -------------------------- +** +** Foreign keys in SQLite come in two flavours: deferred and immediate. +** If an immediate foreign key constraint is violated, SQLITE_CONSTRAINT +** is returned and the current statement transaction rolled back. If a +** deferred foreign key constraint is violated, no action is taken +** immediately. However if the application attempts to commit the +** transaction before fixing the constraint violation, the attempt fails. +** +** Deferred constraints are implemented using a simple counter associated +** with the database handle. The counter is set to zero each time a +** database transaction is opened. Each time a statement is executed +** that causes a foreign key violation, the counter is incremented. Each +** time a statement is executed that removes an existing violation from +** the database, the counter is decremented. When the transaction is +** committed, the commit fails if the current value of the counter is +** greater than zero. This scheme has two big drawbacks: +** +** * When a commit fails due to a deferred foreign key constraint, +** there is no way to tell which foreign constraint is not satisfied, +** or which row it is not satisfied for. +** +** * If the database contains foreign key violations when the +** transaction is opened, this may cause the mechanism to malfunction. +** +** Despite these problems, this approach is adopted as it seems simpler +** than the alternatives. +** +** INSERT operations: +** +** I.1) For each FK for which the table is the child table, search +** the parent table for a match. If none is found increment the +** constraint counter. +** +** I.2) For each FK for which the table is the parent table, +** search the child table for rows that correspond to the new +** row in the parent table. Decrement the counter for each row +** found (as the constraint is now satisfied). +** +** DELETE operations: +** +** D.1) For each FK for which the table is the child table, +** search the parent table for a row that corresponds to the +** deleted row in the child table. If such a row is not found, +** decrement the counter. +** +** D.2) For each FK for which the table is the parent table, search +** the child table for rows that correspond to the deleted row +** in the parent table. For each found increment the counter. +** +** UPDATE operations: +** +** An UPDATE command requires that all 4 steps above are taken, but only +** for FK constraints for which the affected columns are actually +** modified (values must be compared at runtime). +** +** Note that I.1 and D.1 are very similar operations, as are I.2 and D.2. +** This simplifies the implementation a bit. +** +** For the purposes of immediate FK constraints, the OR REPLACE conflict +** resolution is considered to delete rows before the new row is inserted. +** If a delete caused by OR REPLACE violates an FK constraint, an exception +** is thrown, even if the FK constraint would be satisfied after the new +** row is inserted. +** +** Immediate constraints are usually handled similarly. The only difference +** is that the counter used is stored as part of each individual statement +** object (struct Vdbe). If, after the statement has run, its immediate +** constraint counter is greater than zero, it returns SQLITE_CONSTRAINT +** and the statement transaction is rolled back. An exception is an INSERT +** statement that inserts a single row only (no triggers). In this case, +** instead of using a counter, an exception is thrown immediately if the +** INSERT violates a foreign key constraint. This is necessary as such +** an INSERT does not open a statement transaction. +** +** TODO: How should dropping a table be handled? How should renaming a +** table be handled? +** +** +** Query API Notes +** --------------- +** +** Before coding an UPDATE or DELETE row operation, the code-generator +** for those two operations needs to know whether or not the operation +** requires any FK processing and, if so, which columns of the original +** row are required by the FK processing VDBE code (i.e. if FKs were +** implemented using triggers, which of the old.* columns would be +** accessed). No information is required by the code-generator before +** coding an INSERT operation. The functions used by the UPDATE/DELETE +** generation code to query for this information are: +** +** sqlite3FkRequired() - Test to see if FK processing is required. +** sqlite3FkOldmask() - Query for the set of required old.* columns. +** +** +** Externally accessible module functions +** -------------------------------------- +** +** sqlite3FkCheck() - Check for foreign key violations. +** sqlite3FkActions() - Code triggers for ON UPDATE/ON DELETE actions. +** sqlite3FkDelete() - Delete an FKey structure. +*/ + +/* +** VDBE Calling Convention +** ----------------------- +** +** Example: +** +** For the following INSERT statement: +** +** CREATE TABLE t1(a, b INTEGER PRIMARY KEY, c); +** INSERT INTO t1 VALUES(1, 2, 3.1); +** +** Register (x): 2 (type integer) +** Register (x+1): 1 (type integer) +** Register (x+2): NULL (type NULL) +** Register (x+3): 3.1 (type real) +*/ + +/* +** A foreign key constraint requires that the key columns in the parent +** table are collectively subject to a UNIQUE or PRIMARY KEY constraint. +** Given that pParent is the parent table for foreign key constraint pFKey, +** search the schema a unique index on the parent key columns. +** +** If successful, zero is returned. If the parent key is an INTEGER PRIMARY +** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx +** is set to point to the unique index. +** +** If the parent key consists of a single column (the foreign key constraint +** is not a composite foreign key), output variable *paiCol is set to NULL. +** Otherwise, it is set to point to an allocated array of size N, where +** N is the number of columns in the parent key. The first element of the +** array is the index of the child table column that is mapped by the FK +** constraint to the parent table column stored in the left-most column +** of index *ppIdx. The second element of the array is the index of the +** child table column that corresponds to the second left-most column of +** *ppIdx, and so on. +** +** If the required index cannot be found, either because: +** +** 1) The named parent key columns do not exist, or +** +** 2) The named parent key columns do exist, but are not subject to a +** UNIQUE or PRIMARY KEY constraint, or +** +** 3) No parent key columns were provided explicitly as part of the +** foreign key definition, and the parent table does not have a +** PRIMARY KEY, or +** +** 4) No parent key columns were provided explicitly as part of the +** foreign key definition, and the PRIMARY KEY of the parent table +** consists of a a different number of columns to the child key in +** the child table. +** +** then non-zero is returned, and a "foreign key mismatch" error loaded +** into pParse. If an OOM error occurs, non-zero is returned and the +** pParse->db->mallocFailed flag is set. +*/ +static int locateFkeyIndex( + Parse *pParse, /* Parse context to store any error in */ + Table *pParent, /* Parent table of FK constraint pFKey */ + FKey *pFKey, /* Foreign key to find index for */ + Index **ppIdx, /* OUT: Unique index on parent table */ + int **paiCol /* OUT: Map of index columns in pFKey */ +){ + Index *pIdx = 0; /* Value to return via *ppIdx */ + int *aiCol = 0; /* Value to return via *paiCol */ + int nCol = pFKey->nCol; /* Number of columns in parent key */ + char *zKey = pFKey->aCol[0].zCol; /* Name of left-most parent key column */ + + /* The caller is responsible for zeroing output parameters. */ + assert( ppIdx && *ppIdx==0 ); + assert( !paiCol || *paiCol==0 ); + assert( pParse ); + + /* If this is a non-composite (single column) foreign key, check if it + ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx + ** and *paiCol set to zero and return early. + ** + ** Otherwise, for a composite foreign key (more than one column), allocate + ** space for the aiCol array (returned via output parameter *paiCol). + ** Non-composite foreign keys do not require the aiCol array. + */ + if( nCol==1 ){ + /* The FK maps to the IPK if any of the following are true: + ** + ** 1) There is an INTEGER PRIMARY KEY column and the FK is implicitly + ** mapped to the primary key of table pParent, or + ** 2) The FK is explicitly mapped to a column declared as INTEGER + ** PRIMARY KEY. + */ + if( pParent->iPKey>=0 ){ + if( !zKey ) return 0; + if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; + } + }else if( paiCol ){ + assert( nCol>1 ); + aiCol = (int *)sqlite3DbMallocRaw(pParse->db, nCol*sizeof(int)); + if( !aiCol ) return 1; + *paiCol = aiCol; + } + + for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ + /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number + ** of columns. If each indexed column corresponds to a foreign key + ** column of pFKey, then this index is a winner. */ + + if( zKey==0 ){ + /* If zKey is NULL, then this foreign key is implicitly mapped to + ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be + ** identified by the test (Index.autoIndex==2). */ + if( pIdx->autoIndex==2 ){ + if( aiCol ){ + int i; + for(i=0; iaCol[i].iFrom; + } + break; + } + }else{ + /* If zKey is non-NULL, then this foreign key was declared to + ** map to an explicit list of columns in table pParent. Check if this + ** index matches those columns. Also, check that the index uses + ** the default collation sequences for each column. */ + int i, j; + for(i=0; iaiColumn[i]; /* Index of column in parent tbl */ + char *zDfltColl; /* Def. collation for column */ + char *zIdxCol; /* Name of indexed column */ + + /* If the index uses a collation sequence that is different from + ** the default collation sequence for the column, this index is + ** unusable. Bail out early in this case. */ + zDfltColl = pParent->aCol[iCol].zColl; + if( !zDfltColl ){ + zDfltColl = "BINARY"; + } + if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; + + zIdxCol = pParent->aCol[iCol].zName; + for(j=0; jaCol[j].zCol, zIdxCol)==0 ){ + if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; + break; + } + } + if( j==nCol ) break; + } + if( i==nCol ) break; /* pIdx is usable */ + } + } + } + + if( !pIdx ){ + if( !pParse->disableTriggers ){ + sqlite3ErrorMsg(pParse, "foreign key mismatch"); + } + sqlite3DbFree(pParse->db, aiCol); + return 1; + } + + *ppIdx = pIdx; + return 0; +} + +/* +** This function is called when a row is inserted into or deleted from the +** child table of foreign key constraint pFKey. If an SQL UPDATE is executed +** on the child table of pFKey, this function is invoked twice for each row +** affected - once to "delete" the old row, and then again to "insert" the +** new row. +** +** Each time it is called, this function generates VDBE code to locate the +** row in the parent table that corresponds to the row being inserted into +** or deleted from the child table. If the parent row can be found, no +** special action is taken. Otherwise, if the parent row can *not* be +** found in the parent table: +** +** Operation | FK type | Action taken +** -------------------------------------------------------------------------- +** INSERT immediate Increment the "immediate constraint counter". +** +** DELETE immediate Decrement the "immediate constraint counter". +** +** INSERT deferred Increment the "deferred constraint counter". +** +** DELETE deferred Decrement the "deferred constraint counter". +** +** These operations are identified in the comment at the top of this file +** (fkey.c) as "I.1" and "D.1". +*/ +static void fkLookupParent( + Parse *pParse, /* Parse context */ + int iDb, /* Index of database housing pTab */ + Table *pTab, /* Parent table of FK pFKey */ + Index *pIdx, /* Unique index on parent key columns in pTab */ + FKey *pFKey, /* Foreign key constraint */ + int *aiCol, /* Map from parent key columns to child table columns */ + int regData, /* Address of array containing child table row */ + int nIncr, /* Increment constraint counter by this */ + int isIgnore /* If true, pretend pTab contains all NULL values */ +){ + int i; /* Iterator variable */ + Vdbe *v = sqlite3GetVdbe(pParse); /* Vdbe to add code to */ + int iCur = pParse->nTab - 1; /* Cursor number to use */ + int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */ + + /* If nIncr is less than zero, then check at runtime if there are any + ** outstanding constraints to resolve. If there are not, there is no need + ** to check if deleting this row resolves any outstanding violations. + ** + ** Check if any of the key columns in the child table row are NULL. If + ** any are, then the constraint is considered satisfied. No need to + ** search for a matching row in the parent table. */ + if( nIncr<0 ){ + sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk); + } + for(i=0; inCol; i++){ + int iReg = aiCol[i] + regData + 1; + sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); + } + + if( isIgnore==0 ){ + if( pIdx==0 ){ + /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY + ** column of the parent table (table pTab). */ + int iMustBeInt; /* Address of MustBeInt instruction */ + int regTemp = sqlite3GetTempReg(pParse); + + /* Invoke MustBeInt to coerce the child key value to an integer (i.e. + ** apply the affinity of the parent key). If this fails, then there + ** is no matching parent key. Before using MustBeInt, make a copy of + ** the value. Otherwise, the value inserted into the child key column + ** will have INTEGER affinity applied to it, which may not be correct. */ + sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp); + iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0); + + /* If the parent table is the same as the child table, and we are about + ** to increment the constraint-counter (i.e. this is an INSERT operation), + ** then check if the row being inserted matches itself. If so, do not + ** increment the constraint-counter. */ + if( pTab==pFKey->pFrom && nIncr==1 ){ + sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); + } + + sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); + sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); + sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); + sqlite3VdbeJumpHere(v, iMustBeInt); + sqlite3ReleaseTempReg(pParse, regTemp); + }else{ + int nCol = pFKey->nCol; + int regTemp = sqlite3GetTempRange(pParse, nCol); + int regRec = sqlite3GetTempReg(pParse); + KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + + sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); + sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + for(i=0; ipFrom && nIncr==1 ){ + int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; + for(i=0; iaiColumn[i]+1+regData; + sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); + } + sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); + } + + sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeAddOp3(v, OP_Found, iCur, iOk, regRec); + + sqlite3ReleaseTempReg(pParse, regRec); + sqlite3ReleaseTempRange(pParse, regTemp, nCol); + } + } + + if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ + /* Special case: If this is an INSERT statement that will insert exactly + ** one row into the table, raise a constraint immediately instead of + ** incrementing a counter. This is necessary as the VM code is being + ** generated for will not open a statement transaction. */ + assert( nIncr==1 ); + sqlite3HaltConstraint( + pParse, OE_Abort, "foreign key constraint failed", P4_STATIC + ); + }else{ + if( nIncr>0 && pFKey->isDeferred==0 ){ + sqlite3ParseToplevel(pParse)->mayAbort = 1; + } + sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); + } + + sqlite3VdbeResolveLabel(v, iOk); + sqlite3VdbeAddOp1(v, OP_Close, iCur); +} + +/* +** This function is called to generate code executed when a row is deleted +** from the parent table of foreign key constraint pFKey and, if pFKey is +** deferred, when a row is inserted into the same table. When generating +** code for an SQL UPDATE operation, this function may be called twice - +** once to "delete" the old row and once to "insert" the new row. +** +** The code generated by this function scans through the rows in the child +** table that correspond to the parent table row being deleted or inserted. +** For each child row found, one of the following actions is taken: +** +** Operation | FK type | Action taken +** -------------------------------------------------------------------------- +** DELETE immediate Increment the "immediate constraint counter". +** Or, if the ON (UPDATE|DELETE) action is RESTRICT, +** throw a "foreign key constraint failed" exception. +** +** INSERT immediate Decrement the "immediate constraint counter". +** +** DELETE deferred Increment the "deferred constraint counter". +** Or, if the ON (UPDATE|DELETE) action is RESTRICT, +** throw a "foreign key constraint failed" exception. +** +** INSERT deferred Decrement the "deferred constraint counter". +** +** These operations are identified in the comment at the top of this file +** (fkey.c) as "I.2" and "D.2". +*/ +static void fkScanChildren( + Parse *pParse, /* Parse context */ + SrcList *pSrc, /* SrcList containing the table to scan */ + Table *pTab, + Index *pIdx, /* Foreign key index */ + FKey *pFKey, /* Foreign key relationship */ + int *aiCol, /* Map from pIdx cols to child table cols */ + int regData, /* Referenced table data starts here */ + int nIncr /* Amount to increment deferred counter by */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + int i; /* Iterator variable */ + Expr *pWhere = 0; /* WHERE clause to scan with */ + NameContext sNameContext; /* Context used to resolve WHERE clause */ + WhereInfo *pWInfo; /* Context used by sqlite3WhereXXX() */ + int iFkIfZero = 0; /* Address of OP_FkIfZero */ + Vdbe *v = sqlite3GetVdbe(pParse); + + assert( !pIdx || pIdx->pTable==pTab ); + + if( nIncr<0 ){ + iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); + } + + /* Create an Expr object representing an SQL expression like: + ** + ** = AND = ... + ** + ** The collation sequence used for the comparison should be that of + ** the parent key columns. The affinity of the parent key column should + ** be applied to each child key value before the comparison takes place. + */ + for(i=0; inCol; i++){ + Expr *pLeft; /* Value from parent table row */ + Expr *pRight; /* Column ref to child table */ + Expr *pEq; /* Expression (pLeft = pRight) */ + int iCol; /* Index of column in child table */ + const char *zCol; /* Name of column in child table */ + + pLeft = sqlite3Expr(db, TK_REGISTER, 0); + if( pLeft ){ + /* Set the collation sequence and affinity of the LHS of each TK_EQ + ** expression to the parent key column defaults. */ + if( pIdx ){ + Column *pCol; + iCol = pIdx->aiColumn[i]; + pCol = &pIdx->pTable->aCol[iCol]; + pLeft->iTable = regData+iCol+1; + pLeft->affinity = pCol->affinity; + pLeft->pColl = sqlite3LocateCollSeq(pParse, pCol->zColl); + }else{ + pLeft->iTable = regData; + pLeft->affinity = SQLITE_AFF_INTEGER; + } + } + iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; + assert( iCol>=0 ); + zCol = pFKey->pFrom->aCol[iCol].zName; + pRight = sqlite3Expr(db, TK_ID, zCol); + pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0); + pWhere = sqlite3ExprAnd(db, pWhere, pEq); + } + + /* If the child table is the same as the parent table, and this scan + ** is taking place as part of a DELETE operation (operation D.2), omit the + ** row being deleted from the scan by adding ($rowid != rowid) to the WHERE + ** clause, where $rowid is the rowid of the row being deleted. */ + if( pTab==pFKey->pFrom && nIncr>0 ){ + Expr *pEq; /* Expression (pLeft = pRight) */ + Expr *pLeft; /* Value from parent table row */ + Expr *pRight; /* Column ref to child table */ + pLeft = sqlite3Expr(db, TK_REGISTER, 0); + pRight = sqlite3Expr(db, TK_COLUMN, 0); + if( pLeft && pRight ){ + pLeft->iTable = regData; + pLeft->affinity = SQLITE_AFF_INTEGER; + pRight->iTable = pSrc->a[0].iCursor; + pRight->iColumn = -1; + } + pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0); + pWhere = sqlite3ExprAnd(db, pWhere, pEq); + } + + /* Resolve the references in the WHERE clause. */ + memset(&sNameContext, 0, sizeof(NameContext)); + sNameContext.pSrcList = pSrc; + sNameContext.pParse = pParse; + sqlite3ResolveExprNames(&sNameContext, pWhere); + + /* Create VDBE to loop through the entries in pSrc that match the WHERE + ** clause. If the constraint is not deferred, throw an exception for + ** each row found. Otherwise, for deferred constraints, increment the + ** deferred constraint counter by nIncr for each row selected. */ + pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0); + if( nIncr>0 && pFKey->isDeferred==0 ){ + sqlite3ParseToplevel(pParse)->mayAbort = 1; + } + sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); + if( pWInfo ){ + sqlite3WhereEnd(pWInfo); + } + + /* Clean up the WHERE clause constructed above. */ + sqlite3ExprDelete(db, pWhere); + if( iFkIfZero ){ + sqlite3VdbeJumpHere(v, iFkIfZero); + } +} + +/* +** This function returns a pointer to the head of a linked list of FK +** constraints for which table pTab is the parent table. For example, +** given the following schema: +** +** CREATE TABLE t1(a PRIMARY KEY); +** CREATE TABLE t2(b REFERENCES t1(a); +** +** Calling this function with table "t1" as an argument returns a pointer +** to the FKey structure representing the foreign key constraint on table +** "t2". Calling this function with "t2" as the argument would return a +** NULL pointer (as there are no FK constraints for which t2 is the parent +** table). +*/ +FKey *sqlite3FkReferences(Table *pTab){ + int nName = sqlite3Strlen30(pTab->zName); + return (FKey *)sqlite3HashFind(&pTab->pSchema->fkeyHash, pTab->zName, nName); +} + +/* +** The second argument is a Trigger structure allocated by the +** fkActionTrigger() routine. This function deletes the Trigger structure +** and all of its sub-components. +** +** The Trigger structure or any of its sub-components may be allocated from +** the lookaside buffer belonging to database handle dbMem. +*/ +static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ + if( p ){ + TriggerStep *pStep = p->step_list; + sqlite3ExprDelete(dbMem, pStep->pWhere); + sqlite3ExprListDelete(dbMem, pStep->pExprList); + sqlite3SelectDelete(dbMem, pStep->pSelect); + sqlite3ExprDelete(dbMem, p->pWhen); + sqlite3DbFree(dbMem, p); + } +} + +/* +** This function is called to generate code that runs when table pTab is +** being dropped from the database. The SrcList passed as the second argument +** to this function contains a single entry guaranteed to resolve to +** table pTab. +** +** Normally, no code is required. However, if either +** +** (a) The table is the parent table of a FK constraint, or +** (b) The table is the child table of a deferred FK constraint and it is +** determined at runtime that there are outstanding deferred FK +** constraint violations in the database, +** +** then the equivalent of "DELETE FROM " is executed before dropping +** the table from the database. Triggers are disabled while running this +** DELETE, but foreign key actions are not. +*/ +void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ + sqlite3 *db = pParse->db; + if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) && !pTab->pSelect ){ + int iSkip = 0; + Vdbe *v = sqlite3GetVdbe(pParse); + + assert( v ); /* VDBE has already been allocated */ + if( sqlite3FkReferences(pTab)==0 ){ + /* Search for a deferred foreign key constraint for which this table + ** is the child table. If one cannot be found, return without + ** generating any VDBE code. If one can be found, then jump over + ** the entire DELETE if there are no outstanding deferred constraints + ** when this statement is run. */ + FKey *p; + for(p=pTab->pFKey; p; p=p->pNextFrom){ + if( p->isDeferred ) break; + } + if( !p ) return; + iSkip = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); + } + + pParse->disableTriggers = 1; + sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0); + pParse->disableTriggers = 0; + + /* If the DELETE has generated immediate foreign key constraint + ** violations, halt the VDBE and return an error at this point, before + ** any modifications to the schema are made. This is because statement + ** transactions are not able to rollback schema changes. */ + sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2); + sqlite3HaltConstraint( + pParse, OE_Abort, "foreign key constraint failed", P4_STATIC + ); + + if( iSkip ){ + sqlite3VdbeResolveLabel(v, iSkip); + } + } +} + +/* +** This function is called when inserting, deleting or updating a row of +** table pTab to generate VDBE code to perform foreign key constraint +** processing for the operation. +** +** For a DELETE operation, parameter regOld is passed the index of the +** first register in an array of (pTab->nCol+1) registers containing the +** rowid of the row being deleted, followed by each of the column values +** of the row being deleted, from left to right. Parameter regNew is passed +** zero in this case. +** +** For an INSERT operation, regOld is passed zero and regNew is passed the +** first register of an array of (pTab->nCol+1) registers containing the new +** row data. +** +** For an UPDATE operation, this function is called twice. Once before +** the original record is deleted from the table using the calling convention +** described for DELETE. Then again after the original record is deleted +** but before the new record is inserted using the INSERT convention. +*/ +void sqlite3FkCheck( + Parse *pParse, /* Parse context */ + Table *pTab, /* Row is being deleted from this table */ + int regOld, /* Previous row data is stored here */ + int regNew /* New row data is stored here */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + Vdbe *v; /* VM to write code to */ + FKey *pFKey; /* Used to iterate through FKs */ + int iDb; /* Index of database containing pTab */ + const char *zDb; /* Name of database containing pTab */ + int isIgnoreErrors = pParse->disableTriggers; + + /* Exactly one of regOld and regNew should be non-zero. */ + assert( (regOld==0)!=(regNew==0) ); + + /* If foreign-keys are disabled, this function is a no-op. */ + if( (db->flags&SQLITE_ForeignKeys)==0 ) return; + + v = sqlite3GetVdbe(pParse); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zDb = db->aDb[iDb].zName; + + /* Loop through all the foreign key constraints for which pTab is the + ** child table (the table that the foreign key definition is part of). */ + for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + Table *pTo; /* Parent table of foreign key pFKey */ + Index *pIdx = 0; /* Index on key columns in pTo */ + int *aiFree = 0; + int *aiCol; + int iCol; + int i; + int isIgnore = 0; + + /* Find the parent table of this foreign key. Also find a unique index + ** on the parent key columns in the parent table. If either of these + ** schema items cannot be located, set an error in pParse and return + ** early. */ + if( pParse->disableTriggers ){ + pTo = sqlite3FindTable(db, pFKey->zTo, zDb); + }else{ + pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb); + } + if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ + if( !isIgnoreErrors || db->mallocFailed ) return; + continue; + } + assert( pFKey->nCol==1 || (aiFree && pIdx) ); + + if( aiFree ){ + aiCol = aiFree; + }else{ + iCol = pFKey->aCol[0].iFrom; + aiCol = &iCol; + } + for(i=0; inCol; i++){ + if( aiCol[i]==pTab->iPKey ){ + aiCol[i] = -1; + } +#ifndef SQLITE_OMIT_AUTHORIZATION + /* Request permission to read the parent key columns. If the + ** authorization callback returns SQLITE_IGNORE, behave as if any + ** values read from the parent table are NULL. */ + if( db->xAuth ){ + int rcauth; + char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; + rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); + isIgnore = (rcauth==SQLITE_IGNORE); + } +#endif + } + + /* Take a shared-cache advisory read-lock on the parent table. Allocate + ** a cursor to use to search the unique index on the parent key columns + ** in the parent table. */ + sqlite3TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName); + pParse->nTab++; + + if( regOld!=0 ){ + /* A row is being removed from the child table. Search for the parent. + ** If the parent does not exist, removing the child row resolves an + ** outstanding foreign key constraint violation. */ + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore); + } + if( regNew!=0 ){ + /* A row is being added to the child table. If a parent row cannot + ** be found, adding the child row has violated the FK constraint. */ + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore); + } + + sqlite3DbFree(db, aiFree); + } + + /* Loop through all the foreign key constraints that refer to this table */ + for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ + Index *pIdx = 0; /* Foreign key index for pFKey */ + SrcList *pSrc; + int *aiCol = 0; + + if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ + assert( regOld==0 && regNew!=0 ); + /* Inserting a single row into a parent table cannot cause an immediate + ** foreign key violation. So do nothing in this case. */ + continue; + } + + if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ + if( !isIgnoreErrors || db->mallocFailed ) return; + continue; + } + assert( aiCol || pFKey->nCol==1 ); + + /* Create a SrcList structure containing a single table (the table + ** the foreign key that refers to this table is attached to). This + ** is required for the sqlite3WhereXXX() interface. */ + pSrc = sqlite3SrcListAppend(db, 0, 0, 0); + if( pSrc ){ + struct SrcList_item *pItem = pSrc->a; + pItem->pTab = pFKey->pFrom; + pItem->zName = pFKey->pFrom->zName; + pItem->pTab->nRef++; + pItem->iCursor = pParse->nTab++; + + if( regNew!=0 ){ + fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); + } + if( regOld!=0 ){ + /* If there is a RESTRICT action configured for the current operation + ** on the parent table of this FK, then throw an exception + ** immediately if the FK constraint is violated, even if this is a + ** deferred trigger. That's what RESTRICT means. To defer checking + ** the constraint, the FK should specify NO ACTION (represented + ** using OE_None). NO ACTION is the default. */ + fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); + } + pItem->zName = 0; + sqlite3SrcListDelete(db, pSrc); + } + sqlite3DbFree(db, aiCol); + } +} + +#define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) + +/* +** This function is called before generating code to update or delete a +** row contained in table pTab. +*/ +u32 sqlite3FkOldmask( + Parse *pParse, /* Parse context */ + Table *pTab /* Table being modified */ +){ + u32 mask = 0; + if( pParse->db->flags&SQLITE_ForeignKeys ){ + FKey *p; + int i; + for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(i=0; inCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); + } + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + Index *pIdx = 0; + locateFkeyIndex(pParse, pTab, p, &pIdx, 0); + if( pIdx ){ + for(i=0; inColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); + } + } + } + return mask; +} + +/* +** This function is called before generating code to update or delete a +** row contained in table pTab. If the operation is a DELETE, then +** parameter aChange is passed a NULL value. For an UPDATE, aChange points +** to an array of size N, where N is the number of columns in table pTab. +** If the i'th column is not modified by the UPDATE, then the corresponding +** entry in the aChange[] array is set to -1. If the column is modified, +** the value is 0 or greater. Parameter chngRowid is set to true if the +** UPDATE statement modifies the rowid fields of the table. +** +** If any foreign key processing will be required, this function returns +** true. If there is no foreign key related processing, this function +** returns false. +*/ +int sqlite3FkRequired( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being modified */ + int *aChange, /* Non-NULL for UPDATE operations */ + int chngRowid /* True for UPDATE that affects rowid */ +){ + if( pParse->db->flags&SQLITE_ForeignKeys ){ + if( !aChange ){ + /* A DELETE operation. Foreign key processing is required if the + ** table in question is either the child or parent table for any + ** foreign key constraint. */ + return (sqlite3FkReferences(pTab) || pTab->pFKey); + }else{ + /* This is an UPDATE. Foreign key processing is only required if the + ** operation modifies one or more child or parent key columns. */ + int i; + FKey *p; + + /* Check if any child key columns are being modified. */ + for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(i=0; inCol; i++){ + int iChildKey = p->aCol[i].iFrom; + if( aChange[iChildKey]>=0 ) return 1; + if( iChildKey==pTab->iPKey && chngRowid ) return 1; + } + } + + /* Check if any parent key columns are being modified. */ + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + for(i=0; inCol; i++){ + char *zKey = p->aCol[i].zCol; + int iKey; + for(iKey=0; iKeynCol; iKey++){ + Column *pCol = &pTab->aCol[iKey]; + if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey) : pCol->isPrimKey) ){ + if( aChange[iKey]>=0 ) return 1; + if( iKey==pTab->iPKey && chngRowid ) return 1; + } + } + } + } + } + } + return 0; +} + +/* +** This function is called when an UPDATE or DELETE operation is being +** compiled on table pTab, which is the parent table of foreign-key pFKey. +** If the current operation is an UPDATE, then the pChanges parameter is +** passed a pointer to the list of columns being modified. If it is a +** DELETE, pChanges is passed a NULL pointer. +** +** It returns a pointer to a Trigger structure containing a trigger +** equivalent to the ON UPDATE or ON DELETE action specified by pFKey. +** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is +** returned (these actions require no special handling by the triggers +** sub-system, code for them is created by fkScanChildren()). +** +** For example, if pFKey is the foreign key and pTab is table "p" in +** the following schema: +** +** CREATE TABLE p(pk PRIMARY KEY); +** CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE); +** +** then the returned trigger structure is equivalent to: +** +** CREATE TRIGGER ... DELETE ON p BEGIN +** DELETE FROM c WHERE ck = old.pk; +** END; +** +** The returned pointer is cached as part of the foreign key object. It +** is eventually freed along with the rest of the foreign key object by +** sqlite3FkDelete(). +*/ +static Trigger *fkActionTrigger( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being updated or deleted from */ + FKey *pFKey, /* Foreign key to get action for */ + ExprList *pChanges /* Change-list for UPDATE, NULL for DELETE */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + int action; /* One of OE_None, OE_Cascade etc. */ + Trigger *pTrigger; /* Trigger definition to return */ + int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */ + + action = pFKey->aAction[iAction]; + pTrigger = pFKey->apTrigger[iAction]; + + if( action!=OE_None && !pTrigger ){ + u8 enableLookaside; /* Copy of db->lookaside.bEnabled */ + char const *zFrom; /* Name of child table */ + int nFrom; /* Length in bytes of zFrom */ + Index *pIdx = 0; /* Parent key index for this FK */ + int *aiCol = 0; /* child table cols -> parent key cols */ + TriggerStep *pStep = 0; /* First (only) step of trigger program */ + Expr *pWhere = 0; /* WHERE clause of trigger step */ + ExprList *pList = 0; /* Changes list if ON UPDATE CASCADE */ + Select *pSelect = 0; /* If RESTRICT, "SELECT RAISE(...)" */ + int i; /* Iterator variable */ + Expr *pWhen = 0; /* WHEN clause for the trigger */ + + if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; + assert( aiCol || pFKey->nCol==1 ); + + for(i=0; inCol; i++){ + Token tOld = { "old", 3 }; /* Literal "old" token */ + Token tNew = { "new", 3 }; /* Literal "new" token */ + Token tFromCol; /* Name of column in child table */ + Token tToCol; /* Name of column in parent table */ + int iFromCol; /* Idx of column in child table */ + Expr *pEq; /* tFromCol = OLD.tToCol */ + + iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; + assert( iFromCol>=0 ); + tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid"; + tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; + + tToCol.n = sqlite3Strlen30(tToCol.z); + tFromCol.n = sqlite3Strlen30(tFromCol.z); + + /* Create the expression "OLD.zToCol = zFromCol". It is important + ** that the "OLD.zToCol" term is on the LHS of the = operator, so + ** that the affinity and collation sequence associated with the + ** parent table are used for the comparison. */ + pEq = sqlite3PExpr(pParse, TK_EQ, + sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + , 0), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol) + , 0); + pWhere = sqlite3ExprAnd(db, pWhere, pEq); + + /* For ON UPDATE, construct the next term of the WHEN clause. + ** The final WHEN clause will be like this: + ** + ** WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN) + */ + if( pChanges ){ + pEq = sqlite3PExpr(pParse, TK_IS, + sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + 0), + sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + 0), + 0); + pWhen = sqlite3ExprAnd(db, pWhen, pEq); + } + + if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){ + Expr *pNew; + if( action==OE_Cascade ){ + pNew = sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + , 0); + }else if( action==OE_SetDflt ){ + Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; + if( pDflt ){ + pNew = sqlite3ExprDup(db, pDflt, 0); + }else{ + pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); + } + }else{ + pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); + } + pList = sqlite3ExprListAppend(pParse, pList, pNew); + sqlite3ExprListSetName(pParse, pList, &tFromCol, 0); + } + } + sqlite3DbFree(db, aiCol); + + zFrom = pFKey->pFrom->zName; + nFrom = sqlite3Strlen30(zFrom); + + if( action==OE_Restrict ){ + Token tFrom; + Expr *pRaise; + + tFrom.z = zFrom; + tFrom.n = nFrom; + pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed"); + if( pRaise ){ + pRaise->affinity = OE_Abort; + } + pSelect = sqlite3SelectNew(pParse, + sqlite3ExprListAppend(pParse, 0, pRaise), + sqlite3SrcListAppend(db, 0, &tFrom, 0), + pWhere, + 0, 0, 0, 0, 0, 0 + ); + pWhere = 0; + } + + /* In the current implementation, pTab->dbMem==0 for all tables except + ** for temporary tables used to describe subqueries. And temporary + ** tables do not have foreign key constraints. Hence, pTab->dbMem + ** should always be 0 there. + */ + enableLookaside = db->lookaside.bEnabled; + db->lookaside.bEnabled = 0; + + pTrigger = (Trigger *)sqlite3DbMallocZero(db, + sizeof(Trigger) + /* struct Trigger */ + sizeof(TriggerStep) + /* Single step in trigger program */ + nFrom + 1 /* Space for pStep->target.z */ + ); + if( pTrigger ){ + pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; + pStep->target.z = (char *)&pStep[1]; + pStep->target.n = nFrom; + memcpy((char *)pStep->target.z, zFrom, nFrom); + + pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); + pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + if( pWhen ){ + pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0, 0); + pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); + } + } + + /* Re-enable the lookaside buffer, if it was disabled earlier. */ + db->lookaside.bEnabled = enableLookaside; + + sqlite3ExprDelete(db, pWhere); + sqlite3ExprDelete(db, pWhen); + sqlite3ExprListDelete(db, pList); + sqlite3SelectDelete(db, pSelect); + if( db->mallocFailed==1 ){ + fkTriggerDelete(db, pTrigger); + return 0; + } + + switch( action ){ + case OE_Restrict: + pStep->op = TK_SELECT; + break; + case OE_Cascade: + if( !pChanges ){ + pStep->op = TK_DELETE; + break; + } + default: + pStep->op = TK_UPDATE; + } + pStep->pTrig = pTrigger; + pTrigger->pSchema = pTab->pSchema; + pTrigger->pTabSchema = pTab->pSchema; + pFKey->apTrigger[iAction] = pTrigger; + pTrigger->op = (pChanges ? TK_UPDATE : TK_DELETE); + } + + return pTrigger; +} + +/* +** This function is called when deleting or updating a row to implement +** any required CASCADE, SET NULL or SET DEFAULT actions. +*/ +void sqlite3FkActions( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being updated or deleted from */ + ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */ + int regOld /* Address of array containing old row */ +){ + /* If foreign-key support is enabled, iterate through all FKs that + ** refer to table pTab. If there is an action associated with the FK + ** for this operation (either update or delete), invoke the associated + ** trigger sub-program. */ + if( pParse->db->flags&SQLITE_ForeignKeys ){ + FKey *pFKey; /* Iterator variable */ + for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ + Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges); + if( pAction ){ + sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0); + } + } + } +} + +#endif /* ifndef SQLITE_OMIT_TRIGGER */ + +/* +** Free all memory associated with foreign key definitions attached to +** table pTab. Remove the deleted foreign keys from the Schema.fkeyHash +** hash table. +*/ +void sqlite3FkDelete(Table *pTab){ + FKey *pFKey; /* Iterator variable */ + FKey *pNext; /* Copy of pFKey->pNextFrom */ + + for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ + + /* Remove the FK from the fkeyHash hash table. */ + if( pFKey->pPrevTo ){ + pFKey->pPrevTo->pNextTo = pFKey->pNextTo; + }else{ + void *data = (void *)pFKey->pNextTo; + const char *z = (data ? pFKey->pNextTo->zTo : pFKey->zTo); + sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, sqlite3Strlen30(z), data); + } + if( pFKey->pNextTo ){ + pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; + } + + /* Delete any triggers created to implement actions for this FK. */ +#ifndef SQLITE_OMIT_TRIGGER + fkTriggerDelete(pTab->dbMem, pFKey->apTrigger[0]); + fkTriggerDelete(pTab->dbMem, pFKey->apTrigger[1]); +#endif + + /* EV: R-30323-21917 Each foreign key constraint in SQLite is + ** classified as either immediate or deferred. + */ + assert( pFKey->isDeferred==0 || pFKey->isDeferred==1 ); + + pNext = pFKey->pNextFrom; + sqlite3DbFree(pTab->dbMem, pFKey); + } +} +#endif /* ifndef SQLITE_OMIT_FOREIGN_KEY */ Index: src/func.c ================================================================== --- src/func.c +++ src/func.c @@ -13,12 +13,10 @@ ** functions of SQLite. ** ** There is only one exported symbol in this file - the function ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. -** -** $Id: func.c,v 1.239 2009/06/19 16:44:41 drh Exp $ */ #include "sqliteInt.h" #include #include #include "vdbeInt.h" @@ -335,10 +333,18 @@ sqlite3_result_text(context, (char *)z1, -1, sqlite3_free); } } } + +#if 0 /* This function is never used. */ +/* +** The COALESCE() and IFNULL() functions used to be implemented as shown +** here. But now they are implemented as VDBE code so that unused arguments +** do not have to be computed. This legacy implementation is retained as +** comment. +*/ /* ** Implementation of the IFNULL(), NVL(), and COALESCE() functions. ** All three do the same thing. They return the first non-NULL ** argument. */ @@ -353,10 +359,12 @@ sqlite3_result_value(context, argv[i]); break; } } } +#endif /* NOT USED */ +#define ifnullFunc versionFunc /* Substitute function - never called */ /* ** Implementation of random(). Return a random integer. */ static void randomFunc( @@ -700,11 +708,11 @@ sqlite3_result_value(context, argv[0]); } } /* -** Implementation of the VERSION(*) function. The result is the version +** Implementation of the sqlite_version() function. The result is the version ** of the SQLite library that is running. */ static void versionFunc( sqlite3_context *context, int NotUsed, @@ -711,10 +719,24 @@ sqlite3_value **NotUsed2 ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC); } + +/* +** Implementation of the sqlite_source_id() function. The result is a string +** that identifies the particular version of the source code used to build +** SQLite. +*/ +static void sourceidFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **NotUsed2 +){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + sqlite3_result_text(context, SQLITE_SOURCE_ID, -1, SQLITE_STATIC); +} /* Array for converting from half-bytes (nybbles) into ASCII hex ** digits. */ static const char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -1423,18 +1445,21 @@ FUNCTION(round, 2, 0, 0, roundFunc ), #endif FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(coalesce, 1, 0, 0, 0 ), - FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), FUNCTION(coalesce, 0, 0, 0, 0 ), +/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */ + {-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0}, FUNCTION(hex, 1, 0, 0, hexFunc ), - FUNCTION(ifnull, 2, 0, 1, ifnullFunc ), +/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */ + {2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0}, FUNCTION(random, 0, 0, 0, randomFunc ), FUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), + FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), FUNCTION(quote, 1, 0, 0, quoteFunc ), FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), FUNCTION(changes, 0, 0, 0, changes ), FUNCTION(total_changes, 0, 0, 0, total_changes ), FUNCTION(replace, 3, 0, 0, replaceFunc ), Index: src/global.c ================================================================== --- src/global.c +++ src/global.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains definitions of global variables and contants. -** -** $Id: global.c,v 1.12 2009/02/05 16:31:46 drh Exp $ */ #include "sqliteInt.h" /* An array to map all upper-case characters into their corresponding @@ -151,14 +149,16 @@ (void*)0, /* pPage */ 0, /* szPage */ 0, /* nPage */ 0, /* mxParserStack */ 0, /* sharedCacheEnabled */ - /* All the rest need to always be zero */ + /* All the rest should always be initialized to zero */ 0, /* isInit */ 0, /* inProgress */ + 0, /* isMutexInit */ 0, /* isMallocInit */ + 0, /* isPCacheInit */ 0, /* pInitMutex */ 0, /* nRefInitMutex */ }; Index: src/hash.c ================================================================== --- src/hash.c +++ src/hash.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This is the implementation of generic hash-tables ** used in SQLite. -** -** $Id: hash.c,v 1.38 2009/05/09 23:29:12 drh Exp $ */ #include "sqliteInt.h" #include /* Turn bulk memory into a hash table object by initializing the Index: src/hash.h ================================================================== --- src/hash.h +++ src/hash.h @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This is the header file for the generic hash-table implemenation ** used in SQLite. -** -** $Id: hash.h,v 1.15 2009/05/02 13:29:38 drh Exp $ */ #ifndef _SQLITE_HASH_H_ #define _SQLITE_HASH_H_ /* Forward declarations of structures. */ Index: src/hwtime.h ================================================================== --- src/hwtime.h +++ src/hwtime.h @@ -10,12 +10,10 @@ ** ****************************************************************************** ** ** This file contains inline asm code for retrieving "high-performance" ** counters for x86 class CPUs. -** -** $Id: hwtime.h,v 1.3 2008/08/01 14:33:15 shane Exp $ */ #ifndef _HWTIME_H_ #define _HWTIME_H_ /* Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. -** -** $Id: insert.c,v 1.269 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" /* ** Generate code that will open a table for reading. @@ -35,13 +33,13 @@ sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32); VdbeComment((v, "%s", pTab->zName)); } /* -** Set P4 of the most recently inserted opcode to a column affinity -** string for index pIdx. A column affinity string has one character -** for each column in the table, according to the affinity of the column: +** Return a pointer to the column affinity string associated with index +** pIdx. A column affinity string has one character for each column in +** the table, according to the affinity of the column: ** ** Character Column affinity ** ------------------------------ ** 'a' TEXT ** 'b' NONE @@ -49,12 +47,16 @@ ** 'd' INTEGER ** 'e' REAL ** ** An extra 'b' is appended to the end of the string to cover the ** rowid that appears as the last column in every index. +** +** Memory for the buffer containing the column index affinity string +** is managed along with the rest of the Index structure. It will be +** released when sqlite3DeleteIndex() is called. */ -void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ +const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ if( !pIdx->zColAff ){ /* The first time a column affinity string for a particular index is ** required, it is allocated and populated here. It is then stored as ** a member of the Index structure for subsequent use. ** @@ -66,20 +68,20 @@ Table *pTab = pIdx->pTable; sqlite3 *db = sqlite3VdbeDb(v); pIdx->zColAff = (char *)sqlite3Malloc(pIdx->nColumn+2); if( !pIdx->zColAff ){ db->mallocFailed = 1; - return; + return 0; } for(n=0; nnColumn; n++){ pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; } pIdx->zColAff[n++] = SQLITE_AFF_NONE; pIdx->zColAff[n] = 0; } - sqlite3VdbeChangeP4(v, -1, pIdx->zColAff, 0); + return pIdx->zColAff; } /* ** Set P4 of the most recently inserted opcode to a column affinity ** string for table pTab. A column affinity string has one character @@ -129,13 +131,18 @@ ** have been opened at any point in the VDBE program beginning at location ** iStartAddr throught the end of the program. This is used to see if ** a statement of the form "INSERT INTO SELECT ..." can ** run without using temporary table for the results of the SELECT. */ -static int readsTable(Vdbe *v, int iStartAddr, int iDb, Table *pTab){ +static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){ + Vdbe *v = sqlite3GetVdbe(p); int i; int iEnd = sqlite3VdbeCurrentAddr(v); +#ifndef SQLITE_OMIT_VIRTUALTABLE + VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0; +#endif + for(i=iStartAddr; iopcode==OP_OpenRead && pOp->p3==iDb ){ Index *pIndex; @@ -148,11 +155,11 @@ return 1; } } } #ifndef SQLITE_OMIT_VIRTUALTABLE - if( pOp->opcode==OP_VOpen && pOp->p4.pVtab==pTab->pVtab ){ + if( pOp->opcode==OP_VOpen && pOp->p4.pVtab==pVTab ){ assert( pOp->p4.pVtab!=0 ); assert( pOp->p4type==P4_VTAB ); return 1; } #endif @@ -186,24 +193,25 @@ int iDb, /* Index of the database holding pTab */ Table *pTab /* The table we are writing to */ ){ int memId = 0; /* Register holding maximum rowid */ if( pTab->tabFlags & TF_Autoincrement ){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); AutoincInfo *pInfo; - pInfo = pParse->pAinc; + pInfo = pToplevel->pAinc; while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } if( pInfo==0 ){ pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo)); if( pInfo==0 ) return 0; - pInfo->pNext = pParse->pAinc; - pParse->pAinc = pInfo; + pInfo->pNext = pToplevel->pAinc; + pToplevel->pAinc = pInfo; pInfo->pTab = pTab; pInfo->iDb = iDb; - pParse->nMem++; /* Register to hold name of table */ - pInfo->regCtr = ++pParse->nMem; /* Max rowid register */ - pParse->nMem++; /* Rowid in sqlite_sequence */ + pToplevel->nMem++; /* Register to hold name of table */ + pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ + pToplevel->nMem++; /* Rowid in sqlite_sequence */ } memId = pInfo->regCtr; } return memId; } @@ -217,10 +225,15 @@ sqlite3 *db = pParse->db; /* The database connection */ Db *pDb; /* Database only autoinc table */ int memId; /* Register holding max rowid */ int addr; /* A VDBE address */ Vdbe *v = pParse->pVdbe; /* VDBE under construction */ + + /* This routine is never called during trigger-generation. It is + ** only called from the top-level */ + assert( pParse->pTriggerTab==0 ); + assert( pParse==sqlite3ParseToplevel(pParse) ); assert( v ); /* We failed long ago if this is not so */ for(p = pParse->pAinc; p; p = p->pNext){ pDb = &db->aDb[p->iDb]; memId = p->regCtr; @@ -439,11 +452,10 @@ int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ int addrInsTop = 0; /* Jump to label "D" */ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ int addrSelect = 0; /* Address of coroutine that implements the SELECT */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */ - int newIdx = -1; /* Cursor for the NEW pseudo-table */ int iDb; /* Index of database holding TABLE */ Db *pDb; /* The database containing table being inserted into */ int appendFlag = 0; /* True if the insert is likely to be an append */ /* Register allocations */ @@ -454,11 +466,10 @@ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ int regRecord; /* Holds the assemblied row record */ int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ - #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ int tmask; /* Mask of trigger times */ @@ -501,40 +512,34 @@ #ifdef SQLITE_OMIT_VIEW # undef isView # define isView 0 #endif assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); + + /* If pTab is really a view, make sure it has been initialized. + ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual + ** module table). + */ + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto insert_cleanup; + } /* 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, tmask) ){ goto insert_cleanup; } - assert( pTab!=0 ); - - /* If pTab is really a view, make sure it has been initialized. - ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual - ** module table). - */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto insert_cleanup; - } /* Allocate a VDBE */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto insert_cleanup; if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb); - /* if there are row triggers, allocate a temp table for new.* references. */ - if( pTrigger ){ - newIdx = pParse->nTab++; - } - #ifndef SQLITE_OMIT_XFER_OPT /* If the statement is of the form ** ** INSERT INTO SELECT * FROM ; ** @@ -618,11 +623,11 @@ ** ** A temp table must be used if the table being updated is also one ** of the tables being read by the SELECT statement. Also use a ** temp table in the case of row triggers. */ - if( pTrigger || readsTable(v, addrSelect, iDb, pTab) ){ + if( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){ useTempTable = 1; } if( useTempTable ){ /* Invoke the coroutine to extract information from the SELECT @@ -734,16 +739,10 @@ ** in the original table definition. */ if( pColumn==0 && nColumn>0 ){ keyColumn = pTab->iPKey; } - - /* Open the temp table for FOR EACH ROW triggers - */ - if( pTrigger ){ - sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol); - } /* Initialize the count of rows to be inserted */ if( db->flags & SQLITE_CountRows ){ regRowCount = ++pParse->nMem; @@ -806,83 +805,74 @@ /* Run the BEFORE and INSTEAD OF triggers, if there are any */ endOfLoop = sqlite3VdbeMakeLabel(v); if( tmask & TRIGGER_BEFORE ){ - int regTrigRowid; - int regCols; - int regRec; + int regCols = sqlite3GetTempRange(pParse, pTab->nCol+1); /* build the NEW.* reference row. Note that if there is an INTEGER ** PRIMARY KEY into which a NULL is being inserted, that NULL will be ** translated into a unique ID for the row. But on a BEFORE trigger, ** we do not know what the unique ID will be (because the insert has ** not happened yet) so we substitute a rowid of -1 */ - regTrigRowid = sqlite3GetTempReg(pParse); if( keyColumn<0 ){ - sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid); + sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); }else{ int j1; if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regTrigRowid); + sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols); }else{ assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regTrigRowid); + sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regCols); } - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regTrigRowid); - sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid); + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); + sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regTrigRowid); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); } /* Cannot have triggers on a virtual table. If it were possible, ** this block would have to account for hidden column. */ - assert(!IsVirtual(pTab)); + assert( !IsVirtual(pTab) ); /* Create the new column data */ - regCols = sqlite3GetTempRange(pParse, pTab->nCol); for(i=0; inCol; i++){ if( pColumn==0 ){ j = i; }else{ for(j=0; jnId; j++){ if( pColumn->a[j].idx==i ) break; } } if( pColumn && j>=pColumn->nId ){ - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i); + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1); }else if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i); + sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1); }else{ assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i); + sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1); } } - regRec = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRec); /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger, ** do not attempt any conversions before assembling the record. ** If this is a real table, attempt conversions as required by the ** table column affinities. */ if( !isView ){ + sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol); sqlite3TableAffinityStr(v, pTab); } - sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regTrigRowid); - sqlite3ReleaseTempReg(pParse, regRec); - sqlite3ReleaseTempReg(pParse, regTrigRowid); - sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol); /* Fire BEFORE or INSTEAD OF triggers */ - if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, - pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ - goto insert_cleanup; - } + sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, + pTab, regCols-pTab->nCol-1, onError, endOfLoop); + + sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1); } /* Push the record number for the new entry onto the stack. The ** record number is a randomly generate integer created by NewRowid ** except when the table has an INTEGER PRIMARY KEY column, in which @@ -974,23 +964,24 @@ /* Generate code to check constraints and generate index keys and ** do the insertion. */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, - (const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); + sqlite3MayAbort(pParse); }else #endif { int isReplace; /* Set to true if constraints may cause a replace */ sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx, keyColumn>=0, 0, onError, endOfLoop, &isReplace ); + sqlite3FkCheck(pParse, pTab, 0, regIns); sqlite3CompleteInsertion( - pParse, pTab, baseCur, regIns, aRegIdx, 0, - (tmask&TRIGGER_AFTER) ? newIdx : -1, appendFlag, isReplace==0 + pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0 ); } } /* Update the count of rows that are inserted @@ -999,14 +990,12 @@ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } if( pTrigger ){ /* Code AFTER triggers */ - if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, - pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ - goto insert_cleanup; - } + sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, + pTab, regData-2-pTab->nCol, onError, endOfLoop); } /* The bottom of the main insertion loop, if the data source ** is a SELECT statement. */ @@ -1031,20 +1020,20 @@ insert_end: /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ - if( pParse->nested==0 && pParse->trigStack==0 ){ + if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); } /* ** Return the number of rows inserted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ - if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){ + if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); } @@ -1053,36 +1042,48 @@ sqlite3ExprListDelete(db, pList); sqlite3SelectDelete(db, pSelect); sqlite3IdListDelete(db, pColumn); sqlite3DbFree(db, aRegIdx); } + +/* Make sure "isView" and other macros defined above are undefined. Otherwise +** thely may interfere with compilation of other functions in this file +** (or in another file, if this file becomes part of the amalgamation). */ +#ifdef isView + #undef isView +#endif +#ifdef pTrigger + #undef pTrigger +#endif +#ifdef tmask + #undef tmask +#endif + /* ** Generate code to do constraint checks prior to an INSERT or an UPDATE. ** ** The input is a range of consecutive registers as follows: ** -** 1. The rowid of the row to be updated before the update. This -** value is omitted unless we are doing an UPDATE that involves a -** change to the record number or writing to a virtual table. +** 1. The rowid of the row after the update. ** -** 2. The rowid of the row after the update. -** -** 3. The data in the first column of the entry after the update. +** 2. The data in the first column of the entry after the update. ** ** i. Data from middle columns... ** ** N. The data in the last column of the entry after the update. ** -** The regRowid parameter is the index of the register containing (2). +** The regRowid parameter is the index of the register containing (1). ** -** The old rowid shown as entry (1) above is omitted unless both isUpdate -** and rowidChng are 1. isUpdate is true for UPDATEs and false for -** INSERTs. RowidChng means that the new rowid is explicitly specified by -** the update or insert statement. If rowidChng is false, it means that -** the rowid is computed automatically in an insert or that the rowid value -** is not modified by the update. +** If isUpdate is true and rowidChng is non-zero, then rowidChng contains +** the address of a register containing the rowid before the update takes +** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate +** is false, indicating an INSERT statement, then a non-zero rowidChng +** indicates that the rowid was explicitly specified as part of the +** INSERT statement. If rowidChng is false, it means that the rowid is +** computed automatically in an insert or that the rowid value is not +** modified by an update. ** ** The code generated by this routine store new index entries into ** registers identified by aRegIdx[]. No index entry is created for ** indices where aRegIdx[i]==0. The order of indices in aRegIdx[] is ** the same as the order of indices on the linked list of indices @@ -1153,18 +1154,17 @@ int j2 = 0, j3; /* Addresses of jump instructions */ int regData; /* Register containing first data column */ int iCur; /* Table cursor number */ Index *pIdx; /* Pointer to one of the indices */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ - int hasTwoRowids = (isUpdate && rowidChng); + int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ nCol = pTab->nCol; regData = regRowid + 1; - /* Test all NOT NULL constraints. */ for(i=0; iiPKey ){ @@ -1181,12 +1181,13 @@ onError = OE_Abort; } assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail || onError==OE_Ignore || onError==OE_Replace ); switch( onError ){ - case OE_Rollback: case OE_Abort: + sqlite3MayAbort(pParse); + case OE_Rollback: case OE_Fail: { char *zMsg; j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT, onError, regData+i); zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", @@ -1217,11 +1218,11 @@ sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL); onError = overrideError!=OE_Default ? overrideError : OE_Abort; if( onError==OE_Ignore ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); }else{ - sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_CONSTRAINT, onError); + sqlite3HaltConstraint(pParse, onError, 0, 0); } sqlite3VdbeResolveLabel(v, allOk); } #endif /* !defined(SQLITE_OMIT_CHECK) */ @@ -1235,42 +1236,60 @@ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } - if( onError!=OE_Replace || pTab->pIndex ){ - if( isUpdate ){ - j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, regRowid-1); - } - j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid); - switch( onError ){ - default: { - onError = OE_Abort; - /* Fall thru into the next case */ - } - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, - "PRIMARY KEY must be unique", P4_STATIC); - break; - } - case OE_Replace: { + if( isUpdate ){ + j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng); + } + j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid); + switch( onError ){ + default: { + onError = OE_Abort; + /* Fall thru into the next case */ + } + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + sqlite3HaltConstraint( + pParse, onError, "PRIMARY KEY must be unique", P4_STATIC); + break; + } + case OE_Replace: { + /* If there are DELETE triggers on this table and the + ** recursive-triggers flag is set, call GenerateRowDelete() to + ** remove the conflicting row from the the table. This will fire + ** the triggers and remove both the table and index b-tree entries. + ** + ** Otherwise, if there are no triggers or the recursive-triggers + ** flag is not set, call GenerateRowIndexDelete(). This removes + ** the index b-tree entries only. The table b-tree entry will be + ** replaced by the new entry when it is inserted. */ + Trigger *pTrigger = 0; + if( pParse->db->flags&SQLITE_RecTriggers ){ + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); + } + sqlite3MultiWrite(pParse); + if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ + sqlite3GenerateRowDelete( + pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace + ); + }else{ sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); - seenReplace = 1; - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - break; - } - } - sqlite3VdbeJumpHere(v, j3); - if( isUpdate ){ - sqlite3VdbeJumpHere(v, j2); - } + } + seenReplace = 1; + break; + } + case OE_Ignore: { + assert( seenReplace==0 ); + sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); + break; + } + } + sqlite3VdbeJumpHere(v, j3); + if( isUpdate ){ + sqlite3VdbeJumpHere(v, j2); } } /* Test all UNIQUE constraints by creating entries for each UNIQUE ** index and making sure that duplicate entries do not already exist. @@ -1292,11 +1311,11 @@ sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i); } } sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); - sqlite3IndexAffinityStr(v, pIdx); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); /* Find out what action to take in case there is an indexing conflict */ onError = pIdx->onError; if( onError==OE_None ){ @@ -1311,14 +1330,13 @@ if( seenReplace ){ if( onError==OE_Ignore ) onError = OE_Replace; else if( onError==OE_Fail ) onError = OE_Abort; } - /* Check to see if the new index entry will be unique */ regR = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR); + sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR); j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0, regR, SQLITE_INT_TO_PTR(regIdx), P4_INT32); sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); @@ -1344,30 +1362,37 @@ sqlite3StrAccumAppend(&errMsg, zCol, -1); } sqlite3StrAccumAppend(&errMsg, pIdx->nColumn>1 ? " are not unique" : " is not unique", -1); zErr = sqlite3StrAccumFinish(&errMsg); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, zErr, 0); + sqlite3HaltConstraint(pParse, onError, zErr, 0); sqlite3DbFree(errMsg.db, zErr); break; } case OE_Ignore: { assert( seenReplace==0 ); sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); break; } default: { + Trigger *pTrigger = 0; assert( onError==OE_Replace ); - sqlite3GenerateRowDelete(pParse, pTab, baseCur, regR, 0); + sqlite3MultiWrite(pParse); + if( pParse->db->flags&SQLITE_RecTriggers ){ + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); + } + sqlite3GenerateRowDelete( + pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace + ); seenReplace = 1; break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); } - + if( pbMayReplace ){ *pbMayReplace = seenReplace; } } @@ -1385,11 +1410,10 @@ Table *pTab, /* the table into which we are inserting */ int baseCur, /* Index of a read/write cursor pointing at pTab */ int regRowid, /* Range of content */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ int isUpdate, /* True for UPDATE, False for INSERT */ - int newIdx, /* Index of NEW table for triggers. -1 if none */ int appendBias, /* True if this is likely to be an append */ int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */ ){ int i; Vdbe *v; @@ -1413,15 +1437,10 @@ regData = regRowid + 1; regRec = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); sqlite3TableAffinityStr(v, pTab); sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol); -#ifndef SQLITE_OMIT_TRIGGER - if( newIdx>=0 ){ - sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid); - } -#endif if( pParse->nested ){ pik_flags = 0; }else{ pik_flags = OPFLAG_NCHANGE; pik_flags |= (isUpdate?OPFLAG_ISUPDATE:OPFLAG_LASTROWID); @@ -1741,12 +1760,12 @@ regData = sqlite3GetTempReg(pParse); regRowid = sqlite3GetTempReg(pParse); if( pDest->iPKey>=0 ){ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, - "PRIMARY KEY must be unique", P4_STATIC); + sqlite3HaltConstraint( + pParse, onError, "PRIMARY KEY must be unique", P4_STATIC); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); }else if( pDest->pIndex==0 ){ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ @@ -1792,10 +1811,5 @@ }else{ return 1; } } #endif /* SQLITE_OMIT_XFER_OPT */ - -/* Make sure "isView" gets undefined in case this file becomes part of -** the amalgamation - so that subsequent files do not see isView as a -** macro. */ -#undef isView Index: src/journal.c ================================================================== --- src/journal.c +++ src/journal.c @@ -8,16 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** -** @(#) $Id: journal.c,v 1.9 2009/01/20 17:06:27 danielk1977 Exp $ -*/ - -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - -/* ** This file implements a special kind of sqlite3_file object used ** by SQLite to create journal files if the atomic-write optimization ** is enabled. ** ** The distinctive characteristic of this sqlite3_file is that the @@ -28,11 +22,11 @@ ** ** 1) The in-memory representation grows too large for the allocated ** buffer, or ** 2) The sqlite3JournalCreate() function is called. */ - +#ifdef SQLITE_ENABLE_ATOMIC_WRITE #include "sqliteInt.h" /* ** A JournalFile object is a subclass of sqlite3_file used by Index: src/legacy.c ================================================================== --- src/legacy.c +++ src/legacy.c @@ -11,12 +11,10 @@ ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. -** -** $Id: legacy.c,v 1.33 2009/05/05 20:02:48 drh Exp $ */ #include "sqliteInt.h" /* @@ -130,14 +128,17 @@ if( rc!=SQLITE_OK && ALWAYS(rc==sqlite3_errcode(db)) && pzErrMsg ){ int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db)); *pzErrMsg = sqlite3Malloc(nErrMsg); if( *pzErrMsg ){ memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg); + }else{ + rc = SQLITE_NOMEM; + sqlite3Error(db, SQLITE_NOMEM, 0); } }else if( pzErrMsg ){ *pzErrMsg = 0; } assert( (rc&db->errMask)==rc ); sqlite3_mutex_leave(db->mutex); return rc; } ADDED src/lempar.c Index: src/lempar.c ================================================================== --- /dev/null +++ src/lempar.c @@ -0,0 +1,860 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +** +** This version of "lempar.c" is modified, slightly, for use by SQLite. +** The only modifications are the addition of a couple of NEVER() +** macros to disable tests that are needed in the case of a general +** LALR(1) grammar but which are always false in the +** specific grammar used by SQLite. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +%% +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +%% +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +%% +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +%% + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { +%% +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ +%% + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + /* There is no mechanism by which the parser stack can be popped below + ** empty in SQLite. */ + if( NEVER(pParser->yyidx<0) ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + /* In SQLite, we never try to destroy a parser that was not successfully + ** created in the first place. */ + if( NEVER(pParser==0) ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_COUNT + || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( +#if YY_SHIFT_MIN+YYWILDCARD<0 + j>=0 && +#endif +#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT + j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_COUNT ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_COUNT ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +%% + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} Index: src/loadext.c ================================================================== --- src/loadext.c +++ src/loadext.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to dynamically load extensions into ** the SQLite library. -** -** $Id: loadext.c,v 1.60 2009/06/03 01:24:54 drh Exp $ */ #ifndef SQLITE_CORE #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ #endif Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -11,12 +11,10 @@ ************************************************************************* ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. -** -** $Id: main.c,v 1.560 2009/06/26 15:14:55 drh Exp $ */ #include "sqliteInt.h" #ifdef SQLITE_ENABLE_FTS3 # include "fts3.h" @@ -33,10 +31,11 @@ */ #ifndef SQLITE_AMALGAMATION const char sqlite3_version[] = SQLITE_VERSION; #endif const char *sqlite3_libversion(void){ return sqlite3_version; } +const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } #if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) /* @@ -123,17 +122,19 @@ ** malloc subsystem - this implies that the allocation of a static ** mutex must not require support from the malloc subsystem. */ pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); sqlite3_mutex_enter(pMaster); + sqlite3GlobalConfig.isMutexInit = 1; if( !sqlite3GlobalConfig.isMallocInit ){ rc = sqlite3MallocInit(); } if( rc==SQLITE_OK ){ sqlite3GlobalConfig.isMallocInit = 1; if( !sqlite3GlobalConfig.pInitMutex ){ - sqlite3GlobalConfig.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); + sqlite3GlobalConfig.pInitMutex = + sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){ rc = SQLITE_NOMEM; } } } @@ -140,14 +141,13 @@ if( rc==SQLITE_OK ){ sqlite3GlobalConfig.nRefInitMutex++; } sqlite3_mutex_leave(pMaster); - /* If unable to initialize the malloc subsystem, then return early. - ** There is little hope of getting SQLite to run if the malloc - ** subsystem cannot be initialized. - */ + /* If rc is not SQLITE_OK at this point, then either the malloc + ** subsystem could not be initialized or the system failed to allocate + ** the pInitMutex mutex. Return an error in either case. */ if( rc!=SQLITE_OK ){ return rc; } /* Do the rest of the initialization under the recursive mutex so @@ -160,13 +160,16 @@ if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); sqlite3GlobalConfig.inProgress = 1; memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); sqlite3RegisterGlobalFunctions(); - rc = sqlite3PcacheInitialize(); + if( sqlite3GlobalConfig.isPCacheInit==0 ){ + rc = sqlite3PcacheInitialize(); + } if( rc==SQLITE_OK ){ - rc = sqlite3_os_init(); + sqlite3GlobalConfig.isPCacheInit = 1; + rc = sqlite3OsInit(); } if( rc==SQLITE_OK ){ sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); sqlite3GlobalConfig.isInit = 1; @@ -217,18 +220,27 @@ ** on when SQLite is already shut down. If SQLite is already shut down ** when this routine is invoked, then this routine is a harmless no-op. */ int sqlite3_shutdown(void){ if( sqlite3GlobalConfig.isInit ){ - sqlite3GlobalConfig.isMallocInit = 0; - sqlite3PcacheShutdown(); sqlite3_os_end(); sqlite3_reset_auto_extension(); - sqlite3MallocEnd(); - sqlite3MutexEnd(); sqlite3GlobalConfig.isInit = 0; } + if( sqlite3GlobalConfig.isPCacheInit ){ + sqlite3PcacheShutdown(); + sqlite3GlobalConfig.isPCacheInit = 0; + } + if( sqlite3GlobalConfig.isMallocInit ){ + sqlite3MallocEnd(); + sqlite3GlobalConfig.isMallocInit = 0; + } + if( sqlite3GlobalConfig.isMutexInit ){ + sqlite3MutexEnd(); + sqlite3GlobalConfig.isMutexInit = 0; + } + return SQLITE_OK; } /* ** This API allows applications to modify the global configuration of @@ -716,10 +728,13 @@ if( db->flags&SQLITE_InternChanges ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); } + + /* Any deferred constraint violations have now been resolved. */ + db->nDeferredCons = 0; /* If one has been configured, invoke the rollback-hook callback */ if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ db->xRollbackCallback(db->pRollbackArg); } @@ -1202,11 +1217,11 @@ ** A virtual database can be either a disk file (that is automatically ** deleted when the file is closed) or it an be held entirely in memory. ** The sqlite3TempInMemory() function is used to determine which. */ int sqlite3BtreeFactory( - const sqlite3 *db, /* Main database when opening aux otherwise 0 */ + sqlite3 *db, /* Main database when opening aux otherwise 0 */ const char *zFilename, /* Name of the file containing the BTree database */ int omitJournal, /* if TRUE then do not journal this file */ int nCache, /* How many pages in the page cache */ int vfsFlags, /* Flags passed through to vfsOpen */ Btree **ppBtree /* Pointer to new Btree object written here */ @@ -1343,13 +1358,14 @@ /* ** Create a new collating function for database "db". The name is zName ** and the encoding is enc. */ static int createCollation( - sqlite3* db, + sqlite3* db, const char *zName, - int enc, + u8 enc, + u8 collType, void* pCtx, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDel)(void*) ){ CollSeq *pColl; @@ -1410,10 +1426,11 @@ if( pColl ){ pColl->xCmp = xCompare; pColl->pUser = pCtx; pColl->xDel = xDel; pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED)); + pColl->type = collType; } sqlite3Error(db, SQLITE_OK, 0); return SQLITE_OK; } @@ -1432,10 +1449,11 @@ SQLITE_MAX_VDBE_OP, SQLITE_MAX_FUNCTION_ARG, SQLITE_MAX_ATTACHED, SQLITE_MAX_LIKE_PATTERN_LENGTH, SQLITE_MAX_VARIABLE_NUMBER, + SQLITE_MAX_TRIGGER_DEPTH, }; /* ** Make sure the hard limits are set to reasonable values */ @@ -1461,16 +1479,16 @@ # error SQLITE_MAX_ATTACHED must be between 0 and 30 #endif #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 # error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 #endif -#if SQLITE_MAX_VARIABLE_NUMBER<1 -# error SQLITE_MAX_VARIABLE_NUMBER must be at least 1 -#endif #if SQLITE_MAX_COLUMN>32767 # error SQLITE_MAX_COLUMN must not exceed 32767 #endif +#if SQLITE_MAX_TRIGGER_DEPTH<1 +# error SQLITE_MAX_TRIGGER_DEPTH must be at least 1 +#endif /* ** Change the value of a limit. Report the old value. ** If an invalid limit index is supplied, report -1. @@ -1507,11 +1525,10 @@ unsigned flags, /* Operational flags */ const char *zVfs /* Name of the VFS to use */ ){ sqlite3 *db; int rc; - CollSeq *pColl; int isThreadsafe; *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); @@ -1524,10 +1541,15 @@ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; }else{ isThreadsafe = sqlite3GlobalConfig.bFullMutex; + } + if( flags & SQLITE_OPEN_PRIVATECACHE ){ + flags &= ~SQLITE_OPEN_SHAREDCACHE; + }else if( sqlite3GlobalConfig.sharedCacheEnabled ){ + flags |= SQLITE_OPEN_SHAREDCACHE; } /* Remove harmful bits from the flags parameter ** ** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were @@ -1576,10 +1598,13 @@ | SQLITE_LegacyFileFmt #endif #ifdef SQLITE_ENABLE_LOAD_EXTENSION | SQLITE_LoadExtension #endif +#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS + | SQLITE_RecTriggers +#endif ; sqlite3HashInit(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3HashInit(&db->aModule); #endif @@ -1593,29 +1618,27 @@ /* Add the default collation sequence BINARY. BINARY works for both UTF-8 ** and UTF-16, so add a version for each to avoid any unnecessary ** conversions. The only error that can occur here is a malloc() failure. */ - createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0); - createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF8, SQLITE_COLL_BINARY, 0, + binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF16BE, SQLITE_COLL_BINARY, 0, + binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF16LE, SQLITE_COLL_BINARY, 0, + binCollFunc, 0); + createCollation(db, "RTRIM", SQLITE_UTF8, SQLITE_COLL_USER, (void*)1, + binCollFunc, 0); if( db->mallocFailed ){ goto opendb_out; } db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0); assert( db->pDfltColl!=0 ); /* Also add a UTF-8 case-insensitive collation sequence. */ - createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0); - - /* Set flags on the built-in collating sequences */ - db->pDfltColl->type = SQLITE_COLL_BINARY; - pColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "NOCASE", 0); - if( pColl ){ - pColl->type = SQLITE_COLL_NOCASE; - } + createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, + nocaseCollatingFunc, 0); /* Open the backend database driver */ db->openFlags = flags; rc = sqlite3BtreeFactory(db, zFilename, 0, SQLITE_DEFAULT_CACHE_SIZE, flags | SQLITE_OPEN_MAIN_DB, @@ -1634,14 +1657,12 @@ /* The default safety_level for the main database is 'full'; for the temp ** database it is 'NONE'. This matches the pager layer defaults. */ db->aDb[0].zName = "main"; db->aDb[0].safety_level = 3; -#ifndef SQLITE_OMIT_TEMPDB db->aDb[1].zName = "temp"; db->aDb[1].safety_level = 1; -#endif db->magic = SQLITE_MAGIC_OPEN; if( db->mallocFailed ){ goto opendb_out; } @@ -1794,11 +1815,11 @@ int(*xCompare)(void*,int,const void*,int,const void*) ){ int rc; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); - rc = createCollation(db, zName, enc, pCtx, xCompare, 0); + rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } @@ -1814,11 +1835,11 @@ void(*xDel)(void*) ){ int rc; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); - rc = createCollation(db, zName, enc, pCtx, xCompare, xDel); + rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, xDel); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } @@ -1837,11 +1858,11 @@ char *zName8; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); zName8 = sqlite3Utf16to8(db, zName, -1); if( zName8 ){ - rc = createCollation(db, zName8, enc, pCtx, xCompare, 0); + rc = createCollation(db, zName8, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0); sqlite3DbFree(db, zName8); } rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; @@ -2243,10 +2264,25 @@ case SQLITE_TESTCTRL_ALWAYS: { int x = va_arg(ap,int); rc = ALWAYS(x); break; } + + /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N) + ** + ** Set the nReserve size to N for the main database on the database + ** connection db. + */ + case SQLITE_TESTCTRL_RESERVE: { + sqlite3 *db = va_arg(ap, sqlite3*); + int x = va_arg(ap,int); + sqlite3_mutex_enter(db->mutex); + sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0); + sqlite3_mutex_leave(db->mutex); + break; + } + } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } Index: src/malloc.c ================================================================== --- src/malloc.c +++ src/malloc.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** Memory allocation functions used throughout sqlite. -** -** $Id: malloc.c,v 1.64 2009/06/27 00:48:33 drh Exp $ */ #include "sqliteInt.h" #include /* @@ -41,11 +39,13 @@ if( n<0 ){ iLimit = 0; }else{ iLimit = n; } +#ifndef SQLITE_OMIT_AUTOINIT sqlite3_initialize(); +#endif if( iLimit>0 ){ sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, iLimit); }else{ sqlite3MemoryAlarm(0, 0, 0); } @@ -61,13 +61,10 @@ ** cache database pages that are not currently in use. */ int sqlite3_release_memory(int n){ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT int nRet = 0; -#if 0 - nRet += sqlite3VdbeReleaseMemory(n); -#endif nRet += sqlite3PcacheReleaseMemory(n-nRet); return nRet; #else UNUSED_PARAMETER(n); return SQLITE_OK; @@ -86,26 +83,24 @@ /* ** The alarm callback and its arguments. The mem0.mutex lock will ** be held while the callback is running. Recursive calls into ** the memory subsystem are allowed, but no new callbacks will be - ** issued. The alarmBusy variable is set to prevent recursive - ** callbacks. + ** issued. */ sqlite3_int64 alarmThreshold; void (*alarmCallback)(void*, sqlite3_int64,int); void *alarmArg; - int alarmBusy; /* ** Pointers to the end of sqlite3GlobalConfig.pScratch and ** sqlite3GlobalConfig.pPage to a block of memory that records ** which pages are available. */ u32 *aScratchFree; u32 *aPageFree; -} mem0 = { 62560955, 0, 0, 0, 0, 0, 0, 0, 0 }; +} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 }; #define mem0 GLOBAL(struct Mem0Global, mem0) /* ** Initialize the memory allocation subsystem. @@ -218,19 +213,20 @@ */ static void sqlite3MallocAlarm(int nByte){ void (*xCallback)(void*,sqlite3_int64,int); sqlite3_int64 nowUsed; void *pArg; - if( mem0.alarmCallback==0 || mem0.alarmBusy ) return; - mem0.alarmBusy = 1; + if( mem0.alarmCallback==0 ) return; xCallback = mem0.alarmCallback; nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); pArg = mem0.alarmArg; + mem0.alarmCallback = 0; sqlite3_mutex_leave(mem0.mutex); xCallback(pArg, nowUsed, nByte); sqlite3_mutex_enter(mem0.mutex); - mem0.alarmBusy = 0; + mem0.alarmCallback = xCallback; + mem0.alarmArg = pArg; } /* ** Do a memory allocation with statistics and alarms. Assume the ** lock is already held. @@ -422,13 +418,11 @@ 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) ){ + if( isLookaside(db, p) ){ return db->lookaside.sz; }else{ return sqlite3GlobalConfig.m.xSize(p); } } @@ -480,34 +474,32 @@ if( nBytes>=0x7fffff00 ){ /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ return 0; } nOld = sqlite3MallocSize(pOld); - if( sqlite3GlobalConfig.bMemstat ){ + nNew = sqlite3GlobalConfig.m.xRoundup(nBytes); + if( nOld==nNew ){ + pNew = pOld; + }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); - nNew = sqlite3GlobalConfig.m.xRoundup(nBytes); - if( nOld==nNew ){ - pNew = pOld; - }else{ - if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= - mem0.alarmThreshold ){ - sqlite3MallocAlarm(nNew-nOld); - } - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - if( pNew==0 && mem0.alarmCallback ){ - sqlite3MallocAlarm(nBytes); - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - } - if( pNew ){ - nNew = sqlite3MallocSize(pNew); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); - } + if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= + mem0.alarmThreshold ){ + sqlite3MallocAlarm(nNew-nOld); + } + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); + if( pNew==0 && mem0.alarmCallback ){ + sqlite3MallocAlarm(nBytes); + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); + } + if( pNew ){ + nNew = sqlite3MallocSize(pNew); + sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nBytes); + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } return pNew; } /* Index: src/mem0.c ================================================================== --- src/mem0.c +++ src/mem0.c @@ -13,12 +13,10 @@ ** This file contains a no-op memory allocation drivers for use when ** SQLITE_ZERO_MALLOC is defined. The allocation drivers implemented ** here always fail. SQLite will not operate with these drivers. These ** are merely placeholders. Real drivers must be substituted using ** sqlite3_config() before SQLite will operate. -** -** $Id: mem0.c,v 1.1 2008/10/28 18:58:20 drh Exp $ */ #include "sqliteInt.h" /* ** This version of the memory allocator is the default. It is Index: src/mem1.c ================================================================== --- src/mem1.c +++ src/mem1.c @@ -14,12 +14,10 @@ ** SQLite will use the standard C-library malloc/realloc/free interface ** to obtain the memory it needs. ** ** This file contains implementations of the low-level memory allocation ** routines specified in the sqlite3_mem_methods object. -** -** $Id: mem1.c,v 1.30 2009/03/23 04:33:33 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** This version of the memory allocator is the default. It is Index: src/mem2.c ================================================================== --- src/mem2.c +++ src/mem2.c @@ -16,12 +16,10 @@ ** information to each allocation in order to help detect and fix memory ** leaks and memory usage errors. ** ** This file contains implementations of the low-level memory allocation ** routines specified in the sqlite3_mem_methods object. -** -** $Id: mem2.c,v 1.45 2009/03/23 04:33:33 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** This version of the memory allocator is used only if the Index: src/mem3.c ================================================================== --- src/mem3.c +++ src/mem3.c @@ -20,12 +20,10 @@ ** the amount of memory available to SQLite is fixed and cannot ** be changed. ** ** This version of the memory allocation subsystem is included ** in the build only if SQLITE_ENABLE_MEMSYS3 is defined. -** -** $Id: mem3.c,v 1.25 2008/11/19 16:52:44 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** This version of the memory allocator is only built into the library @@ -576,10 +574,11 @@ /* ** Deinitialize this module. */ static void memsys3Shutdown(void *NotUsed){ UNUSED_PARAMETER(NotUsed); + mem3.mutex = 0; return; } Index: src/mem5.c ================================================================== --- src/mem5.c +++ src/mem5.c @@ -11,21 +11,44 @@ ************************************************************************* ** This file contains the C functions that implement a memory ** allocation subsystem for use by SQLite. ** ** This version of the memory allocation subsystem omits all -** use of malloc(). The SQLite user supplies a block of memory +** use of malloc(). The application gives SQLite a block of memory ** before calling sqlite3_initialize() from which allocations ** are made and returned by the xMalloc() and xRealloc() ** implementations. Once sqlite3_initialize() has been called, ** the amount of memory available to SQLite is fixed and cannot ** be changed. ** ** This version of the memory allocation subsystem is included ** in the build only if SQLITE_ENABLE_MEMSYS5 is defined. ** -** $Id: mem5.c,v 1.19 2008/11/19 16:52:44 danielk1977 Exp $ +** This memory allocator uses the following algorithm: +** +** 1. All memory allocations sizes are rounded up to a power of 2. +** +** 2. If two adjacent free blocks are the halves of a larger block, +** then the two blocks are coalesed into the single larger block. +** +** 3. New memory is allocated from the first available free block. +** +** This algorithm is described in: J. M. Robson. "Bounds for Some Functions +** Concerning Dynamic Storage Allocation". Journal of the Association for +** Computing Machinery, Volume 21, Number 8, July 1974, pages 491-499. +** +** Let n be the size of the largest allocation divided by the minimum +** allocation size (after rounding all sizes up to a power of 2.) Let M +** be the maximum amount of memory ever outstanding at one time. Let +** N be the total amount of memory available for allocation. Robson +** proved that this memory allocator will never breakdown due to +** fragmentation as long as the following constraint holds: +** +** N >= M*(1 + log2(n)/2) - n + 1 +** +** The sqlite3_status() logic tracks the maximum values of n and M so +** that an application can, at any time, verify this constraint. */ #include "sqliteInt.h" /* ** This version of the memory allocator is used only when @@ -35,28 +58,31 @@ /* ** A minimum allocation is an instance of the following structure. ** Larger allocations are an array of these structures where the ** size of the array is a power of 2. +** +** The size of this object must be a power of two. That fact is +** verified in memsys5Init(). */ typedef struct Mem5Link Mem5Link; struct Mem5Link { int next; /* Index of next free chunk */ int prev; /* Index of previous free chunk */ }; /* -** Maximum size of any allocation is ((1<=0 && i0 ); /* Keep track of the maximum allocation request. Even unfulfilled ** requests are counted */ if( (u32)nByte>mem5.maxRequest ){ mem5.maxRequest = nByte; } + + /* Abort if the requested allocation size is larger than the largest + ** power of two that we can represent using 32-bit signed integers. + */ + if( nByte > 0x40000000 ){ + return 0; + } /* Round nByte up to the next valid power of two */ - for(iFullSz=mem5.nAtom, iLogsize=0; iFullSz=0 && iBlock0 ); - assert( mem5.currentOut>=(size*mem5.nAtom) ); + assert( mem5.currentOut>=(size*mem5.szAtom) ); mem5.currentCount--; - mem5.currentOut -= size*mem5.nAtom; + mem5.currentOut -= size*mem5.szAtom; assert( mem5.currentOut>0 || mem5.currentCount==0 ); assert( mem5.currentCount>0 || mem5.currentOut==0 ); mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; - while( iLogsize>iLogsize) & 1 ){ iBuddy = iBlock - size; }else{ iBuddy = iBlock + size; @@ -314,32 +362,40 @@ return (void*)p; } /* ** Free memory. +** +** The outer layer memory allocator prevents this routine from +** being called with pPrior==0. */ static void memsys5Free(void *pPrior){ - if( pPrior==0 ){ -assert(0); - return; - } + assert( pPrior!=0 ); memsys5Enter(); memsys5FreeUnsafe(pPrior); memsys5Leave(); } /* -** Change the size of an existing memory allocation +** Change the size of an existing memory allocation. +** +** The outer layer memory allocator prevents this routine from +** being called with pPrior==0. +** +** nBytes is always a value obtained from a prior call to +** memsys5Round(). Hence nBytes is always a non-negative power +** of two. If nBytes==0 that means that an oversize allocation +** (an allocation larger than 0x40000000) was requested and this +** routine should return 0 without freeing pPrior. */ static void *memsys5Realloc(void *pPrior, int nBytes){ int nOld; void *p; - if( pPrior==0 ){ - return memsys5Malloc(nBytes); - } - if( nBytes<=0 ){ - memsys5Free(pPrior); + assert( pPrior!=0 ); + assert( (nBytes&(nBytes-1))==0 ); + assert( nBytes>=0 ); + if( nBytes==0 ){ return 0; } nOld = memsys5Size(pPrior); if( nBytes<=nOld ){ return pPrior; @@ -353,49 +409,77 @@ memsys5Leave(); return p; } /* -** Round up a request size to the next valid allocation size. +** Round up a request size to the next valid allocation size. If +** the allocation is too large to be handled by this allocation system, +** return 0. +** +** All allocations must be a power of two and must be expressed by a +** 32-bit signed integer. Hence the largest allocation is 0x40000000 +** or 1073741824 bytes. */ static int memsys5Roundup(int n){ int iFullSz; - for(iFullSz=mem5.nAtom; iFullSz 0x40000000 ) return 0; + for(iFullSz=mem5.szAtom; iFullSz 0 +** memsys5Log(2) -> 1 +** memsys5Log(4) -> 2 +** memsys5Log(5) -> 3 +** memsys5Log(8) -> 3 +** memsys5Log(9) -> 4 +*/ static int memsys5Log(int iValue){ int iLog; for(iLog=0; (1<mem5.nAtom ){ - mem5.nAtom = mem5.nAtom << 1; + mem5.szAtom = (1<mem5.szAtom ){ + mem5.szAtom = mem5.szAtom << 1; } - mem5.nBlock = (nByte / (mem5.nAtom+sizeof(u8))); + mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8))); mem5.zPool = zByte; - mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.nAtom]; + mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.szAtom]; for(ii=0; ii<=LOGMAX; ii++){ mem5.aiFreelist[ii] = -1; } @@ -407,28 +491,34 @@ memsys5Link(iOffset, ii); iOffset += nAlloc; } assert((iOffset+nAlloc)>mem5.nBlock); } + + /* If a mutex is required for normal operation, allocate one */ + if( sqlite3GlobalConfig.bMemstat==0 ){ + mem5.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); + } return SQLITE_OK; } /* ** Deinitialize this module. */ static void memsys5Shutdown(void *NotUsed){ UNUSED_PARAMETER(NotUsed); + mem5.mutex = 0; return; } +#ifdef SQLITE_TEST /* ** Open the file indicated and write a log of all unfreed memory ** allocations into that log. */ void sqlite3Memsys5Dump(const char *zFilename){ -#ifdef SQLITE_DEBUG FILE *out; int i, j, n; int nMinLog; if( zFilename==0 || zFilename[0]==0 ){ @@ -440,14 +530,14 @@ zFilename); return; } } memsys5Enter(); - nMinLog = memsys5Log(mem5.nAtom); + nMinLog = memsys5Log(mem5.szAtom); for(i=0; i<=LOGMAX && i+nMinLog<32; i++){ for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){} - fprintf(out, "freelist items of size %d: %d\n", mem5.nAtom << i, n); + fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n); } fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc); fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc); fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess); fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut); @@ -459,14 +549,12 @@ if( out==stdout ){ fflush(stdout); }else{ fclose(out); } -#else - UNUSED_PARAMETER(zFilename); -#endif } +#endif /* ** This routine is the only routine in this file with external ** linkage. It returns a pointer to a static sqlite3_mem_methods ** struct populated with the memsys5 methods. Index: src/memjournal.c ================================================================== --- src/memjournal.c +++ src/memjournal.c @@ -11,12 +11,10 @@ ************************************************************************* ** ** This file contains code use to implement an in-memory rollback journal. ** 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.12 2009/05/04 11:42:30 danielk1977 Exp $ */ #include "sqliteInt.h" /* Forward references to internal structures */ typedef struct MemJournal MemJournal; Index: src/mutex.c ================================================================== --- src/mutex.c +++ src/mutex.c @@ -10,15 +10,22 @@ ** ************************************************************************* ** This file contains the C functions that implement mutexes. ** ** This file contains code that is common across all mutex implementations. - -** -** $Id: mutex.c,v 1.30 2009/02/17 16:29:11 danielk1977 Exp $ */ #include "sqliteInt.h" + +#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT) +/* +** For debugging purposes, record when the mutex subsystem is initialized +** and uninitialized so that we can assert() if there is an attempt to +** allocate a mutex while the system is uninitialized. +*/ +static SQLITE_WSD int mutexIsInit = 0; +#endif /* SQLITE_DEBUG */ + #ifndef SQLITE_MUTEX_OMIT /* ** Initialize the mutex system. */ @@ -28,37 +35,25 @@ if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){ /* If the xMutexAlloc method has not been set, then the user did not ** install a mutex implementation via sqlite3_config() prior to ** sqlite3_initialize() being called. This block copies pointers to ** the default implementation into the sqlite3GlobalConfig structure. - ** - ** The danger is that although sqlite3_config() is not a threadsafe - ** API, sqlite3_initialize() is, and so multiple threads may be - ** attempting to run this function simultaneously. To guard write - ** access to the sqlite3GlobalConfig structure, the 'MASTER' static mutex - ** is obtained before modifying it. */ - sqlite3_mutex_methods *p = sqlite3DefaultMutex(); - sqlite3_mutex *pMaster = 0; - - rc = p->xMutexInit(); - if( rc==SQLITE_OK ){ - pMaster = p->xMutexAlloc(SQLITE_MUTEX_STATIC_MASTER); - assert(pMaster); - p->xMutexEnter(pMaster); - assert( sqlite3GlobalConfig.mutex.xMutexAlloc==0 - || sqlite3GlobalConfig.mutex.xMutexAlloc==p->xMutexAlloc - ); - if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){ - sqlite3GlobalConfig.mutex = *p; - } - p->xMutexLeave(pMaster); - } - }else{ - rc = sqlite3GlobalConfig.mutex.xMutexInit(); - } - } + sqlite3_mutex_methods *pFrom = sqlite3DefaultMutex(); + sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex; + + memcpy(pTo, pFrom, offsetof(sqlite3_mutex_methods, xMutexAlloc)); + memcpy(&pTo->xMutexFree, &pFrom->xMutexFree, + sizeof(*pTo) - offsetof(sqlite3_mutex_methods, xMutexFree)); + pTo->xMutexAlloc = pFrom->xMutexAlloc; + } + rc = sqlite3GlobalConfig.mutex.xMutexInit(); + } + +#ifdef SQLITE_DEBUG + GLOBAL(int, mutexIsInit) = 1; +#endif return rc; } /* @@ -68,10 +63,15 @@ int sqlite3MutexEnd(void){ int rc = SQLITE_OK; if( sqlite3GlobalConfig.mutex.xMutexEnd ){ rc = sqlite3GlobalConfig.mutex.xMutexEnd(); } + +#ifdef SQLITE_DEBUG + GLOBAL(int, mutexIsInit) = 0; +#endif + return rc; } /* ** Retrieve a pointer to a static mutex or allocate a new dynamic one. @@ -85,10 +85,11 @@ sqlite3_mutex *sqlite3MutexAlloc(int id){ if( !sqlite3GlobalConfig.bCoreMutex ){ return 0; } + assert( GLOBAL(int, mutexIsInit) ); return sqlite3GlobalConfig.mutex.xMutexAlloc(id); } /* ** Free a dynamic mutex. @@ -144,6 +145,6 @@ int sqlite3_mutex_notheld(sqlite3_mutex *p){ return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); } #endif -#endif /* SQLITE_OMIT_MUTEX */ +#endif /* SQLITE_MUTEX_OMIT */ Index: src/mutex.h ================================================================== --- src/mutex.h +++ src/mutex.h @@ -16,12 +16,10 @@ ** better organized. ** ** NOTE: source files should *not* #include this header file directly. ** Source files should #include the sqliteInt.h file and let that file ** include this one indirectly. -** -** $Id: mutex.h,v 1.9 2008/10/07 15:25:48 drh Exp $ */ /* ** Figure out what version of the code to use. The choices are @@ -68,6 +66,6 @@ #define sqlite3_mutex_held(X) 1 #define sqlite3_mutex_notheld(X) 1 #define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) #define sqlite3MutexInit() SQLITE_OK #define sqlite3MutexEnd() -#endif /* defined(SQLITE_OMIT_MUTEX) */ +#endif /* defined(SQLITE_MUTEX_OMIT) */ Index: src/mutex_noop.c ================================================================== --- src/mutex_noop.c +++ src/mutex_noop.c @@ -22,12 +22,10 @@ ** interface. ** ** If compiled with SQLITE_DEBUG, then additional logic is inserted ** that does error checking on mutexes to make sure they are being ** called correctly. -** -** $Id: mutex_noop.c,v 1.3 2008/12/05 17:17:08 drh Exp $ */ #include "sqliteInt.h" #if defined(SQLITE_MUTEX_NOOP) && !defined(SQLITE_DEBUG) Index: src/mutex_os2.c ================================================================== --- src/mutex_os2.c +++ src/mutex_os2.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains the C functions that implement mutexes for OS/2 -** -** $Id: mutex_os2.c,v 1.11 2008/11/22 19:50:54 pweilbacher Exp $ */ #include "sqliteInt.h" /* ** The code in this file is only used if SQLITE_MUTEX_OS2 is defined. Index: src/mutex_unix.c ================================================================== --- src/mutex_unix.c +++ src/mutex_unix.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains the C functions that implement mutexes for pthreads -** -** $Id: mutex_unix.c,v 1.16 2008/12/08 18:19:18 drh Exp $ */ #include "sqliteInt.h" /* ** The code in this file is only used if we are compiling threadsafe @@ -89,10 +87,11 @@ **
  • SQLITE_MUTEX_STATIC_MASTER **
  • SQLITE_MUTEX_STATIC_MEM **
  • SQLITE_MUTEX_STATIC_MEM2 **
  • SQLITE_MUTEX_STATIC_PRNG **
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 ** ** ** The first two constants cause sqlite3_mutex_alloc() to create ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. @@ -102,11 +101,11 @@ ** cases where it really needs one. If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Three static mutexes are +** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. Index: src/mutex_w32.c ================================================================== --- src/mutex_w32.c +++ src/mutex_w32.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains the C functions that implement mutexes for win32 -** -** $Id: mutex_w32.c,v 1.17 2009/06/01 17:10:22 shane Exp $ */ #include "sqliteInt.h" /* ** The code in this file is only used if we are compiling multithreaded @@ -91,17 +89,18 @@ */ static long winMutex_lock = 0; static int winMutexInit(void){ /* The first to increment to 1 does actual initialization */ - if( InterlockedIncrement(&winMutex_lock)==1 ){ + if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ int i; - for(i=0; i -**
  • SQLITE_MUTEX_FAST 0 -**
  • SQLITE_MUTEX_RECURSIVE 1 -**
  • SQLITE_MUTEX_STATIC_MASTER 2 -**
  • SQLITE_MUTEX_STATIC_MEM 3 -**
  • SQLITE_MUTEX_STATIC_PRNG 4 +**
  • SQLITE_MUTEX_FAST +**
  • SQLITE_MUTEX_RECURSIVE +**
  • SQLITE_MUTEX_STATIC_MASTER +**
  • SQLITE_MUTEX_STATIC_MEM +**
  • SQLITE_MUTEX_STATIC_MEM2 +**
  • SQLITE_MUTEX_STATIC_PRNG +**
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 ** ** ** The first two constants cause sqlite3_mutex_alloc() to create ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. @@ -146,11 +148,11 @@ ** cases where it really needs one. If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Three static mutexes are +** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. @@ -175,11 +177,11 @@ break; } default: { assert( winMutex_isInit==1 ); assert( iType-2 >= 0 ); - assert( iType-2 < sizeof(winMutex_staticMutexes)/sizeof(winMutex_staticMutexes[0]) ); + assert( iType-2 < ArraySize(winMutex_staticMutexes) ); p = &winMutex_staticMutexes[iType-2]; p->id = iType; break; } } Index: src/notify.c ================================================================== --- src/notify.c +++ src/notify.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** ** 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. */ Index: src/os.c ================================================================== --- src/os.c +++ src/os.c @@ -10,12 +10,10 @@ ** ****************************************************************************** ** ** This file contains OS interface code that is common to all ** architectures. -** -** $Id: os.c,v 1.126 2009/03/25 14:24:42 drh Exp $ */ #define _SQLITE_OS_C_ 1 #include "sqliteInt.h" #undef _SQLITE_OS_C_ @@ -35,17 +33,17 @@ ** sqlite3OsSync() ** sqlite3OsLock() ** */ #if defined(SQLITE_TEST) && (SQLITE_OS_WIN==0) - #define DO_OS_MALLOC_TEST if (1) { \ - void *pTstAlloc = sqlite3Malloc(10); \ - if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \ - sqlite3_free(pTstAlloc); \ + #define DO_OS_MALLOC_TEST(x) if (!x || !sqlite3IsMemJournal(x)) { \ + void *pTstAlloc = sqlite3Malloc(10); \ + if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \ + sqlite3_free(pTstAlloc); \ } #else - #define DO_OS_MALLOC_TEST + #define DO_OS_MALLOC_TEST(x) #endif /* ** The following routines are convenience wrappers around methods ** of the sqlite3_file object. This is mostly just syntactic sugar. All @@ -59,37 +57,37 @@ pId->pMethods = 0; } return rc; } int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xRead(id, pBuf, amt, offset); } int sqlite3OsWrite(sqlite3_file *id, const void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xWrite(id, pBuf, amt, offset); } int sqlite3OsTruncate(sqlite3_file *id, i64 size){ return id->pMethods->xTruncate(id, size); } int sqlite3OsSync(sqlite3_file *id, int flags){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xSync(id, flags); } int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xFileSize(id, pSize); } int sqlite3OsLock(sqlite3_file *id, int lockType){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xLock(id, lockType); } int sqlite3OsUnlock(sqlite3_file *id, int lockType){ return id->pMethods->xUnlock(id, lockType); } int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xCheckReservedLock(id, pResOut); } int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ return id->pMethods->xFileControl(id, op, pArg); } @@ -111,12 +109,16 @@ sqlite3_file *pFile, int flags, int *pFlagsOut ){ int rc; - DO_OS_MALLOC_TEST; - rc = pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut); + DO_OS_MALLOC_TEST(0); + /* 0x7f1f is a mask of SQLITE_OPEN_ flags that are valid to be passed + ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, + ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before + ** reaching the VFS. */ + rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x7f1f, 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); @@ -125,11 +127,11 @@ sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(0); return pVfs->xAccess(pVfs, zPath, flags, pResOut); } int sqlite3OsFullPathname( sqlite3_vfs *pVfs, const char *zPath, @@ -187,10 +189,23 @@ assert( pFile ); rc = sqlite3OsClose(pFile); sqlite3_free(pFile); return rc; } + +/* +** This function is a wrapper around the OS specific implementation of +** sqlite3_os_init(). The purpose of the wrapper is to provide the +** ability to simulate a malloc failure, so that the handling of an +** error in sqlite3_os_init() by the upper layers can be tested. +*/ +int sqlite3OsInit(void){ + void *p = sqlite3_malloc(10); + if( p==0 ) return SQLITE_NOMEM; + sqlite3_free(p); + return sqlite3_os_init(); +} /* ** The list of all registered VFS implementations. */ static sqlite3_vfs * SQLITE_WSD vfsList = 0; Index: src/os.h ================================================================== --- src/os.h +++ src/os.h @@ -14,12 +14,10 @@ ** "os.c") attempt to abstract the underlying operating system so that ** the SQLite library will work on both POSIX and windows systems. ** ** This header file is #include-ed by sqliteInt.h and thus ends up ** being included by every source file. -** -** $Id: os.h,v 1.108 2009/02/05 16:31:46 drh Exp $ */ #ifndef _SQLITE_OS_H_ #define _SQLITE_OS_H_ /* @@ -222,10 +220,15 @@ #define PENDING_BYTE sqlite3PendingByte #define RESERVED_BYTE (PENDING_BYTE+1) #define SHARED_FIRST (PENDING_BYTE+2) #define SHARED_SIZE 510 +/* +** Wrapper around OS specific sqlite3_os_init() function. +*/ +int sqlite3OsInit(void); + /* ** Functions for accessing sqlite3_file methods */ int sqlite3OsClose(sqlite3_file*); int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); Index: src/os_common.h ================================================================== --- src/os_common.h +++ src/os_common.h @@ -14,12 +14,10 @@ ** all of the platform-specific files (os_*.c) and is #included into those ** files. ** ** 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.38 2009/02/24 18:40:50 danielk1977 Exp $ */ #ifndef _OS_COMMON_H_ #define _OS_COMMON_H_ /* Index: src/os_os2.c ================================================================== --- src/os_os2.c +++ src/os_os2.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This file contains code that is specific to OS/2. -** -** $Id: os_os2.c,v 1.63 2008/12/10 19:26:24 drh Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_OS2 Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -40,12 +40,10 @@ ** methods plus "finder" functions for each locking method. ** * sqlite3_vfs method implementations. ** * Locking primitives for the proxy uber-locking-method. (MacOSX only) ** * 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.253 2009/06/17 13:09:39 drh Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_UNIX /* This file is used on unix only */ /* @@ -167,10 +165,23 @@ ** a normal expected return code of SQLITE_BUSY or SQLITE_OK */ #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) +/* +** Sometimes, after a file handle is closed by SQLite, the file descriptor +** cannot be closed immediately. In these cases, instances of the following +** structure are used to store the file descriptor while waiting for an +** opportunity to either close or reuse it. +*/ +typedef struct UnixUnusedFd UnixUnusedFd; +struct UnixUnusedFd { + int fd; /* File descriptor to close */ + int flags; /* Flags this file descriptor was opened with */ + UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ +}; + /* ** The unixFile structure is subclass of sqlite3_file specific to the unix ** VFS implementations. */ typedef struct unixFile unixFile; @@ -181,10 +192,12 @@ int h; /* The file descriptor */ int dirfd; /* File descriptor for the directory */ unsigned char locktype; /* The type of lock held on this fd */ int lastErrno; /* The unix errno from the last I/O error */ void *lockingContext; /* Locking style specific state */ + UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ + int fileFlags; /* Miscellanous flags */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_THREADSAFE && defined(__linux__) pthread_t tid; /* The thread that "owns" this unixFile */ @@ -202,24 +215,24 @@ ** one described by ticket #3584. */ unsigned char transCntrChng; /* True if the transaction counter changed */ unsigned char dbUpdate; /* True if any part of database file changed */ unsigned char inNormalWrite; /* True if in a normal write operation */ - - /* If true, that means we are dealing with a database file that has - ** a range of locking bytes from PENDING_BYTE through PENDING_BYTE+511 - ** which should never be read or written. Asserts() will verify this */ - unsigned char isLockable; /* True if file might be locked */ #endif #ifdef SQLITE_TEST /* In test mode, increase the size of this structure a bit so that ** it is larger than the struct CrashFile defined in test6.c. */ char aPadding[32]; #endif }; +/* +** The following macros define bits in unixFile.fileFlags +*/ +#define SQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */ + /* ** Include code that is common to all os_*.c files */ #include "os_common.h" @@ -260,18 +273,34 @@ #define threadid 0 #endif /* -** Helper functions to obtain and relinquish the global mutex. +** Helper functions to obtain and relinquish the global mutex. The +** global mutex is used to protect the unixOpenCnt, unixLockInfo and +** vxworksFileId objects used by this file, all of which may be +** shared by multiple threads. +** +** Function unixMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() +** statements. e.g. +** +** unixEnterMutex() +** assert( unixMutexHeld() ); +** unixEnterLeave() */ static void unixEnterMutex(void){ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } static void unixLeaveMutex(void){ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } +#ifdef SQLITE_DEBUG +static int unixMutexHeld(void) { + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +#endif #ifdef SQLITE_DEBUG /* ** Helper function for printing out trace information from debugging @@ -278,15 +307,15 @@ ** binaries. This returns the string represetation of the supplied ** integer lock-type. */ static const char *locktypeName(int locktype){ switch( locktype ){ - case NO_LOCK: return "NONE"; - case SHARED_LOCK: return "SHARED"; - case RESERVED_LOCK: return "RESERVED"; - case PENDING_LOCK: return "PENDING"; - case EXCLUSIVE_LOCK: return "EXCLUSIVE"; + case NO_LOCK: return "NONE"; + case SHARED_LOCK: return "SHARED"; + case RESERVED_LOCK: return "RESERVED"; + case PENDING_LOCK: return "PENDING"; + case EXCLUSIVE_LOCK: return "EXCLUSIVE"; } return "ERROR"; } #endif @@ -736,15 +765,14 @@ */ struct unixOpenCnt { struct unixFileId fileId; /* The lookup key */ int nRef; /* Number of pointers to this structure */ int nLock; /* Number of outstanding locks */ - int nPending; /* Number of pending close() operations */ - int *aPending; /* Malloced space holding fd's awaiting a close() */ + UnixUnusedFd *pUnused; /* Unused file descriptors to close */ #if OS_VXWORKS sem_t *pSem; /* Named POSIX semaphore */ - char aSemName[MAX_PATHNAME+1]; /* Name of that semaphore */ + char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ #endif struct unixOpenCnt *pNext, *pPrev; /* List of all unixOpenCnt objects */ }; /* @@ -837,22 +865,27 @@ if( rc!=0 ) return; memset(&d, 0, sizeof(d)); d.fd = fd; d.lock = l; d.lock.l_type = F_WRLCK; - pthread_create(&t, 0, threadLockingTest, &d); - pthread_join(t, 0); + if( pthread_create(&t, 0, threadLockingTest, &d)==0 ){ + pthread_join(t, 0); + } close(fd); if( d.result!=0 ) return; threadsOverrideEachOthersLocks = (d.lock.l_type==F_UNLCK); } -#endif /* SQLITE_THERADSAFE && defined(__linux__) */ +#endif /* SQLITE_THREADSAFE && defined(__linux__) */ /* ** Release a unixLockInfo structure previously allocated by findLockInfo(). +** +** The mutex entered using the unixEnterMutex() function must be held +** when this function is called. */ static void releaseLockInfo(struct unixLockInfo *pLock){ + assert( unixMutexHeld() ); if( pLock ){ pLock->nRef--; if( pLock->nRef==0 ){ if( pLock->pPrev ){ assert( pLock->pPrev->pNext==pLock ); @@ -870,12 +903,16 @@ } } /* ** Release a unixOpenCnt structure previously allocated by findLockInfo(). +** +** The mutex entered using the unixEnterMutex() function must be held +** when this function is called. */ static void releaseOpenCnt(struct unixOpenCnt *pOpen){ + assert( unixMutexHeld() ); if( pOpen ){ pOpen->nRef--; if( pOpen->nRef==0 ){ if( pOpen->pPrev ){ assert( pOpen->pPrev->pNext==pOpen ); @@ -886,20 +923,33 @@ } if( pOpen->pNext ){ assert( pOpen->pNext->pPrev==pOpen ); pOpen->pNext->pPrev = pOpen->pPrev; } - sqlite3_free(pOpen->aPending); +#if SQLITE_THREADSAFE && defined(__linux__) + assert( !pOpen->pUnused || threadsOverrideEachOthersLocks==0 ); +#endif + + /* If pOpen->pUnused is not null, then memory and file-descriptors + ** are leaked. + ** + ** This will only happen if, under Linuxthreads, the user has opened + ** a transaction in one thread, then attempts to close the database + ** handle from another thread (without first unlocking the db file). + ** This is a misuse. */ sqlite3_free(pOpen); } } } /* ** Given a file descriptor, locate unixLockInfo and unixOpenCnt structures that ** describes that file descriptor. Create new ones if necessary. The ** return values might be uninitialized if an error occurs. +** +** The mutex entered using the unixEnterMutex() function must be held +** when this function is called. ** ** Return an appropriate error code. */ static int findLockInfo( unixFile *pFile, /* Unix file with file desc used in the key */ @@ -911,10 +961,12 @@ struct unixLockKey lockKey; /* Lookup key for the unixLockInfo structure */ struct unixFileId fileId; /* Lookup key for the unixOpenCnt struct */ struct stat statbuf; /* Low-level file information */ struct unixLockInfo *pLock = 0;/* Candidate unixLockInfo object */ struct unixOpenCnt *pOpen; /* Candidate unixOpenCnt object */ + + assert( unixMutexHeld() ); /* Get low-level information about the file that we can used to ** create a unique name for the file. */ fd = pFile->h; @@ -974,11 +1026,11 @@ pLock = sqlite3_malloc( sizeof(*pLock) ); if( pLock==0 ){ rc = SQLITE_NOMEM; goto exit_findlockinfo; } - pLock->lockKey = lockKey; + memcpy(&pLock->lockKey,&lockKey,sizeof(lockKey)); pLock->nRef = 1; pLock->cnt = 0; pLock->locktype = 0; pLock->pNext = lockList; pLock->pPrev = 0; @@ -999,23 +1051,16 @@ if( pOpen==0 ){ releaseLockInfo(pLock); rc = SQLITE_NOMEM; goto exit_findlockinfo; } + memset(pOpen, 0, sizeof(*pOpen)); pOpen->fileId = fileId; pOpen->nRef = 1; - pOpen->nLock = 0; - pOpen->nPending = 0; - pOpen->aPending = 0; pOpen->pNext = openList; - pOpen->pPrev = 0; if( openList ) openList->pPrev = pOpen; openList = pOpen; -#if OS_VXWORKS - pOpen->pSem = NULL; - pOpen->aSemName[0] = '\0'; -#endif }else{ pOpen->nRef++; } *ppOpen = pOpen; } @@ -1117,10 +1162,66 @@ OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); *pResOut = reserved; return rc; } + +/* +** Perform a file locking operation on a range of bytes in a file. +** The "op" parameter should be one of F_RDLCK, F_WRLCK, or F_UNLCK. +** Return 0 on success or -1 for failure. On failure, write the error +** code into *pErrcode. +** +** If the SQLITE_WHOLE_FILE_LOCKING bit is clear, then only lock +** the range of bytes on the locking page between SHARED_FIRST and +** SHARED_SIZE. If SQLITE_WHOLE_FILE_LOCKING is set, then lock all +** bytes from 0 up to but not including PENDING_BYTE, and all bytes +** that follow SHARED_FIRST. +** +** In other words, of SQLITE_WHOLE_FILE_LOCKING if false (the historical +** default case) then only lock a small range of bytes from SHARED_FIRST +** through SHARED_FIRST+SHARED_SIZE-1. But if SQLITE_WHOLE_FILE_LOCKING is +** true then lock every byte in the file except for PENDING_BYTE and +** RESERVED_BYTE. +** +** SQLITE_WHOLE_FILE_LOCKING=true overlaps SQLITE_WHOLE_FILE_LOCKING=false +** and so the locking schemes are compatible. One type of lock will +** effectively exclude the other type. The reason for using the +** SQLITE_WHOLE_FILE_LOCKING=true is that by indicating the full range +** of bytes to be read or written, we give hints to NFS to help it +** maintain cache coherency. On the other hand, whole file locking +** is slower, so we don't want to use it except for NFS. +*/ +static int rangeLock(unixFile *pFile, int op, int *pErrcode){ + struct flock lock; + int rc; + lock.l_type = op; + lock.l_start = SHARED_FIRST; + lock.l_whence = SEEK_SET; + if( (pFile->fileFlags & SQLITE_WHOLE_FILE_LOCKING)==0 ){ + lock.l_len = SHARED_SIZE; + rc = fcntl(pFile->h, F_SETLK, &lock); + *pErrcode = errno; + }else{ + lock.l_len = 0; + rc = fcntl(pFile->h, F_SETLK, &lock); + *pErrcode = errno; + if( NEVER(op==F_UNLCK) || rc!=(-1) ){ + lock.l_start = 0; + lock.l_len = PENDING_BYTE; + rc = fcntl(pFile->h, F_SETLK, &lock); + if( ALWAYS(op!=F_UNLCK) && rc==(-1) ){ + *pErrcode = errno; + lock.l_type = F_UNLCK; + lock.l_start = SHARED_FIRST; + lock.l_len = 0; + fcntl(pFile->h, F_SETLK, &lock); + } + } + } + return rc; +} /* ** Lock the file with the lock specified by parameter locktype - one ** of the following: ** @@ -1185,11 +1286,12 @@ */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; struct unixLockInfo *pLock = pFile->pLock; struct flock lock; - int s; + int s = 0; + int tErrno; assert( pFile ); OSTRACE7("LOCK %d %s was %s(%s,%d) pid=%d\n", pFile->h, locktypeName(locktype), locktypeName(pFile->locktype), locktypeName(pLock->locktype), pLock->cnt , getpid()); @@ -1202,11 +1304,14 @@ OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h, locktypeName(locktype)); return SQLITE_OK; } - /* Make sure the locking sequence is correct + /* Make sure the locking sequence is correct. + ** (1) We never move from unlocked to anything higher than shared lock. + ** (2) SQLite never explicitly requests a pendig lock. + ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); assert( locktype!=PENDING_LOCK ); assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); @@ -1246,26 +1351,25 @@ pLock->cnt++; pFile->pOpen->nLock++; goto end_lock; } - lock.l_len = 1L; - - lock.l_whence = SEEK_SET; /* A PENDING lock is needed before acquiring a SHARED lock and before ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will ** be released. */ + lock.l_len = 1L; + lock.l_whence = SEEK_SET; if( locktype==SHARED_LOCK || (locktype==EXCLUSIVE_LOCK && pFile->locktypeh, F_SETLK, &lock); if( s==(-1) ){ - int tErrno = errno; + tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_lock; @@ -1275,20 +1379,16 @@ /* If control gets to this point, then actually go ahead and make ** operating system calls for the specified lock. */ if( locktype==SHARED_LOCK ){ - int tErrno = 0; assert( pLock->cnt==0 ); assert( pLock->locktype==0 ); /* Now get the read-lock */ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ - tErrno = errno; - } + s = rangeLock(pFile, F_RDLCK, &tErrno); + /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; lock.l_type = F_UNLCK; if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){ @@ -1324,21 +1424,20 @@ assert( 0!=pFile->locktype ); lock.l_type = F_WRLCK; switch( locktype ){ case RESERVED_LOCK: lock.l_start = RESERVED_BYTE; + s = fcntl(pFile->h, F_SETLK, &lock); + tErrno = errno; break; case EXCLUSIVE_LOCK: - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; + s = rangeLock(pFile, F_WRLCK, &tErrno); break; default: assert(0); } - s = fcntl(pFile->h, F_SETLK, &lock); if( s==(-1) ){ - int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } } @@ -1374,24 +1473,68 @@ unixLeaveMutex(); OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype), rc==SQLITE_OK ? "ok" : "failed"); return rc; } + +/* +** Close all file descriptors accumuated in the unixOpenCnt->pUnused list. +** If all such file descriptors are closed without error, the list is +** cleared and SQLITE_OK returned. +** +** Otherwise, if an error occurs, then successfully closed file descriptor +** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. +** not deleted and SQLITE_IOERR_CLOSE returned. +*/ +static int closePendingFds(unixFile *pFile){ + int rc = SQLITE_OK; + struct unixOpenCnt *pOpen = pFile->pOpen; + UnixUnusedFd *pError = 0; + UnixUnusedFd *p; + UnixUnusedFd *pNext; + for(p=pOpen->pUnused; p; p=pNext){ + pNext = p->pNext; + if( close(p->fd) ){ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + p->pNext = pError; + pError = p; + }else{ + sqlite3_free(p); + } + } + pOpen->pUnused = pError; + return rc; +} + +/* +** Add the file descriptor used by file handle pFile to the corresponding +** pUnused list. +*/ +static void setPendingFd(unixFile *pFile){ + struct unixOpenCnt *pOpen = pFile->pOpen; + UnixUnusedFd *p = pFile->pUnused; + p->pNext = pOpen->pUnused; + pOpen->pUnused = p; + pFile->h = -1; + pFile->pUnused = 0; +} /* ** Lower the locking level on file descriptor pFile to locktype. locktype ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int locktype){ - struct unixLockInfo *pLock; - struct flock lock; - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - int h; + unixFile *pFile = (unixFile*)id; /* The open file */ + struct unixLockInfo *pLock; /* Structure describing current lock state */ + struct flock lock; /* Information passed into fcntl() */ + int rc = SQLITE_OK; /* Return code from this interface */ + int h; /* The underlying file descriptor */ + int tErrno; /* Error code from system call errors */ assert( pFile ); OSTRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype, pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid()); @@ -1427,16 +1570,11 @@ pFile->inNormalWrite = 0; #endif if( locktype==SHARED_LOCK ){ - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - int tErrno = errno; + if( rangeLock(pFile, F_RDLCK, &tErrno)==(-1) ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; @@ -1447,21 +1585,20 @@ lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pLock->locktype = SHARED_LOCK; }else{ - int tErrno = errno; + tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } 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 ** the lock. */ @@ -1474,11 +1611,11 @@ SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pLock->locktype = NO_LOCK; }else{ - int tErrno = errno; + tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } pLock->locktype = NO_LOCK; @@ -1491,32 +1628,15 @@ ** was deferred because of outstanding locks. */ pOpen = pFile->pOpen; pOpen->nLock--; assert( pOpen->nLock>=0 ); - if( pOpen->nLock==0 && pOpen->nPending>0 ){ - int i; - for(i=0; inPending; 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; + if( pOpen->nLock==0 ){ + int rc2 = closePendingFds(pFile); + if( rc==SQLITE_OK ){ + rc = rc2; + } } } end_unlock: unixLeaveMutex(); @@ -1562,10 +1682,11 @@ pFile->pId = 0; } #endif OSTRACE2("CLOSE %-3d\n", pFile->h); OpenCounter(-1); + sqlite3_free(pFile->pUnused); memset(pFile, 0, sizeof(unixFile)); } return SQLITE_OK; } @@ -1579,24 +1700,14 @@ unixUnlock(id, NO_LOCK); unixEnterMutex(); if( pFile->pOpen && pFile->pOpen->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file - ** descriptor to pOpen->aPending. It will be automatically closed when - ** the last lock is cleared. + ** descriptor to pOpen->pUnused list. It will be automatically closed + ** when the last lock is cleared. */ - int *aNew; - struct unixOpenCnt *pOpen = pFile->pOpen; - aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); - if( aNew==0 ){ - /* If a malloc fails, just leak the file descriptor */ - }else{ - pOpen->aPending = aNew; - pOpen->aPending[pOpen->nPending] = pFile->h; - pOpen->nPending++; - pFile->h = -1; - } + setPendingFd(pFile); } releaseLockInfo(pFile->pLock); releaseOpenCnt(pFile->pOpen); rc = closeUnixFile(id); unixLeaveMutex(); @@ -1649,11 +1760,11 @@ ******************************************************************************/ /****************************************************************************** ************************* Begin dot-file Locking ****************************** ** -** The dotfile locking implementation uses the existing of separate lock +** The dotfile locking implementation uses the existance of separate lock ** files in order to control access to the database. This works on just ** about every filesystem imaginable. But there are serious downsides: ** ** (1) There is zero concurrency. A single reader blocks all other ** connections from reading or writing the database. @@ -2429,11 +2540,12 @@ /* If control gets to this point, then actually go ahead and make ** operating system calls for the specified lock. */ if( locktype==SHARED_LOCK ){ - int lk, lrc1, lrc2, lrc1Errno; + int lk, lrc1, lrc2; + int lrc1Errno = 0; /* Now get the read-lock SHARED_LOCK */ /* note that the quality of the randomness doesn't matter that much */ lk = random(); context->sharedByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); @@ -2561,31 +2673,19 @@ if( rc==SQLITE_OK ){ if( locktype==NO_LOCK ){ struct unixOpenCnt *pOpen = pFile->pOpen; pOpen->nLock--; assert( pOpen->nLock>=0 ); - if( pOpen->nLock==0 && pOpen->nPending>0 ){ - int i; - for(i=0; inPending; i++){ - 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; - } + if( pOpen->nLock==0 ){ + rc = closePendingFds(pFile); } } } unixLeaveMutex(); - if( rc==SQLITE_OK ) pFile->locktype = locktype; + if( rc==SQLITE_OK ){ + pFile->locktype = locktype; + } return rc; } /* ** Close a file & cleanup AFP specific locking context @@ -2599,21 +2699,11 @@ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pOpen->aPending. It will be automatically closed when ** the last lock is cleared. */ - int *aNew; - struct unixOpenCnt *pOpen = pFile->pOpen; - aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); - if( aNew==0 ){ - /* If a malloc fails, just leak the file descriptor */ - }else{ - pOpen->aPending = aNew; - pOpen->aPending[pOpen->nPending] = pFile->h; - pOpen->nPending++; - pFile->h = -1; - } + setPendingFd(pFile); } releaseOpenCnt(pFile->pOpen); sqlite3_free(pFile->lockingContext); closeUnixFile(id); unixLeaveMutex(); @@ -2695,26 +2785,29 @@ sqlite3_file *id, void *pBuf, int amt, sqlite3_int64 offset ){ + unixFile *pFile = (unixFile *)id; int got; assert( id ); - /* Never read or write any of the bytes in the locking range */ - assert( ((unixFile*)id)->isLockable==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE ); + /* If this is a database file (not a journal, master-journal or temp + ** file), the bytes in the locking range should never be read or written. */ + assert( pFile->pUnused==0 + || offset>=PENDING_BYTE+512 + || offset+amt<=PENDING_BYTE + ); - got = seekAndRead((unixFile*)id, offset, pBuf, amt); + got = seekAndRead(pFile, offset, pBuf, amt); if( got==amt ){ return SQLITE_OK; }else if( got<0 ){ /* lastErrno set by seekAndRead */ return SQLITE_IOERR_READ; }else{ - ((unixFile*)id)->lastErrno = 0; /* not a system error */ + pFile->lastErrno = 0; /* not a system error */ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[got], 0, amt-got); return SQLITE_IOERR_SHORT_READ; } } @@ -2764,28 +2857,30 @@ sqlite3_file *id, const void *pBuf, int amt, sqlite3_int64 offset ){ + unixFile *pFile = (unixFile*)id; int wrote = 0; assert( id ); assert( amt>0 ); - /* Never read or write any of the bytes in the locking range */ - assert( ((unixFile*)id)->isLockable==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE ); + /* If this is a database file (not a journal, master-journal or temp + ** file), the bytes in the locking range should never be read or written. */ + assert( pFile->pUnused==0 + || offset>=PENDING_BYTE+512 + || offset+amt<=PENDING_BYTE + ); #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to ** doing a hot-journal rollback or a write to some file other than a ** normal database file) then record the fact that the database ** has changed. If the transaction counter is modified, record that ** fact too. */ - if( ((unixFile*)id)->inNormalWrite ){ - unixFile *pFile = (unixFile*)id; + if( pFile->inNormalWrite ){ pFile->dbUpdate = 1; /* The database has been modified */ if( offset<=24 && offset+amt>=27 ){ int rc; char oldCntr[4]; SimulateIOErrorBenign(1); @@ -2796,11 +2891,11 @@ } } } #endif - while( amt>0 && (wrote = seekAndWrite((unixFile*)id, offset, pBuf, amt))>0 ){ + while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ amt -= wrote; offset += wrote; pBuf = &((char*)pBuf)[wrote]; } SimulateIOError(( wrote=(-1), amt=1 )); @@ -2808,11 +2903,11 @@ if( amt>0 ){ if( wrote<0 ){ /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ - ((unixFile*)id)->lastErrno = 0; /* not a system error */ + pFile->lastErrno = 0; /* not a system error */ return SQLITE_FULL; } } return SQLITE_OK; } @@ -3016,10 +3111,23 @@ rc = ftruncate(((unixFile*)id)->h, (off_t)nByte); if( rc ){ ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_TRUNCATE; }else{ +#ifndef NDEBUG + /* If we are doing a normal write to a database file (as opposed to + ** doing a hot-journal rollback or a write to some file other than a + ** normal database file) and we truncate the file to zero length, + ** that effectively updates the change counter. This might happen + ** when restoring a database using the backup API from a zero-length + ** source. + */ + if( ((unixFile*)id)->inNormalWrite && nByte==0 ){ + ((unixFile*)id)->transCntrChng = 1; + } +#endif + return SQLITE_OK; } } /* @@ -3136,11 +3244,11 @@ ** ** For finder-funtion F, two objects are created: ** ** (1) The real finder-function named "FImpt()". ** -** (2) A constant pointer to this functio named just "F". +** (2) A constant pointer to this function named just "F". ** ** ** A pointer to the F pointer is used as the pAppData value for VFS ** objects. We have to do this instead of letting pAppData point ** directly at the finder-function since C90 rules prevent a void* @@ -3169,15 +3277,15 @@ CKLOCK, /* xCheckReservedLock */ \ unixFileControl, /* xFileControl */ \ unixSectorSize, /* xSectorSize */ \ unixDeviceCharacteristics /* xDeviceCapabilities */ \ }; \ -static const sqlite3_io_methods *FINDER##Impl(const char *z, int h){ \ - UNUSED_PARAMETER(z); UNUSED_PARAMETER(h); \ +static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ + UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ return &METHOD; \ } \ -static const sqlite3_io_methods *(*const FINDER)(const char*,int) \ +static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ = FINDER##Impl; /* ** Here are all of the sqlite3_io_methods objects for each of the ** locking strategies. Functions that return pointers to these methods @@ -3239,10 +3347,27 @@ afpUnlock, /* xUnlock method */ afpCheckReservedLock /* xCheckReservedLock method */ ) #endif +/* +** The "Whole File Locking" finder returns the same set of methods as +** the posix locking finder. But it also sets the SQLITE_WHOLE_FILE_LOCKING +** flag to force the posix advisory locks to cover the whole file instead +** of just a small span of bytes near the 1GiB boundary. Whole File Locking +** is useful on NFS-mounted files since it helps NFS to maintain cache +** coherency. But it is a detriment to other filesystems since it runs +** slower. +*/ +static const sqlite3_io_methods *posixWflIoFinderImpl(const char*z, unixFile*p){ + UNUSED_PARAMETER(z); + p->fileFlags = SQLITE_WHOLE_FILE_LOCKING; + return &posixIoMethods; +} +static const sqlite3_io_methods + *(*const posixWflIoFinder)(const char*,unixFile *p) = posixWflIoFinderImpl; + /* ** The proxy locking method is a "super-method" in the sense that it ** opens secondary file descriptors for the conch and lock files and ** it uses proxy, dot-file, AFP, and flock() locking methods on those ** secondary files. For this reason, the division that implements @@ -3274,11 +3399,11 @@ ** ** This is for MacOSX only. */ static const sqlite3_io_methods *autolockIoFinderImpl( const char *filePath, /* name of the database file */ - int fd /* file descriptor open on the database file */ + unixFile *pNew /* open file object for the database file */ ){ static const struct Mapping { const char *zFilesystem; /* Filesystem type name */ const sqlite3_io_methods *pMethods; /* Appropriate locking method */ } aMap[] = { @@ -3319,18 +3444,19 @@ */ 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 ) { + if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + pNew->fileFlags = SQLITE_WHOLE_FILE_LOCKING; return &posixIoMethods; }else{ return &dotlockIoMethods; } } -static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int) - = autolockIoFinderImpl; +static const sqlite3_io_methods + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ #if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE /* @@ -3340,11 +3466,11 @@ ** ** 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 */ + unixFile *pNew /* the open file object */ ){ struct flock lockInfo; if( !filePath ){ /* If filePath==NULL that means we are dealing with a transient file @@ -3357,25 +3483,25 @@ */ 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 ) { + if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { return &posixIoMethods; }else{ return &semIoMethods; } } -static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int) - = autolockIoFinderImpl; +static const sqlite3_io_methods + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; #endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ /* ** An abstract type for a pointer to a IO method finder function: */ -typedef const sqlite3_io_methods *(*finder_type)(const char*,int); +typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); /**************************************************************************** **************************** sqlite3_vfs methods **************************** ** @@ -3400,22 +3526,20 @@ int rc = SQLITE_OK; assert( pNew->pLock==NULL ); assert( pNew->pOpen==NULL ); - /* Parameter isDelete is only used on vxworks. - ** Express this explicitly here to prevent compiler warnings - ** about unused parameters. + /* Parameter isDelete is only used on vxworks. Express this explicitly + ** here to prevent compiler warnings about unused parameters. */ -#if !OS_VXWORKS UNUSED_PARAMETER(isDelete); -#endif OSTRACE3("OPEN %-3d %s\n", h, zFilename); pNew->h = h; pNew->dirfd = dirfd; SET_THREADID(pNew); + pNew->fileFlags = 0; #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ noLock = 1; @@ -3424,11 +3548,11 @@ #endif if( noLock ){ pLockingStyle = &nolockIoMethods; }else{ - pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, h); + pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); #if SQLITE_ENABLE_LOCKING_STYLE /* Cache zFilename in the locking context (AFP and dotlock override) for ** proxyLock activation is possible (remote proxy is based on db name) ** zFilename remains valid until file is closed, to support */ pNew->lockingContext = (void*)zFilename; @@ -3436,10 +3560,32 @@ } if( pLockingStyle == &posixIoMethods ){ unixEnterMutex(); rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if( rc!=SQLITE_OK ){ + /* If an error occured in findLockInfo(), close the file descriptor + ** immediately, before releasing the mutex. findLockInfo() may fail + ** in two scenarios: + ** + ** (a) A call to fstat() failed. + ** (b) A malloc failed. + ** + ** Scenario (b) may only occur if the process is holding no other + ** file descriptors open on the same file. If there were other file + ** descriptors on this file, then no malloc would be required by + ** findLockInfo(). If this is the case, it is quite safe to close + ** handle h - as it is guaranteed that no posix locks will be released + ** by doing so. + ** + ** If scenario (a) caused the error then things are not so safe. The + ** implicit assumption here is that if fstat() fails, things are in + ** such bad shape that dropping a lock or two doesn't matter much. + */ + close(h); + h = -1; + } unixLeaveMutex(); } #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) else if( pLockingStyle == &afpIoMethods ){ @@ -3487,13 +3633,13 @@ unixEnterMutex(); rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); if( (rc==SQLITE_OK) && (pNew->pOpen->pSem==NULL) ){ char *zSemName = pNew->pOpen->aSemName; int n; - sqlite3_snprintf(MAX_PATHNAME, zSemName, "%s.sem", + sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", pNew->pId->zCanonicalName); - for( n=0; zSemName[n]; n++ ) + for( n=1; zSemName[n]; n++ ) if( zSemName[n]=='/' ) zSemName[n] = '_'; pNew->pOpen->pSem = sem_open(zSemName, O_CREAT, 0666, 1); if( pNew->pOpen->pSem == SEM_FAILED ){ rc = SQLITE_NOMEM; pNew->pOpen->aSemName[0] = '\0'; @@ -3511,11 +3657,11 @@ } pNew->isDelete = isDelete; #endif if( rc!=SQLITE_OK ){ if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ - close(h); + if( h>=0 ) close(h); }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); } return rc; @@ -3620,10 +3766,66 @@ ** if SQLITE_PREFER_PROXY_LOCKING is defined. */ static int proxyTransformUnixFile(unixFile*, const char*); #endif +/* +** Search for an unused file descriptor that was opened on the database +** file (not a journal or master-journal file) identified by pathname +** zPath with SQLITE_OPEN_XXX flags matching those passed as the second +** argument to this function. +** +** Such a file descriptor may exist if a database connection was closed +** but the associated file descriptor could not be closed because some +** other file descriptor open on the same file is holding a file-lock. +** Refer to comments in the unixClose() function and the lengthy comment +** describing "Posix Advisory Locking" at the start of this file for +** further details. Also, ticket #4018. +** +** If a suitable file descriptor is found, then it is returned. If no +** such file descriptor is located, -1 is returned. +*/ +static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ + UnixUnusedFd *pUnused = 0; + + /* Do not search for an unused file descriptor on vxworks. Not because + ** vxworks would not benefit from the change (it might, we're not sure), + ** but because no way to test it is currently available. It is better + ** not to risk breaking vxworks support for the sake of such an obscure + ** feature. */ +#if !OS_VXWORKS + struct stat sStat; /* Results of stat() call */ + + /* A stat() call may fail for various reasons. If this happens, it is + ** almost certain that an open() call on the same path will also fail. + ** For this reason, if an error occurs in the stat() call here, it is + ** ignored and -1 is returned. The caller will try to open a new file + ** descriptor on the same path, fail, and return an error to SQLite. + ** + ** Even if a subsequent open() call does succeed, the consequences of + ** not searching for a resusable file descriptor are not dire. */ + if( 0==stat(zPath, &sStat) ){ + struct unixOpenCnt *pO; + struct unixFileId id; + id.dev = sStat.st_dev; + id.ino = sStat.st_ino; + + unixEnterMutex(); + for(pO=openList; pO && memcmp(&id, &pO->fileId, sizeof(id)); pO=pO->pNext); + if( pO ){ + UnixUnusedFd **pp; + for(pp=&pO->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); + pUnused = *pp; + if( pUnused ){ + *pp = pUnused->pNext; + } + } + unixLeaveMutex(); + } +#endif /* if !OS_VXWORKS */ + return pUnused; +} /* ** Open the file zPath. ** ** Previously, the SQLite OS layer used three functions in place of this @@ -3650,16 +3852,17 @@ const char *zPath, /* Pathname of file to be opened */ sqlite3_file *pFile, /* The file descriptor to be filled in */ int flags, /* Input flags to control the opening */ int *pOutFlags /* Output flags returned to SQLite core */ ){ - int fd = -1; /* File descriptor returned by open() */ + unixFile *p = (unixFile *)pFile; + 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 */ int noLock; /* True to omit locking primitives */ - int rc = SQLITE_OK; + int rc = SQLITE_OK; /* Function Return Code */ int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); int isReadonly = (flags & SQLITE_OPEN_READONLY); @@ -3690,79 +3893,106 @@ assert(isCreate==0 || isReadWrite); assert(isExclusive==0 || isCreate); assert(isDelete==0 || isCreate); /* The main DB, main journal, and master journal are never automatically - ** deleted - */ - assert( eType!=SQLITE_OPEN_MAIN_DB || !isDelete ); - assert( eType!=SQLITE_OPEN_MAIN_JOURNAL || !isDelete ); - assert( eType!=SQLITE_OPEN_MASTER_JOURNAL || !isDelete ); + ** deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); /* Assert that the upper layer has set one of the "file-type" flags. */ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_TRANSIENT_DB ); - memset(pFile, 0, sizeof(unixFile)); + memset(p, 0, sizeof(unixFile)); - if( !zName ){ + if( eType==SQLITE_OPEN_MAIN_DB ){ + UnixUnusedFd *pUnused; + pUnused = findReusableFd(zName, flags); + if( pUnused ){ + fd = pUnused->fd; + }else{ + pUnused = sqlite3_malloc(sizeof(*pUnused)); + if( !pUnused ){ + return SQLITE_NOMEM; + } + } + p->pUnused = pUnused; + }else if( !zName ){ + /* If zName is NULL, the upper layer is requesting a temp file. */ assert(isDelete && !isOpenDirectory); rc = getTempname(MAX_PATHNAME+1, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zName = zTmpname; } + /* Determine the value of the flags parameter passed to POSIX function + ** open(). These must be calculated even if open() is not called, as + ** they may be stored as part of the file handle and used by the + ** 'conch file' locking functions later on. */ if( isReadonly ) openFlags |= O_RDONLY; if( isReadWrite ) openFlags |= O_RDWR; if( isCreate ) openFlags |= O_CREAT; if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); openFlags |= (O_LARGEFILE|O_BINARY); - fd = open(zName, openFlags, isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS); - OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags); - if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ - /* Failed to open the file for read/write access. Try read-only. */ - flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); - flags |= SQLITE_OPEN_READONLY; - return unixOpen(pVfs, zPath, pFile, flags, pOutFlags); - } if( fd<0 ){ - return SQLITE_CANTOPEN; + mode_t openMode = (isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = open(zName, openFlags, openMode); + OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags); + if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ + /* Failed to open the file for read/write access. Try read-only. */ + flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); + openFlags &= ~(O_RDWR|O_CREAT); + flags |= SQLITE_OPEN_READONLY; + openFlags |= O_RDONLY; + fd = open(zName, openFlags, openMode); + } + if( fd<0 ){ + rc = SQLITE_CANTOPEN; + goto open_finished; + } + } + assert( fd>=0 ); + if( pOutFlags ){ + *pOutFlags = flags; + } + + if( p->pUnused ){ + p->pUnused->fd = fd; + p->pUnused->flags = flags; } + if( isDelete ){ #if OS_VXWORKS zPath = zName; #else unlink(zName); #endif } #if SQLITE_ENABLE_LOCKING_STYLE else{ - ((unixFile*)pFile)->openFlags = openFlags; - } -#endif - if( pOutFlags ){ - *pOutFlags = flags; - } - -#ifndef NDEBUG - if( (flags & SQLITE_OPEN_MAIN_DB)!=0 ){ - ((unixFile*)pFile)->isLockable = 1; + p->openFlags = openFlags; } #endif - assert( fd>=0 ); if( isOpenDirectory ){ rc = openDirectory(zPath, &dirfd); if( rc!=SQLITE_OK ){ - close(fd); /* silently leak if fail, already in error */ - return rc; + /* It is safe to close fd at this point, because it is guaranteed not + ** to be open on a database file. If it were open on a database file, + ** it would not be safe to close as this would release any locks held + ** on the file by this process. */ + assert( eType!=SQLITE_OPEN_MAIN_DB ); + close(fd); /* silently leak if fail, already in error */ + goto open_finished; } } #ifdef FD_CLOEXEC fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); @@ -3769,42 +3999,56 @@ #endif noLock = eType!=SQLITE_OPEN_MAIN_DB; #if SQLITE_PREFER_PROXY_LOCKING - if( zPath!=NULL && !noLock ){ + if( zPath!=NULL && !noLock && pVfs->xOpen ){ char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); int useProxy = 0; - /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, - ** 0 means never use proxy, NULL means use proxy for non-local files only - */ + /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means + ** never use proxy, NULL means use proxy for non-local files only. */ if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ struct statfs fsInfo; - if( statfs(zPath, &fsInfo) == -1 ){ - ((unixFile*)pFile)->lastErrno = errno; - if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ + /* In theory, the close(fd) call is sub-optimal. If the file opened + ** with fd is a database file, and there are other connections open + ** on that file that are currently holding advisory locks on it, + ** then the call to close() will cancel those locks. In practice, + ** we're assuming that statfs() doesn't fail very often. At least + ** not while other file descriptors opened by the same process on + ** the same file are working. */ + p->lastErrno = errno; + if( dirfd>=0 ){ + close(dirfd); /* silently leak if fail, in error */ + } close(fd); /* silently leak if fail, in error */ - return SQLITE_IOERR_ACCESS; + rc = SQLITE_IOERR_ACCESS; + goto open_finished; } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); if( rc==SQLITE_OK ){ rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); } - return rc; + goto open_finished; } } #endif - return fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); +open_finished: + if( rc!=SQLITE_OK ){ + sqlite3_free(p->pUnused); + } + return rc; } + /* ** Delete the file at zPath. If the dirSync argument is true, fsync() ** the directory after deleting the file. */ @@ -4470,37 +4714,47 @@ ** ** The caller is responsible not only for closing the file descriptor ** but also for freeing the memory associated with the file descriptor. */ static int proxyCreateUnixFile(const char *path, unixFile **ppFile) { - int fd; - int dirfd = -1; unixFile *pNew; + int flags = SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; int rc = SQLITE_OK; sqlite3_vfs dummyVfs; - fd = open(path, O_RDWR | O_CREAT, SQLITE_DEFAULT_FILE_PERMISSIONS); - if( fd<0 ){ - return SQLITE_CANTOPEN; - } - pNew = (unixFile *)sqlite3_malloc(sizeof(unixFile)); - if( pNew==NULL ){ - rc = SQLITE_NOMEM; - goto end_create_proxy; + if( !pNew ){ + return SQLITE_NOMEM; } memset(pNew, 0, sizeof(unixFile)); + /* Call unixOpen() to open the proxy file. The flags passed to unixOpen() + ** suggest that the file being opened is a "main database". This is + ** necessary as other file types do not necessarily support locking. It + ** is better to use unixOpen() instead of opening the file directly with + ** open(), as unixOpen() sets up the various mechanisms required to + ** make sure a call to close() does not cause the system to discard + ** POSIX locks prematurely. + ** + ** It is important that the xOpen member of the VFS object passed to + ** unixOpen() is NULL. This tells unixOpen() may try to open a proxy-file + ** for the proxy-file (creating a potential infinite loop). + */ dummyVfs.pAppData = (void*)&autolockIoFinder; - rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0); - if( rc==SQLITE_OK ){ - *ppFile = pNew; - return SQLITE_OK; - } -end_create_proxy: - close(fd); /* silently leak fd if error, we're already in error */ - sqlite3_free(pNew); + dummyVfs.xOpen = 0; + rc = unixOpen(&dummyVfs, path, (sqlite3_file *)pNew, flags, &flags); + if( rc==SQLITE_OK && (flags&SQLITE_OPEN_READONLY) ){ + pNew->pMethod->xClose((sqlite3_file *)pNew); + rc = SQLITE_CANTOPEN; + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(pNew); + pNew = 0; + } + + *ppFile = pNew; return rc; } /* takes the conch by taking a shared lock and read the contents conch, if ** lockPath is non-NULL, the host ID and lock file path must match. A NULL @@ -5109,10 +5363,11 @@ #else UNIXVFS("unix", posixIoFinder ), #endif UNIXVFS("unix-none", nolockIoFinder ), UNIXVFS("unix-dotfile", dotlockIoFinder ), + UNIXVFS("unix-wfl", posixWflIoFinder ), #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif #if SQLITE_ENABLE_LOCKING_STYLE UNIXVFS("unix-posix", posixIoFinder ), Index: src/os_win.c ================================================================== --- src/os_win.c +++ src/os_win.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This file contains code that is specific to windows. -** -** $Id: os_win.c,v 1.156 2009/04/23 19:08:33 shane Exp $ */ #include "sqliteInt.h" #if SQLITE_OS_WIN /* This file is used for windows only */ @@ -73,11 +71,11 @@ ** Determine if we are dealing with WindowsCE - which has a much ** reduced API. */ #if SQLITE_OS_WINCE # define AreFileApisANSI() 1 -# define GetDiskFreeSpaceW() 0 +# define FormatMessageW(a,b,c,d,e,f,g) 0 #endif /* ** WinCE lacks native support for file locking so we have to fake it ** with some code of our own. @@ -307,12 +305,12 @@ FILETIME uTm, lTm; SYSTEMTIME pTm; sqlite3_int64 t64; t64 = *t; t64 = (t64 + 11644473600)*10000000; - uTm.dwLowDateTime = t64 & 0xFFFFFFFF; - uTm.dwHighDateTime= t64 >> 32; + uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); + uTm.dwHighDateTime= (DWORD)(t64 >> 32); FileTimeToLocalFileTime(&uTm,&lTm); FileTimeToSystemTime(&lTm,&pTm); y.tm_year = pTm.wYear - 1900; y.tm_mon = pTm.wMonth - 1; y.tm_wday = pTm.wDayOfWeek; @@ -328,11 +326,11 @@ #define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e) #define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e) #define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f) -#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-offsetof(winFile,h)] +#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] /* ** Acquire a lock on the handle h */ static void winceMutexAcquire(HANDLE h){ @@ -467,27 +465,29 @@ DWORD nNumberOfBytesToLockHigh ){ winFile *pFile = HANDLE_TO_WINFILE(phFile); BOOL bReturn = FALSE; + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + if (!pFile->hMutex) return TRUE; winceMutexAcquire(pFile->hMutex); /* Wanting an exclusive lock? */ - if (dwFileOffsetLow == SHARED_FIRST - && nNumberOfBytesToLockLow == SHARED_SIZE){ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST + && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ pFile->shared->bExclusive = TRUE; pFile->local.bExclusive = TRUE; bReturn = TRUE; } } /* Want a read-only lock? */ - else if ((dwFileOffsetLow >= SHARED_FIRST && - dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE) && - nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && + nNumberOfBytesToLockLow == 1){ if (pFile->shared->bExclusive == 0){ pFile->local.nReaders ++; if (pFile->local.nReaders == 1){ pFile->shared->nReaders ++; } @@ -494,20 +494,21 @@ bReturn = TRUE; } } /* Want a pending lock? */ - else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToLockLow == 1){ /* If no pending lock has been acquired, then acquire it */ if (pFile->shared->bPending == 0) { pFile->shared->bPending = TRUE; pFile->local.bPending = TRUE; bReturn = TRUE; } } + /* Want a reserved lock? */ - else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ if (pFile->shared->bReserved == 0) { pFile->shared->bReserved = TRUE; pFile->local.bReserved = TRUE; bReturn = TRUE; } @@ -528,25 +529,29 @@ DWORD nNumberOfBytesToUnlockHigh ){ winFile *pFile = HANDLE_TO_WINFILE(phFile); BOOL bReturn = FALSE; + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); + if (!pFile->hMutex) return TRUE; winceMutexAcquire(pFile->hMutex); /* Releasing a reader lock or an exclusive lock */ - if (dwFileOffsetLow >= SHARED_FIRST && - dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE){ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ /* Did we have an exclusive lock? */ if (pFile->local.bExclusive){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); pFile->local.bExclusive = FALSE; pFile->shared->bExclusive = FALSE; bReturn = TRUE; } /* Did we just have a reader lock? */ else if (pFile->local.nReaders){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE || nNumberOfBytesToUnlockLow == 1); pFile->local.nReaders --; if (pFile->local.nReaders == 0) { pFile->shared->nReaders --; } @@ -553,19 +558,19 @@ bReturn = TRUE; } } /* Releasing a pending lock */ - else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ if (pFile->local.bPending){ pFile->local.bPending = FALSE; pFile->shared->bPending = FALSE; bReturn = TRUE; } } /* Releasing a reserved lock */ - else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ if (pFile->local.bReserved) { pFile->local.bReserved = FALSE; pFile->shared->bReserved = FALSE; bReturn = TRUE; } @@ -584,15 +589,18 @@ DWORD dwReserved, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped ){ + UNUSED_PARAMETER(dwReserved); + UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + /* If the caller wants a shared read lock, forward this call ** to winceLockFile */ - if (lpOverlapped->Offset == SHARED_FIRST && + if (lpOverlapped->Offset == (DWORD)SHARED_FIRST && dwFlags == 1 && - nNumberOfBytesToLockLow == SHARED_SIZE){ + nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0); } return FALSE; } /* @@ -1238,31 +1246,63 @@ ** The return value of getLastErrorMsg ** is zero if the error message fits in the buffer, or non-zero ** otherwise (if the message was truncated). */ static int getLastErrorMsg(int nBuf, char *zBuf){ - DWORD error = GetLastError(); - -#if SQLITE_OS_WINCE - sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); -#else /* FormatMessage returns 0 on failure. Otherwise it ** returns the number of TCHARs written to the output ** buffer, excluding the terminating null char. */ - if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - error, - 0, - zBuf, - nBuf-1, - 0)) - { + DWORD error = GetLastError(); + DWORD dwLen = 0; + char *zOut = 0; + + if( isNT() ){ + WCHAR *zTempWide = NULL; + dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPWSTR) &zTempWide, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = unicodeToUtf8(zTempWide); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTempWide); + } +/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. +** Since the ASCII version of these Windows API do not exist for WINCE, +** it's important to not reference them for WINCE builds. +*/ +#if SQLITE_OS_WINCE==0 + }else{ + char *zTemp = NULL; + dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPSTR) &zTemp, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = sqlite3_win32_mbcs_to_utf8(zTemp); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTemp); + } +#endif + } + if( 0 == dwLen ){ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); + }else{ + /* copy a maximum of nBuf chars to output buffer */ + sqlite3_snprintf(nBuf, zBuf, "%s", zOut); + /* free the UTF8 buffer */ + free(zOut); } -#endif - return 0; } /* ** Open a file. @@ -1590,13 +1630,19 @@ static int getSectorSize( sqlite3_vfs *pVfs, const char *zRelative /* UTF-8 file name */ ){ DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; + /* GetDiskFreeSpace is not supported under WINCE */ +#if SQLITE_OS_WINCE + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(zRelative); +#else char zFullpath[MAX_PATH+1]; int rc; - DWORD dwRet = 0, dwDummy; + DWORD dwRet = 0; + DWORD dwDummy; /* ** We need to get the full path name of the file ** to get the drive letter to look up the sector ** size. @@ -1618,33 +1664,32 @@ dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted, &dwDummy, &bytesPerSector, &dwDummy, &dwDummy); -#if SQLITE_OS_WINCE==0 }else{ /* trim path to just drive reference */ - CHAR *p = (CHAR *)zConverted; + char *p = (char *)zConverted; for(;*p;p++){ if( *p == '\\' ){ *p = '\0'; break; } } - dwRet = GetDiskFreeSpaceA((CHAR*)zConverted, + dwRet = GetDiskFreeSpaceA((char*)zConverted, &dwDummy, &bytesPerSector, &dwDummy, &dwDummy); -#endif } free(zConverted); } if( !dwRet ){ bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; } } +#endif return (int) bytesPerSector; } #ifndef SQLITE_OMIT_LOAD_EXTENSION /* @@ -1867,13 +1912,14 @@ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError /* xGetLastError */ }; + sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } int sqlite3_os_end(void){ return SQLITE_OK; } #endif /* SQLITE_OS_WIN */ Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -15,12 +15,10 @@ ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. -** -** @(#) $Id: pager.c,v 1.603 2009/06/26 12:15:23 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" /* @@ -112,16 +110,16 @@ # define CODEC1(P,D,N,X,E) /* NO-OP */ # define CODEC2(P,D,N,X,E,O) O=(char*)D #endif /* -** The maximum allowed sector size. 16MB. If the xSectorsize() method +** The maximum allowed sector size. 64KiB. If the xSectorsize() method ** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. ** This could conceivably cause corruption following a power failure on ** such a system. This is currently an undocumented limit. */ -#define MAX_SECTOR_SIZE 0x0100000 +#define MAX_SECTOR_SIZE 0x10000 /* ** An instance of the following structure is allocated for each active ** savepoint and statement transaction in the system. All such structures ** are stored in the Pager.aSavepoint[] array, which is allocated and @@ -783,12 +781,11 @@ || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) ){ memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); }else{ - zHeader[0] = '\0'; - put32bits(&zHeader[sizeof(aJournalMagic)], 0); + memset(zHeader, 0, sizeof(aJournalMagic)+4); } /* The random check-hash initialiser */ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); @@ -1125,11 +1122,11 @@ releaseAllSavepoints(pPager); /* If the file is unlocked, somebody else might change it. The ** values stored in Pager.dbSize etc. might become invalid if ** this happens. TODO: Really, this doesn't need to be cleared - ** until the change-counter check fails in pagerSharedLock(). + ** until the change-counter check fails in PagerSharedLock(). */ pPager->dbSizeValid = 0; rc = osUnlock(pPager->fd, NO_LOCK); if( rc ){ @@ -1172,30 +1169,18 @@ ** to be replayed to restore the contents of the database file (as if ** it were a hot-journal). */ static int pager_error(Pager *pPager, int rc){ int rc2 = rc & 0xff; + assert( rc==SQLITE_OK || !MEMDB ); assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK || (pPager->errCode & 0xff)==SQLITE_IOERR ); - if( - rc2==SQLITE_FULL || - rc2==SQLITE_IOERR || - rc2==SQLITE_CORRUPT - ){ + if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ pPager->errCode = rc; - if( pPager->state==PAGER_UNLOCK - && sqlite3PcacheRefCount(pPager->pPCache)==0 - ){ - /* If the pager is already unlocked, call pager_unlock() now to - ** clear the error state and ensure that the pager-cache is - ** completely empty. - */ - pager_unlock(pPager); - } } return rc; } /* @@ -1290,25 +1275,14 @@ releaseAllSavepoints(pPager); assert( isOpen(pPager->jfd) || pPager->pInJournal==0 ); if( isOpen(pPager->jfd) ){ - /* TODO: There's a problem here if a journal-file was opened in MEMORY - ** mode and then the journal-mode is changed to TRUNCATE or PERSIST - ** during the transaction. This code should be changed to assume - ** that the journal mode has not changed since the transaction was - ** started. And the sqlite3PagerJournalMode() function should be - ** changed to make sure that this is the case too. - */ - /* Finalize the journal file. */ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ - int isMemoryJournal = sqlite3IsMemJournal(pPager->jfd); + if( sqlite3IsMemJournal(pPager->jfd) ){ + assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); sqlite3OsClose(pPager->jfd); - if( !isMemoryJournal ){ - rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); - } }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ if( pPager->journalOff==0 ){ rc = SQLITE_OK; }else{ rc = sqlite3OsTruncate(pPager->jfd, 0); @@ -1321,13 +1295,19 @@ rc = zeroJournalHdr(pPager, hasMaster); pager_error(pPager, rc); pPager->journalOff = 0; pPager->journalStarted = 0; }else{ - assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || rc ); + /* This branch may be executed with Pager.journalMode==MEMORY if + ** a hot-journal was just rolled back. In this case the journal + ** file should be closed and deleted. If this connection writes to + ** the database file, it will do so using an in-memory journal. */ + assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE + || pPager->journalMode==PAGER_JOURNALMODE_MEMORY + ); sqlite3OsClose(pPager->jfd); - if( rc==SQLITE_OK && !pPager->tempFile ){ + if( !pPager->tempFile ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } } #ifdef SQLITE_CHECK_PAGES @@ -1573,13 +1553,11 @@ ** sqlite3PagerRollback(). */ void *pData; pData = pPg->pData; memcpy(pData, aData, pPager->pageSize); - if( pPager->xReiniter ){ - pPager->xReiniter(pPg); - } + pPager->xReiniter(pPg); if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ /* If the contents of this page were just restored from the main ** journal file, then its content must be as they were when the ** transaction was first opened. In this case we can mark the page ** as clean, since there will be no need to write it out to the. @@ -1613,50 +1591,10 @@ sqlite3PcacheRelease(pPg); } return rc; } -#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) -/* -** This routine looks ahead into the main journal file and determines -** whether or not the next record (the record that begins at file -** offset pPager->journalOff) is a well-formed page record consisting -** of a valid page number, pPage->pageSize bytes of content, followed -** by a valid checksum. -** -** The pager never needs to know this in order to do its job. This -** routine is only used from with assert() and testcase() macros. -*/ -static int pagerNextJournalPageIsValid(Pager *pPager){ - Pgno pgno; /* The page number of the page */ - u32 cksum; /* The page checksum */ - int rc; /* Return code from read operations */ - sqlite3_file *fd; /* The file descriptor from which we are reading */ - u8 *aData; /* Content of the page */ - - /* Read the page number header */ - fd = pPager->jfd; - rc = read32bits(fd, pPager->journalOff, &pgno); - if( rc!=SQLITE_OK ){ return 0; } /*NO_TEST*/ - if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ return 0; } /*NO_TEST*/ - if( pgno>(Pgno)pPager->dbSize ){ return 0; } /*NO_TEST*/ - - /* Read the checksum */ - rc = read32bits(fd, pPager->journalOff+pPager->pageSize+4, &cksum); - if( rc!=SQLITE_OK ){ return 0; } /*NO_TEST*/ - - /* Read the data and verify the checksum */ - aData = (u8*)pPager->pTmpSpace; - rc = sqlite3OsRead(fd, aData, pPager->pageSize, pPager->journalOff+4); - if( rc!=SQLITE_OK ){ return 0; } /*NO_TEST*/ - if( pager_cksum(pPager, aData)!=cksum ){ return 0; } /*NO_TEST*/ - - /* Reach this point only if the page is valid */ - return 1; -} -#endif /* !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) */ - /* ** Parameter zMaster is the name of a master journal file. A single journal ** file that referred to the master journal file has just been rolled back. ** This routine checks if it is possible to delete the master journal file, ** and does so if it is. @@ -1728,18 +1666,19 @@ int nMasterPtr = pVfs->mxPathname+1; /* Load the entire master journal file into space obtained from ** sqlite3_malloc() and pointed to by zMasterJournal. */ - zMasterJournal = (char *)sqlite3Malloc((int)nMasterJournal + nMasterPtr); + zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1); if( !zMasterJournal ){ rc = SQLITE_NOMEM; goto delmaster_out; } - zMasterPtr = &zMasterJournal[nMasterJournal]; + zMasterPtr = &zMasterJournal[nMasterJournal+1]; rc = sqlite3OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0); if( rc!=SQLITE_OK ) goto delmaster_out; + zMasterJournal[nMasterJournal] = 0; zJournal = zMasterJournal; while( (zJournal-zMasterJournal)pageSize bytes of data. ** + 4 byte checksum ** -** When we speak of the journal header, we mean the first 8 items above. -** Each entry in the journal is an instance of the 9th item. +** When we speak of the journal header, we mean the first 7 items above. +** Each entry in the journal is an instance of the 8th item. ** ** Call the value from the second bullet "nRec". nRec is the number of ** valid page entries in the journal. In most cases, you can compute the ** value of nRec from the size of the journal file. But if a power ** failure occurred while the journal was being written, it could be the @@ -2011,15 +1944,10 @@ ** when doing a ROLLBACK and the nRec==0 chunk is the last chunk in ** the journal, it means that the journal might contain additional ** pages that need to be rolled back and that the number of pages ** should be computed based on the journal file size. */ - testcase( nRec==0 && !isHot - && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)!=pPager->journalOff - && ((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager))>0 - && pagerNextJournalPageIsValid(pPager) - ); if( nRec==0 && !isHot && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); isUnsync = 1; } @@ -2207,15 +2135,10 @@ /* ** The "pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff" ** test is related to ticket #2565. See the discussion in the ** pager_playback() function for additional information. */ - assert( !(nJRec==0 - && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)!=pPager->journalOff - && ((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager))>0 - && pagerNextJournalPageIsValid(pPager)) - ); if( nJRec==0 && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager)); } @@ -2359,21 +2282,10 @@ ){ pPager->xBusyHandler = xBusyHandler; pPager->pBusyHandlerArg = pBusyHandlerArg; } -/* -** Set the reinitializer for this pager. If not NULL, the reinitializer -** is called when the content of a page in cache is modified (restored) -** as part of a transaction or savepoint rollback. The callback gives -** higher-level code an opportunity to restore the EXTRA section to -** agree with the restored page data. -*/ -void sqlite3PagerSetReiniter(Pager *pPager, void (*xReinit)(DbPage*)){ - pPager->xReiniter = xReinit; -} - /* ** Report the current page size and number of reserved bytes back ** to the codec. */ #ifdef SQLITE_HAS_CODEC @@ -2417,16 +2329,17 @@ ** function was called, or because the memory allocation attempt failed, ** then *pPageSize is set to the old, retained page size before returning. */ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){ int rc = pPager->errCode; + if( rc==SQLITE_OK ){ u16 pageSize = *pPageSize; assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); - if( pageSize && pageSize!=pPager->pageSize - && (pPager->memDb==0 || pPager->dbSize==0) + if( (pPager->memDb==0 || pPager->dbSize==0) && sqlite3PcacheRefCount(pPager->pPCache)==0 + && pageSize && pageSize!=pPager->pageSize ){ char *pNew = (char *)sqlite3PageMalloc(pageSize); if( !pNew ){ rc = SQLITE_NOMEM; }else{ @@ -2635,10 +2548,44 @@ } } return rc; } +/* +** Function assertTruncateConstraint(pPager) checks that one of the +** following is true for all dirty pages currently in the page-cache: +** +** a) The page number is less than or equal to the size of the +** current database image, in pages, OR +** +** b) if the page content were written at this time, it would not +** be necessary to write the current content out to the sub-journal +** (as determined by function subjRequiresPage()). +** +** If the condition asserted by this function were not true, and the +** dirty page were to be discarded from the cache via the pagerStress() +** routine, pagerStress() would not write the current page content to +** the database file. If a savepoint transaction were rolled back after +** this happened, the correct behaviour would be to restore the current +** content of the page. However, since this content is not present in either +** the database file or the portion of the rollback journal and +** sub-journal rolled back the content could not be restored and the +** database image would become corrupt. It is therefore fortunate that +** this circumstance cannot arise. +*/ +#if defined(SQLITE_DEBUG) +static void assertTruncateConstraintCb(PgHdr *pPg){ + assert( pPg->flags&PGHDR_DIRTY ); + assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); +} +static void assertTruncateConstraint(Pager *pPager){ + sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); +} +#else +# define assertTruncateConstraint(pPager) +#endif + /* ** Truncate the in-memory database file image to nPage pages. This ** function does not actually modify the database file on disk. It ** just sets the internal state of the pager object so that the ** truncation will be done when the current transaction is committed. @@ -2646,10 +2593,11 @@ void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ assert( pPager->dbSizeValid ); assert( pPager->dbSize>=nPage ); assert( pPager->state>=PAGER_RESERVED ); pPager->dbSize = nPage; + assertTruncateConstraint(pPager); } /* ** Shutdown the page cache. Free all memory and close all files. ** @@ -2881,11 +2829,11 @@ */ static int pager_write_pagelist(PgHdr *pList){ Pager *pPager; /* Pager object */ int rc; /* Return code */ - if( pList==0 ) return SQLITE_OK; + if( NEVER(pList==0) ) return SQLITE_OK; pPager = pList->pPager; /* At this point there may be either a RESERVED or EXCLUSIVE lock on the ** database file. If there is already an EXCLUSIVE lock, the following ** call is a no-op. @@ -2921,11 +2869,13 @@ ** than Pager.dbSize, this means sqlite3PagerTruncateImage() was called to ** make the file smaller (presumably by auto-vacuum code). Do not write ** any such pages to the file. ** ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag - ** set (set by sqlite3PagerDontWrite()). + ** set (set by sqlite3PagerDontWrite()). Note that if compiled with + ** SQLITE_SECURE_DELETE the PGHDR_DONT_WRITE bit is never set and so + ** the second test is always true. */ if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ char *pData; /* Data to write */ @@ -2998,11 +2948,10 @@ } if( rc==SQLITE_OK ){ pPager->nSubRec++; assert( pPager->nSavepoint>0 ); rc = addToSavepointBitvecs(pPager, pPg->pgno); - testcase( rc!=SQLITE_OK ); } return rc; } @@ -3043,11 +2992,13 @@ ** reusing pPg. ** ** Similarly, if the pager has already entered the error state, do not ** try to write the contents of pPg to disk. */ - if( pPager->errCode || (pPager->doNotSync && pPg->flags&PGHDR_NEED_SYNC) ){ + if( NEVER(pPager->errCode) + || (pPager->doNotSync && pPg->flags&PGHDR_NEED_SYNC) + ){ return SQLITE_OK; } /* Sync the journal file if required. */ if( pPg->flags&PGHDR_NEED_SYNC ){ @@ -3086,11 +3037,13 @@ ** The solution is to write the current data for page X into the ** sub-journal file now (if it is not already there), so that it will ** be restored to its current value when the "ROLLBACK TO sp" is ** executed. */ - if( rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) ){ + if( NEVER( + rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) + ) ){ rc = subjournalPage(pPg); } /* Write the contents of the page out to the database file. */ if( rc==SQLITE_OK ){ @@ -3142,11 +3095,12 @@ sqlite3_vfs *pVfs, /* The virtual file system to use */ Pager **ppPager, /* OUT: Return the Pager structure here */ const char *zFilename, /* Name of the database file to open */ int nExtra, /* Extra bytes append to each in-memory page */ int flags, /* flags controlling this file */ - int vfsFlags /* flags passed through to sqlite3_vfs.xOpen() */ + int vfsFlags, /* flags passed through to sqlite3_vfs.xOpen() */ + void (*xReinit)(DbPage*) /* Function to reinitialize pages */ ){ u8 *pPtr; Pager *pPager = 0; /* Pager object to allocate and return */ int rc = SQLITE_OK; /* Return code */ int tempFile = 0; /* True for temp files (incl. in-memory files) */ @@ -3251,10 +3205,11 @@ if( zPathname ){ pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); + if( pPager->zFilename[0]==0 ) pPager->zJournal[0] = 0; sqlite3_free(zPathname); } pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; @@ -3360,25 +3315,29 @@ pPager->exclusiveMode = (u8)tempFile; pPager->changeCountDone = pPager->tempFile; pPager->memDb = (u8)memDb; pPager->readOnly = (u8)readOnly; /* pPager->needSync = 0; */ - pPager->noSync = (pPager->tempFile || !useJournal) ?1:0; + assert( useJournal || pPager->tempFile ); + pPager->noSync = pPager->tempFile; pPager->fullSync = pPager->noSync ?0:1; pPager->sync_flags = SQLITE_SYNC_NORMAL; /* pPager->pFirst = 0; */ /* pPager->pFirstSynced = 0; */ /* pPager->pLast = 0; */ pPager->nExtra = (u16)nExtra; pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; assert( isOpen(pPager->fd) || tempFile ); setSectorSize(pPager); - if( memDb ){ + if( !useJournal ){ + pPager->journalMode = PAGER_JOURNALMODE_OFF; + }else if( memDb ){ pPager->journalMode = PAGER_JOURNALMODE_MEMORY; } /* pPager->xBusyHandler = 0; */ /* pPager->pBusyHandlerArg = 0; */ + pPager->xReiniter = xReinit; /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ *ppPager = pPager; return SQLITE_OK; } @@ -3422,10 +3381,11 @@ assert( pPager!=0 ); assert( pPager->useJournal ); assert( isOpen(pPager->fd) ); assert( !isOpen(pPager->jfd) ); + assert( pPager->state <= PAGER_SHARED ); *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 */ @@ -3450,17 +3410,13 @@ */ rc = sqlite3PagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ if( nPage==0 ){ sqlite3BeginBenignMalloc(); - if( pPager->state>=PAGER_RESERVED - || sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){ + if( sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); - assert( pPager->state>=PAGER_SHARED ); - if( pPager->state==PAGER_SHARED ){ - sqlite3OsUnlock(pPager->fd, SHARED_LOCK); - } + sqlite3OsUnlock(pPager->fd, SHARED_LOCK); } sqlite3EndBenignMalloc(); }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is @@ -3515,12 +3471,13 @@ Pgno pgno = pPg->pgno; /* Page number to read */ int rc; /* Return code */ i64 iOffset; /* Byte offset of file to read from */ assert( pPager->state>=PAGER_SHARED && !MEMDB ); + assert( isOpen(pPager->fd) ); - if( !isOpen(pPager->fd) ){ + if( NEVER(!isOpen(pPager->fd)) ){ assert( pPager->tempFile ); memset(pPg->pData, 0, pPager->pageSize); return SQLITE_OK; } iOffset = (pgno-1)*(i64)pPager->pageSize; @@ -3542,14 +3499,16 @@ return rc; } /* -** This function is called whenever the upper layer requests a database -** page is requested, before the cache is checked for a suitable page -** or any data is read from the database. It performs the following -** two functions: +** This function is called to obtain a shared lock on the database file. +** It is illegal to call sqlite3PagerAcquire() until after this function +** has been successfully called. If a shared-lock is already held when +** this function is called, it is a no-op. +** +** The following operations are also performed by this function. ** ** 1) If the pager is currently in PAGER_UNLOCK state (no lock held ** on the database file), then an attempt is made to obtain a ** SHARED lock on the database file. Immediately after obtaining ** the SHARED lock, the file-system is checked for a hot-journal, @@ -3571,55 +3530,53 @@ ** ** Otherwise, if everything is successful, SQLITE_OK is returned. If an ** IO error occurs while locking the database, checking for a hot-journal ** file or rolling back a journal file, the IO error code is returned. */ -static int pagerSharedLock(Pager *pPager){ +int sqlite3PagerSharedLock(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ int isErrorReset = 0; /* True if recovering from error state */ - /* If this database has no outstanding page references and is in an - ** error-state, this is a chance to clear the error. Discard the - ** contents of the pager-cache and rollback any hot journal in the - ** file-system. + /* This routine is only called from b-tree and only when there are no + ** outstanding pages */ + assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); + if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; } + + /* If this database is in an error-state, now is a chance to clear + ** the error. Discard the contents of the pager-cache and rollback + ** any hot journal in the file-system. */ - if( !MEMDB && sqlite3PcacheRefCount(pPager->pPCache)==0 && pPager->errCode ){ + if( pPager->errCode ){ if( isOpen(pPager->jfd) || pPager->zJournal ){ isErrorReset = 1; } pPager->errCode = SQLITE_OK; pager_reset(pPager); } - /* If the pager is still in an error state, do not proceed. The error - ** state will be cleared at some point in the future when all page - ** references are dropped and the cache can be discarded. - */ - if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){ - return pPager->errCode; - } - if( pPager->state==PAGER_UNLOCK || isErrorReset ){ sqlite3_vfs * const pVfs = pPager->pVfs; int isHotJournal = 0; assert( !MEMDB ); assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); - if( !pPager->noReadlock ){ + if( pPager->noReadlock ){ + assert( pPager->readOnly ); + pPager->state = PAGER_SHARED; + }else{ rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){ assert( pPager->state==PAGER_UNLOCK ); return pager_error(pPager, rc); } - }else if( pPager->state==PAGER_UNLOCK ){ - pPager->state = PAGER_SHARED; } assert( pPager->state>=SHARED_LOCK ); /* If a journal file exists, and there is no RESERVED lock on the ** database file, then it either needs to be played back or deleted. */ if( !isErrorReset ){ + assert( pPager->state <= PAGER_SHARED ); rc = hasHotJournal(pPager, &isHotJournal); if( rc!=SQLITE_OK ){ goto failed; } } @@ -3767,32 +3724,15 @@ ){ pagerUnlockAndRollback(pPager); } } -/* -** Drop a page from the cache using sqlite3PcacheDrop(). -** -** If this means there are now no pages with references to them, a rollback -** occurs and the lock on the database is removed. -*/ -static void pagerDropPage(DbPage *pPg){ - Pager *pPager = pPg->pPager; - sqlite3PcacheDrop(pPg); - pagerUnlockIfUnused(pPager); -} - /* ** Acquire a reference to page number pgno in pager pPager (a page ** reference has type DbPage*). If the requested reference is ** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. ** -** This function calls pagerSharedLock() to obtain a SHARED lock on -** the database file if such a lock or greater is not already held. -** This may cause hot-journal rollback or a cache purge. See comments -** above function pagerSharedLock() for details. -** ** If the requested page is already in the cache, it is returned. ** Otherwise, a new page object is allocated and populated with data ** read from the database file. In some cases, the pcache module may ** choose not to allocate a new page object and may reuse an existing ** object with no outstanding references. @@ -3840,66 +3780,70 @@ Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ int noContent /* Do not bother reading content from disk if true */ ){ - PgHdr *pPg = 0; int rc; + PgHdr *pPg; assert( assert_pager_state(pPager) ); - assert( pPager->state==PAGER_UNLOCK - || sqlite3PcacheRefCount(pPager->pPCache)>0 - || pgno==1 - ); - - /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page - ** number greater than this, or zero, is requested. - */ - if( pgno>PAGER_MAX_PGNO || pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ + assert( pPager->state>PAGER_UNLOCK ); + + if( pgno==0 ){ return SQLITE_CORRUPT_BKPT; } - /* Make sure we have not hit any critical errors. - */ - assert( pPager!=0 ); - *ppPage = 0; - - /* If this is the first page accessed, then get a SHARED lock - ** on the database file. pagerSharedLock() is a no-op if - ** a database lock is already held. - */ - rc = pagerSharedLock(pPager); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pPager->state!=PAGER_UNLOCK ); - - rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, &pPg); - if( rc!=SQLITE_OK ){ - pagerUnlockIfUnused(pPager); - return rc; - } - assert( pPg->pgno==pgno ); - assert( pPg->pPager==pPager || pPg->pPager==0 ); - if( pPg->pPager==0 ){ - /* The pager cache has created a new page. Its content needs to - ** be initialized. - */ - int nMax; - PAGER_INCR(pPager->nMiss); - pPg->pPager = pPager; + /* If the pager is in the error state, return an error immediately. + ** Otherwise, request the page from the PCache layer. */ + if( pPager->errCode!=SQLITE_OK && pPager->errCode!=SQLITE_FULL ){ + rc = pPager->errCode; + }else{ + rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage); + } + + if( rc!=SQLITE_OK ){ + /* Either the call to sqlite3PcacheFetch() returned an error or the + ** pager was already in the error-state when this function was called. + ** Set pPg to 0 and jump to the exception handler. */ + pPg = 0; + goto pager_acquire_err; + } + assert( (*ppPage)->pgno==pgno ); + assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 ); + + if( (*ppPage)->pPager ){ + /* In this case the pcache already contains an initialized copy of + ** the page. Return without further ado. */ + assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); + PAGER_INCR(pPager->nHit); + return SQLITE_OK; + + }else{ + /* The pager cache has created a new page. Its content needs to + ** be initialized. */ + int nMax; + + PAGER_INCR(pPager->nMiss); + pPg = *ppPage; + pPg->pPager = pPager; + + /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page + ** number greater than this, or the unused locking-page, is requested. */ + if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ + rc = SQLITE_CORRUPT_BKPT; + goto pager_acquire_err; + } rc = sqlite3PagerPagecount(pPager, &nMax); if( rc!=SQLITE_OK ){ - sqlite3PagerUnref(pPg); - return rc; + goto pager_acquire_err; } if( nMax<(int)pgno || MEMDB || noContent ){ if( pgno>pPager->mxPgno ){ - sqlite3PagerUnref(pPg); - return SQLITE_FULL; + rc = SQLITE_FULL; + goto pager_acquire_err; } if( noContent ){ /* Failure to set the bits in the InJournal bit-vectors is benign. ** It merely means that we might do some extra work to journal a ** page that does not need to be journaled. Nevertheless, be sure @@ -3920,24 +3864,29 @@ IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ assert( pPg->pPager==pPager ); rc = readDbPage(pPg); if( rc!=SQLITE_OK ){ - pagerDropPage(pPg); - return rc; + goto pager_acquire_err; } } #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif - }else{ - /* The requested page is in the page cache. */ - PAGER_INCR(pPager->nHit); } - *ppPage = pPg; return SQLITE_OK; + +pager_acquire_err: + assert( rc!=SQLITE_OK ); + if( pPg ){ + sqlite3PcacheDrop(pPg); + } + pagerUnlockIfUnused(pPager); + + *ppPage = 0; + return rc; } /* ** Acquire a page if it is already in the in-memory cache. Do ** not read the page from disk. Return a pointer to the page, @@ -3953,17 +3902,13 @@ */ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ PgHdr *pPg = 0; assert( pPager!=0 ); assert( pgno!=0 ); - - if( (pPager->state!=PAGER_UNLOCK) - && (pPager->errCode==SQLITE_OK || pPager->errCode==SQLITE_FULL) - ){ - sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); - } - + assert( pPager->pPCache!=0 ); + assert( pPager->state > PAGER_UNLOCK ); + sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); return pPg; } /* ** Release a page reference. @@ -4028,16 +3973,17 @@ int rc = SQLITE_OK; /* Return code */ sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */ assert( pPager->state>=PAGER_RESERVED ); assert( pPager->useJournal ); + assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF ); assert( pPager->pInJournal==0 ); - /* If already in the error state, this function is a no-op. */ - if( pPager->errCode ){ - return pPager->errCode; - } + /* If already in the error state, this function is a no-op. But on + ** the other hand, this routine is never called if we are already in + ** an error state. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; /* TODO: Is it really possible to get here with dbSizeValid==0? If not, ** the call to PagerPagecount() can be removed. */ testcase( pPager->dbSizeValid==0 ); @@ -4143,13 +4089,11 @@ } /* If the required locks were successfully obtained, open the journal ** file and write the first journal-header to it. */ - if( rc==SQLITE_OK && pPager->useJournal - && pPager->journalMode!=PAGER_JOURNALMODE_OFF - ){ + if( rc==SQLITE_OK && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ rc = pager_open_journal(pPager); } }else if( isOpen(pPager->jfd) && pPager->journalOff==0 ){ /* This happens when the pager was in exclusive-access mode the last ** time a (read or write) transaction was successfully concluded @@ -4163,10 +4107,19 @@ rc = pager_open_journal(pPager); } PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); assert( !isOpen(pPager->jfd) || pPager->journalOff>0 || rc!=SQLITE_OK ); + if( rc!=SQLITE_OK ){ + assert( !pPager->dbModified ); + /* Ignore any IO error that occurs within pager_end_transaction(). The + ** purpose of this call is to reset the internal state of the pager + ** sub-system. It doesn't matter if the journal-file is not properly + ** finalized at this point (since it is not a valid journal file anyway). + */ + pager_end_transaction(pPager, 0); + } return rc; } /* ** Mark a single data page as writeable. The page is written into the @@ -4178,18 +4131,23 @@ static int pager_write(PgHdr *pPg){ void *pData = pPg->pData; Pager *pPager = pPg->pPager; int rc = SQLITE_OK; - /* Check for errors + /* This routine is not called unless a transaction has already been + ** started. + */ + assert( pPager->state>=PAGER_RESERVED ); + + /* If an error has been previously detected, we should not be + ** calling this routine. Repeat the error for robustness. */ - if( pPager->errCode ){ - return pPager->errCode; - } - if( pPager->readOnly ){ - return SQLITE_PERM; - } + if( NEVER(pPager->errCode) ) return pPager->errCode; + + /* Higher-level routines never call this function if database is not + ** writable. But check anyway, just for robustness. */ + if( NEVER(pPager->readOnly) ) return SQLITE_PERM; assert( !pPager->setMaster ); CHECK_PAGE(pPg); @@ -4203,21 +4161,20 @@ /* If we get this far, it means that the page needs to be ** written to the transaction journal or the ckeckpoint journal ** or both. ** - ** First check to see that the transaction journal exists and - ** create it if it does not. + ** Higher level routines should have already started a transaction, + ** which means they have acquired the necessary locks and opened + ** a rollback journal. Double-check to makes sure this is the case. */ - assert( pPager->state!=PAGER_UNLOCK ); rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory); - if( rc!=SQLITE_OK ){ + if( NEVER(rc!=SQLITE_OK) ){ return rc; } - assert( pPager->state>=PAGER_RESERVED ); - if( !isOpen(pPager->jfd) && pPager->useJournal - && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + assert( pPager->useJournal ); rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } pPager->dbModified = 1; @@ -4392,13 +4349,13 @@ ** starting at pg1, then it needs to be set for all of them. Because ** writing to any of these nPage pages may damage the others, the ** journal file must contain sync()ed copies of all of them ** before any of them can be written out to the database file. */ - if( needSync ){ + if( rc==SQLITE_OK && needSync ){ assert( !MEMDB && pPager->noSync==0 ); - for(ii=0; iiflags |= PGHDR_NEED_SYNC; sqlite3PagerUnref(pPage); } @@ -4423,10 +4380,11 @@ int sqlite3PagerIswriteable(DbPage *pPg){ return pPg->flags&PGHDR_DIRTY; } #endif +#ifndef SQLITE_SECURE_DELETE /* ** A call to this routine tells the pager that it is not necessary to ** write the information on page pPg back to the disk, even though ** that page might be marked as dirty. This happens, for example, when ** the page has been added as a leaf of the freelist and so its @@ -4448,22 +4406,23 @@ #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif } } +#endif /* !defined(SQLITE_SECURE_DELETE) */ /* ** This routine is called to increment the value of the database file ** change-counter, stored as a 4-byte big-endian integer starting at ** byte offset 24 of the pager file. ** -** If the isDirect flag is zero, then this is done by calling +** If the isDirectMode flag is zero, then this is done by calling ** sqlite3PagerWrite() on page 1, then modifying the contents of the ** page data. In this case the file will be updated when the current ** transaction is committed. ** -** The isDirect flag may only be non-zero if the library was compiled +** The isDirectMode flag may only be non-zero if the library was compiled ** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, ** if isDirect is non-zero, then the database file is updated directly ** by writing an updated version of page 1 using a call to the ** sqlite3OsWrite() function. */ @@ -4479,15 +4438,15 @@ ** enabled at compile time, the compiler can omit the tests of ** 'isDirect' below, as well as the block enclosed in the ** "if( isDirect )" condition. */ #ifndef SQLITE_ENABLE_ATOMIC_WRITE - const int isDirect = 0; +# define DIRECT_MODE 0 assert( isDirectMode==0 ); UNUSED_PARAMETER(isDirectMode); #else - const int isDirect = isDirectMode; +# define DIRECT_MODE isDirectMode #endif assert( pPager->state>=PAGER_RESERVED ); if( !pPager->changeCountDone && pPager->dbSize>0 ){ PgHdr *pPgHdr; /* Reference to page 1 */ @@ -4498,13 +4457,15 @@ /* Open page 1 of the file for writing. */ rc = sqlite3PagerGet(pPager, 1, &pPgHdr); assert( pPgHdr==0 || rc==SQLITE_OK ); /* If page one was fetched successfully, and this function is not - ** operating in direct-mode, make page 1 writable. + ** operating in direct-mode, make page 1 writable. When not in + ** direct mode, page 1 is always held in cache and hence the PagerGet() + ** above is always successful - hence the ALWAYS on rc==SQLITE_OK. */ - if( rc==SQLITE_OK && !isDirect ){ + if( !DIRECT_MODE && ALWAYS(rc==SQLITE_OK) ){ rc = sqlite3PagerWrite(pPgHdr); } if( rc==SQLITE_OK ){ /* Increment the value just read and write it back to byte 24. */ @@ -4511,18 +4472,18 @@ change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers); change_counter++; put32bits(((char*)pPgHdr->pData)+24, change_counter); /* If running in direct mode, write the contents of page 1 to the file. */ - if( isDirect ){ + if( DIRECT_MODE ){ const void *zBuf = pPgHdr->pData; assert( pPager->dbFileSize>0 ); rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); - } - - /* If everything worked, set the changeCountDone flag. */ - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ + pPager->changeCountDone = 1; + } + }else{ pPager->changeCountDone = 1; } } /* Release the page reference. */ @@ -4538,11 +4499,12 @@ ** If successful, or called on a pager for which it is a no-op, this ** function returns SQLITE_OK. Otherwise, an IO error code is returned. */ int sqlite3PagerSync(Pager *pPager){ int rc; /* Return code */ - if( MEMDB || pPager->noSync ){ + assert( !MEMDB ); + if( pPager->noSync ){ rc = SQLITE_OK; }else{ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags); } return rc; @@ -4579,21 +4541,26 @@ const char *zMaster, /* If not NULL, the master journal name */ int noSync /* True to omit the xSync on the db file */ ){ int rc = SQLITE_OK; /* Return code */ - if( pPager->errCode ){ - return pPager->errCode; - } + /* The dbOrigSize is never set if journal_mode=OFF */ + assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF || pPager->dbOrigSize==0 ); + + /* If a prior error occurred, this routine should not be called. ROLLBACK + ** is the appropriate response to an error, not COMMIT. Guard against + ** coding errors by repeating the prior error. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", pPager->zFilename, zMaster, pPager->dbSize)); - /* If this is an in-memory db, or no pages have been written to, or this - ** function has already been called, it is a no-op. - */ if( MEMDB && pPager->dbModified ){ + /* If this is an in-memory db, or no pages have been written to, or this + ** function has already been called, it is mostly a no-op. However, any + ** backup in progress needs to be restarted. + */ sqlite3BackupRestart(pPager->pBackup); }else if( pPager->state!=PAGER_SYNCED && pPager->dbModified ){ /* The following block updates the change-counter. Exactly how it ** does this depends on whether or not the atomic-update optimization @@ -4651,14 +4618,17 @@ ** Before reading the pages with page numbers larger than the ** current value of Pager.dbSize, set dbSize back to the value ** that it took at the start of the transaction. Otherwise, the ** calls to sqlite3PagerGet() return zeroed pages instead of ** reading data from the database file. + ** + ** When journal_mode==OFF the dbOrigSize is always zero, so this + ** block never runs if journal_mode=OFF. */ #ifndef SQLITE_OMIT_AUTOVACUUM - if( pPager->dbSizedbOrigSize - && pPager->journalMode!=PAGER_JOURNALMODE_OFF + if( pPager->dbSizedbOrigSize + && ALWAYS(pPager->journalMode!=PAGER_JOURNALMODE_OFF) ){ Pgno i; /* Iterator variable */ const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ const Pgno dbSize = pPager->dbSize; /* Database image size */ pPager->dbSize = pPager->dbOrigSize; @@ -4716,18 +4686,10 @@ pPager->state = PAGER_SYNCED; } commit_phase_one_exit: - if( rc==SQLITE_IOERR_BLOCKED ){ - /* pager_incr_changecounter() may attempt to obtain an exclusive - ** lock to spill the cache and return IOERR_BLOCKED. But since - ** there is no chance the cache is inconsistent, it is - ** better to return SQLITE_BUSY. - **/ - rc = SQLITE_BUSY; - } return rc; } /* @@ -4746,22 +4708,20 @@ ** moves into the error state. Otherwise, SQLITE_OK is returned. */ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ - /* Do not proceed if the pager is already in the error state. */ - if( pPager->errCode ){ - return pPager->errCode; - } + /* This routine should not be called if a prior error has occurred. + ** But if (due to a coding error elsewhere in the system) it does get + ** called, just return the same error code without doing anything. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; /* This function should not be called if the pager is not in at least ** PAGER_RESERVED state. And indeed SQLite never does this. But it is - ** nice to have this defensive block here anyway. + ** nice to have this defensive test here anyway. */ - if( NEVER(pPager->statestatedbSizeValid ); aNew[ii].nOrig = pPager->dbSize; - if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ + if( isOpen(pPager->jfd) && ALWAYS(pPager->journalOff>0) ){ aNew[ii].iOffset = pPager->journalOff; }else{ aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); } aNew[ii].iSubRec = pPager->nSubRec; @@ -4966,10 +4926,11 @@ } } /* Open the sub-journal, if it is not already opened. */ rc = openSubJournal(pPager); + assertTruncateConstraint(pPager); } return rc; } @@ -5093,11 +5054,11 @@ void (*xCodecSizeChng)(void*,int,int), void (*xCodecFree)(void*), void *pCodec ){ if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); - pPager->xCodec = xCodec; + pPager->xCodec = pPager->memDb ? 0 : xCodec; pPager->xCodecSizeChng = xCodecSizeChng; pPager->xCodecFree = xCodecFree; pPager->pCodec = pCodec; pagerReportSize(pPager); } @@ -5221,11 +5182,11 @@ */ PgHdr *pPgHdr; assert( pPager->needSync ); rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr); if( rc!=SQLITE_OK ){ - if( pPager->pInJournal && needSyncPgno<=pPager->dbOrigSize ){ + if( needSyncPgno<=pPager->dbOrigSize ){ assert( pPager->pTmpSpace!=0 ); sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace); } return rc; } @@ -5243,11 +5204,14 @@ ** 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; + if( rc!=SQLITE_OK ){ + sqlite3PcacheMove(pPg, origPgno); + return rc; + } sqlite3PagerUnref(pNew); } return SQLITE_OK; } @@ -5264,12 +5228,11 @@ /* ** Return a pointer to the Pager.nExtra bytes of "extra" space ** allocated along with the specified page. */ void *sqlite3PagerGetExtra(DbPage *pPg){ - Pager *pPager = pPg->pPager; - return (pPager?pPg->pExtra:0); + return pPg->pExtra; } /* ** Get/set the locking-mode for this pager. Parameter eMode must be one ** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or Index: src/pager.h ================================================================== --- src/pager.h +++ src/pager.h @@ -10,12 +10,10 @@ ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. -** -** @(#) $Id: pager.h,v 1.102 2009/06/18 17:22:39 drh Exp $ */ #ifndef _PAGER_H_ #define _PAGER_H_ @@ -84,17 +82,24 @@ ** that make up the Pager sub-system API. See source code comments for ** a detailed description of each routine. */ /* Open and close a Pager connection. */ -int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int); +int sqlite3PagerOpen( + sqlite3_vfs*, + Pager **ppPager, + const char*, + int, + int, + int, + void(*)(DbPage*) +); int sqlite3PagerClose(Pager *pPager); int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); -void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*)); int sqlite3PagerSetPagesize(Pager*, u16*, int); int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); void sqlite3PagerSetSafetyLevel(Pager*,int,int); int sqlite3PagerLockingMode(Pager *, int); @@ -124,10 +129,11 @@ int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); +int sqlite3PagerSharedLock(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); int sqlite3PagerRefcount(Pager*); const char *sqlite3PagerFilename(Pager*); Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -11,12 +11,10 @@ ************************************************************************* ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. -** -** @(#) $Id: parse.y,v 1.283 2009/06/19 14:06:03 drh Exp $ */ // All token codes are small integers with #defines that begin with "TK_" %token_prefix TK_ @@ -194,13 +192,13 @@ // The following directive causes tokens ABORT, AFTER, ASC, etc. to // fallback to ID if they will not parse as their original value. // This obviates the need for the "id" nonterminal. // %fallback ID - ABORT AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW CONFLICT - DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR - IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH PLAN + ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW + CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR + IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN QUERY KEY OF OFFSET PRAGMA RAISE RELEASE REPLACE RESTRICT ROW ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif SQLITE_OMIT_COMPOUND_SELECT @@ -228,11 +226,11 @@ %left BITAND BITOR LSHIFT RSHIFT. %left PLUS MINUS. %left STAR SLASH REM. %left CONCAT. %left COLLATE. -%right UMINUS UPLUS BITNOT. +%right BITNOT. // And "ids" is an identifer-or-string. // %type ids {Token} ids(A) ::= ID|STRING(X). {A = X;} @@ -312,24 +310,24 @@ // that determine if the referential integrity checking is deferred or // or immediate and which determine what action to take if a ref-integ // check fails. // %type refargs {int} -refargs(A) ::= . { A = OE_Restrict * 0x010101; } +refargs(A) ::= . { A = OE_None*0x0101; /* EV: R-19803-45884 */} refargs(A) ::= refargs(X) refarg(Y). { A = (X & ~Y.mask) | Y.value; } %type refarg {struct {int value; int mask;}} refarg(A) ::= MATCH nm. { A.value = 0; A.mask = 0x000000; } refarg(A) ::= ON DELETE refact(X). { A.value = X; A.mask = 0x0000ff; } refarg(A) ::= ON UPDATE refact(X). { A.value = X<<8; A.mask = 0x00ff00; } -refarg(A) ::= ON INSERT refact(X). { A.value = X<<16; A.mask = 0xff0000; } %type refact {int} -refact(A) ::= SET NULL. { A = OE_SetNull; } -refact(A) ::= SET DEFAULT. { A = OE_SetDflt; } -refact(A) ::= CASCADE. { A = OE_Cascade; } -refact(A) ::= RESTRICT. { A = OE_Restrict; } +refact(A) ::= SET NULL. { A = OE_SetNull; /* EV: R-33326-45252 */} +refact(A) ::= SET DEFAULT. { A = OE_SetDflt; /* EV: R-33326-45252 */} +refact(A) ::= CASCADE. { A = OE_Cascade; /* EV: R-33326-45252 */} +refact(A) ::= RESTRICT. { A = OE_Restrict; /* EV: R-33326-45252 */} +refact(A) ::= NO ACTION. { A = OE_None; /* EV: R-33326-45252 */} %type defer_subclause {int} -defer_subclause(A) ::= NOT DEFERRABLE init_deferred_pred_opt(X). {A = X;} +defer_subclause(A) ::= NOT DEFERRABLE init_deferred_pred_opt. {A = 0;} defer_subclause(A) ::= DEFERRABLE init_deferred_pred_opt(X). {A = X;} %type init_deferred_pred_opt {int} init_deferred_pred_opt(A) ::= . {A = 0;} init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;} init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;} @@ -360,16 +358,16 @@ // The following is a non-standard extension that allows us to declare the // default behavior when there is a constraint conflict. // %type onconf {int} -%type orconf {int} +%type orconf {u8} %type resolvetype {int} onconf(A) ::= . {A = OE_Default;} onconf(A) ::= ON CONFLICT resolvetype(X). {A = X;} orconf(A) ::= . {A = OE_Default;} -orconf(A) ::= OR resolvetype(X). {A = X;} +orconf(A) ::= OR resolvetype(X). {A = (u8)X;} resolvetype(A) ::= raisetype(X). {A = X;} resolvetype(A) ::= IGNORE. {A = OE_Ignore;} resolvetype(A) ::= REPLACE. {A = OE_Replace;} ////////////////////////// The DROP TABLE ///////////////////////////////////// @@ -502,13 +500,11 @@ as(Z) on_opt(N) using_opt(U). { A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,S,N,U); } seltablist(A) ::= stl_prefix(X) LP seltablist(F) RP as(Z) on_opt(N) using_opt(U). { - if( X==0 ){ - sqlite3ExprDelete(pParse->db, N); - sqlite3IdListDelete(pParse->db, U); + if( X==0 && Z.n==0 && N==0 && U==0 ){ A = F; }else{ Select *pSubquery; sqlite3SrcListShiftJoinType(F); pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0); @@ -687,11 +683,11 @@ cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). {sqlite3Insert(pParse, X, 0, S, F, R);} cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. {sqlite3Insert(pParse, X, 0, 0, F, R);} -%type insert_cmd {int} +%type insert_cmd {u8} insert_cmd(A) ::= INSERT orconf(R). {A = R;} insert_cmd(A) ::= REPLACE. {A = OE_Replace;} %type itemlist {ExprList*} @@ -884,14 +880,30 @@ pOut->zEnd = &pPostOp->z[pPostOp->n]; } } expr(A) ::= expr(X) ISNULL|NOTNULL(E). {spanUnaryPostfix(&A,pParse,@E,&X,&E);} -expr(A) ::= expr(X) IS NULL(E). {spanUnaryPostfix(&A,pParse,TK_ISNULL,&X,&E);} expr(A) ::= expr(X) NOT NULL(E). {spanUnaryPostfix(&A,pParse,TK_NOTNULL,&X,&E);} -expr(A) ::= expr(X) IS NOT NULL(E). - {spanUnaryPostfix(&A,pParse,TK_NOTNULL,&X,&E);} + +// expr1 IS expr2 +// expr1 IS NOT expr2 +// +// If expr2 is NULL then code as TK_ISNULL or TK_NOTNULL. If expr2 +// is any other expression, code as TK_IS or TK_ISNOT. +// +expr(A) ::= expr(X) IS expr(Y). { + spanBinaryExpr(&A,pParse,TK_IS,&X,&Y); + if( pParse->db->mallocFailed==0 && Y.pExpr->op==TK_NULL ){ + A.pExpr->op = TK_ISNULL; + } +} +expr(A) ::= expr(X) IS NOT expr(Y). { + spanBinaryExpr(&A,pParse,TK_ISNOT,&X,&Y); + if( pParse->db->mallocFailed==0 && Y.pExpr->op==TK_NULL ){ + A.pExpr->op = TK_NOTNULL; + } +} %include { /* Construct an expression node for a unary prefix operator */ static void spanUnaryPrefix( @@ -909,13 +921,13 @@ expr(A) ::= NOT(B) expr(X). {spanUnaryPrefix(&A,pParse,@B,&X,&B);} expr(A) ::= BITNOT(B) expr(X). {spanUnaryPrefix(&A,pParse,@B,&X,&B);} -expr(A) ::= MINUS(B) expr(X). [UMINUS] +expr(A) ::= MINUS(B) expr(X). [BITNOT] {spanUnaryPrefix(&A,pParse,TK_UMINUS,&X,&B);} -expr(A) ::= PLUS(B) expr(X). [UPLUS] +expr(A) ::= PLUS(B) expr(X). [BITNOT] {spanUnaryPrefix(&A,pParse,TK_UPLUS,&X,&B);} %type between_op {int} between_op(A) ::= BETWEEN. {A = 0;} between_op(A) ::= NOT BETWEEN. {A = 1;} @@ -1178,27 +1190,59 @@ trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. { assert( X!=0 ); X->pLast = X; A = X; } + +// Disallow qualified table names on INSERT, UPDATE, and DELETE statements +// within a trigger. The table to INSERT, UPDATE, or DELETE is always in +// the same database as the table that the trigger fires on. +// +%type trnm {Token} +trnm(A) ::= nm(X). {A = X;} +trnm(A) ::= nm DOT nm(X). { + A = X; + sqlite3ErrorMsg(pParse, + "qualified table names are not allowed on INSERT, UPDATE, and DELETE " + "statements within triggers"); +} + +// Disallow the INDEX BY and NOT INDEXED clauses on UPDATE and DELETE +// statements within triggers. We make a specific error message for this +// since it is an exception to the default grammar rules. +// +tridxby ::= . +tridxby ::= INDEXED BY nm. { + sqlite3ErrorMsg(pParse, + "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " + "within triggers"); +} +tridxby ::= NOT INDEXED. { + sqlite3ErrorMsg(pParse, + "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " + "within triggers"); +} + + %type trigger_cmd {TriggerStep*} %destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);} // UPDATE -trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z). - { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } +trigger_cmd(A) ::= + UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). + { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } // INSERT -trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) - VALUES LP itemlist(Y) RP. - {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);} +trigger_cmd(A) ::= + insert_cmd(R) INTO trnm(X) inscollist_opt(F) VALUES LP itemlist(Y) RP. + {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);} -trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S). +trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S). {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);} // DELETE -trigger_cmd(A) ::= DELETE FROM nm(X) where_opt(Y). +trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);} // SELECT trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); } Index: src/pcache.c ================================================================== --- src/pcache.c +++ src/pcache.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file implements that page cache. -** -** @(#) $Id: pcache.c,v 1.44 2009/03/31 01:32:18 drh Exp $ */ #include "sqliteInt.h" /* ** A complete page cache is an instance of this structure. @@ -206,10 +204,11 @@ ){ PgHdr *pPage = 0; int eCreate; assert( pCache!=0 ); + assert( createFlag==1 || createFlag==0 ); assert( pgno>0 ); /* If the pluggable cache (sqlite3_pcache*) has not been allocated, ** allocate it now. */ @@ -223,14 +222,11 @@ } sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax); pCache->pCache = p; } - eCreate = createFlag ? 1 : 0; - if( eCreate && (!pCache->bPurgeable || !pCache->pDirty) ){ - eCreate = 2; - } + eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty)); if( pCache->pCache ){ pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate); } if( !pPage && eCreate==1 ){ @@ -468,41 +464,37 @@ /* ** Sort the list of pages in accending order by pgno. Pages are ** connected by pDirty pointers. The pDirtyPrev pointers are ** corrupted by this sort. +** +** Since there cannot be more than 2^31 distinct pages in a database, +** there cannot be more than 31 buckets required by the merge sorter. +** One extra bucket is added to catch overflow in case something +** ever changes to make the previous sentence incorrect. */ -#define N_SORT_BUCKET_ALLOC 25 -#define N_SORT_BUCKET 25 -#ifdef SQLITE_TEST - int sqlite3_pager_n_sort_bucket = 0; - #undef N_SORT_BUCKET - #define N_SORT_BUCKET \ - (sqlite3_pager_n_sort_bucket?sqlite3_pager_n_sort_bucket:N_SORT_BUCKET_ALLOC) -#endif +#define N_SORT_BUCKET 32 static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ - PgHdr *a[N_SORT_BUCKET_ALLOC], *p; + PgHdr *a[N_SORT_BUCKET], *p; int i; memset(a, 0, sizeof(a)); while( pIn ){ p = pIn; pIn = p->pDirty; p->pDirty = 0; - for(i=0; ipCache ){ sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage); } } -#ifdef SQLITE_CHECK_PAGES +#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* ** For all dirty pages currently in the cache, invoke the specified ** callback. This is only used if the SQLITE_CHECK_PAGES macro is ** defined. */ Index: src/pcache.h ================================================================== --- src/pcache.h +++ src/pcache.h @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. -** -** @(#) $Id: pcache.h,v 1.19 2009/01/20 17:06:27 danielk1977 Exp $ */ #ifndef _PCACHE_H_ typedef struct PgHdr PgHdr; @@ -122,11 +120,11 @@ int sqlite3PcachePageRefcount(PgHdr*); /* Return the total number of pages stored in the cache */ int sqlite3PcachePagecount(PCache*); -#ifdef SQLITE_CHECK_PAGES +#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* Iterate through all dirty pages currently stored in the cache. This ** interface is only available if SQLITE_CHECK_PAGES is defined when the ** library is built. */ void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)); Index: src/pcache1.c ================================================================== --- src/pcache1.c +++ src/pcache1.c @@ -13,12 +13,10 @@ ** This file implements the default page cache implementation (the ** sqlite3_pcache interface). It also contains part of the implementation ** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features. ** If the default page cache implementation is overriden, then neither of ** these two features are available. -** -** @(#) $Id: pcache1.c,v 1.17 2009/06/09 18:58:53 shane Exp $ */ #include "sqliteInt.h" typedef struct PCache1 PCache1; @@ -216,13 +214,17 @@ return p; } /* ** Free a page object allocated by pcache1AllocPage(). +** +** The pointer is allowed to be NULL, which is prudent. But it turns out +** that the current implementation happens to never call this routine +** with a NULL pointer, so we mark the NULL test with ALWAYS(). */ static void pcache1FreePage(PgHdr1 *p){ - if( p ){ + if( ALWAYS(p) ){ if( p->pCache->bPurgeable ){ pcache1.nCurrentPage--; } pcache1Free(PGHDR1_TO_PAGE(p)); } @@ -406,10 +408,12 @@ return SQLITE_OK; } /* ** Implementation of the sqlite3_pcache.xShutdown method. +** Note that the static mutex allocated in xInit does +** not need to be freed. */ static void pcache1Shutdown(void *NotUsed){ UNUSED_PARAMETER(NotUsed); assert( pcache1.isInit!=0 ); memset(&pcache1, 0, sizeof(pcache1)); @@ -469,11 +473,18 @@ ** Implementation of the sqlite3_pcache.xFetch method. ** ** Fetch a page by key value. ** ** Whether or not a new page may be allocated by this function depends on -** the value of the createFlag argument. +** the value of the createFlag argument. 0 means do not allocate a new +** page. 1 means allocate a new page if space is easily available. 2 +** means to try really hard to allocate a new page. +** +** For a non-purgeable cache (a cache used as the storage for an in-memory +** database) there is really no difference between createFlag 1 and 2. So +** the calling function (pcache.c) will never have a createFlag of 1 on +** a non-purgable cache. ** ** There are three different approaches to obtaining space for a page, ** depending on the value of parameter createFlag (which may be 0, 1 or 2). ** ** 1. Regardless of the value of createFlag, the cache is searched for a @@ -480,13 +491,12 @@ ** copy of the requested page. If one is found, it is returned. ** ** 2. If createFlag==0 and the page is not already in the cache, NULL is ** returned. ** -** 3. If createFlag is 1, the cache is marked as purgeable and the page is -** not already in the cache, and if either of the following are true, -** return NULL: +** 3. If createFlag is 1, and the page is not already in the cache, +** and if either of the following are true, return NULL: ** ** (a) the number of pages pinned by the cache is greater than ** PCache1.nMax, or ** (b) the number of pages pinned by the cache is greater than ** the sum of nMax for all purgeable caches, less the sum of @@ -511,10 +521,11 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){ unsigned int nPinned; PCache1 *pCache = (PCache1 *)p; PgHdr1 *pPage = 0; + assert( pCache->bPurgeable || createFlag!=1 ); pcache1EnterMutex(); if( createFlag==1 ) sqlite3BeginBenignMalloc(); /* Search the hash table for an existing entry. */ if( pCache->nHash>0 ){ @@ -527,11 +538,11 @@ goto fetch_out; } /* Step 3 of header comment. */ nPinned = pCache->nPage - pCache->nRecyclable; - if( createFlag==1 && pCache->bPurgeable && ( + if( createFlag==1 && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) || nPinned>=(pCache->nMax * 9 / 10) )){ goto fetch_out; } @@ -653,11 +664,18 @@ h = iNew%pCache->nHash; pPage->iKey = iNew; pPage->pNext = pCache->apHash[h]; pCache->apHash[h] = pPage; - if( iNew>pCache->iMaxKey ){ + /* The xRekey() interface is only used to move pages earlier in the + ** database file (in order to move all free pages to the end of the + ** file where they can be truncated off.) Hence, it is not possible + ** for the new page number to be greater than the largest previously + ** fetched page. But we retain the following test in case xRekey() + ** begins to be used in different ways in the future. + */ + if( NEVER(iNew>pCache->iMaxKey) ){ pCache->iMaxKey = iNew; } pcache1LeaveMutex(); } Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the PRAGMA command. -** -** $Id: pragma.c,v 1.213 2009/06/19 14:06:03 drh Exp $ */ #include "sqliteInt.h" /* Ignore this whole file if pragmas are disabled */ @@ -188,10 +186,17 @@ { "omit_readlock", SQLITE_NoReadlock }, /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted ** flag if there are any active statements. */ { "read_uncommitted", SQLITE_ReadUncommitted }, + { "recursive_triggers", SQLITE_RecTriggers }, + + /* This flag may only be set if both foreign-key and trigger support + ** are present in the build. */ +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) + { "foreign_keys", SQLITE_ForeignKeys }, +#endif }; int i; const struct sPragmaType *p; for(i=0, p=aPragma; izName)==0 ){ @@ -201,14 +206,21 @@ assert( v!=0 ); /* Already allocated by sqlite3Pragma() */ if( ALWAYS(v) ){ if( zRight==0 ){ returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 ); }else{ + int mask = p->mask; /* Mask of bits to set or clear. */ + if( db->autoCommit==0 ){ + /* Foreign key support may not be enabled or disabled while not + ** in auto-commit mode. */ + mask &= ~(SQLITE_ForeignKeys); + } + if( getBoolean(zRight) ){ - db->flags |= p->mask; + db->flags |= mask; }else{ - db->flags &= ~p->mask; + db->flags &= ~mask; } /* Many of the flag-pragmas modify the code generated by the SQL ** compiler (eg. count_changes). So add an opcode to expire all ** compiled SQL statements after modifying a pragma value. @@ -225,21 +237,24 @@ #endif /* SQLITE_OMIT_FLAG_PRAGMAS */ /* ** Return a human-readable name for a constraint resolution action. */ +#ifndef SQLITE_OMIT_FOREIGN_KEY static const char *actionName(u8 action){ const char *zName; switch( action ){ - case OE_SetNull: zName = "SET NULL"; break; - case OE_SetDflt: zName = "SET DEFAULT"; break; - case OE_Cascade: zName = "CASCADE"; break; - default: zName = "RESTRICT"; - assert( action==OE_Restrict ); break; + case OE_SetNull: zName = "SET NULL"; break; + case OE_SetDflt: zName = "SET DEFAULT"; break; + case OE_Cascade: zName = "CASCADE"; break; + case OE_Restrict: zName = "RESTRICT"; break; + default: zName = "NO ACTION"; + assert( action==OE_None ); break; } return zName; } +#endif /* ** Process a pragma statement. ** ** Pragmas are of this form: @@ -316,16 +331,17 @@ ** synchronous setting. A negative value means synchronous is off ** and a positive value means synchronous is on. */ if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){ static const VdbeOpList getCacheSize[] = { - { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 0 */ - { OP_IfPos, 1, 6, 0}, + { OP_Transaction, 0, 0, 0}, /* 0 */ + { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ + { OP_IfPos, 1, 7, 0}, { OP_Integer, 0, 2, 0}, { OP_Subtract, 1, 2, 1}, - { OP_IfPos, 1, 6, 0}, - { OP_Integer, 0, 1, 0}, /* 5 */ + { OP_IfPos, 1, 7, 0}, + { OP_Integer, 0, 1, 0}, /* 6 */ { OP_ResultRow, 1, 1, 0}, }; int addr; if( sqlite3ReadSchema(pParse) ) goto pragma_out; sqlite3VdbeUsesBtree(v, iDb); @@ -333,11 +349,12 @@ sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC); pParse->nMem += 2; addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP1(v, addr+5, SQLITE_DEFAULT_CACHE_SIZE); + sqlite3VdbeChangeP1(v, addr+1, iDb); + sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ int size = atoi(zRight); if( size<0 ) size = -size; sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); @@ -960,12 +977,12 @@ sqlite3VdbeSetColName(v, 7, COLNAME_NAME, "match", SQLITE_STATIC); while(pFK){ int j; for(j=0; jnCol; j++){ char *zCol = pFK->aCol[j].zCol; - char *zOnUpdate = (char *)actionName(pFK->updateConf); - char *zOnDelete = (char *)actionName(pFK->deleteConf); + char *zOnDelete = (char *)actionName(pFK->aAction[0]); + char *zOnUpdate = (char *)actionName(pFK->aAction[1]); sqlite3VdbeAddOp2(v, OP_Integer, i, 1); sqlite3VdbeAddOp2(v, OP_Integer, j, 2); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0); sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, pTab->aCol[pFK->aCol[j].iFrom].zName, 0); @@ -1300,16 +1317,18 @@ sqlite3VdbeChangeP1(v, addr+2, iDb); sqlite3VdbeChangeP2(v, addr+2, iCookie); }else{ /* Read the specified cookie value */ static const VdbeOpList readCookie[] = { - { OP_ReadCookie, 0, 1, 0}, /* 0 */ + { OP_Transaction, 0, 0, 0}, /* 0 */ + { OP_ReadCookie, 0, 1, 0}, /* 1 */ { OP_ResultRow, 1, 1, 0} }; int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie); sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP3(v, addr, iCookie); + sqlite3VdbeChangeP1(v, addr+1, iDb); + sqlite3VdbeChangeP3(v, addr+1, iCookie); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT); } }else #endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */ Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** This file contains the implementation of the sqlite3_prepare() ** interface, and routines that contribute to loading the database schema ** from disk. -** -** $Id: prepare.c,v 1.125 2009/06/25 11:50:21 drh Exp $ */ #include "sqliteInt.h" /* ** Fill the InitData structure with an error message that indicates @@ -27,15 +25,15 @@ const char *zExtra /* Error information */ ){ sqlite3 *db = pData->db; if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){ if( zObj==0 ) zObj = "?"; - sqlite3SetString(pData->pzErrMsg, pData->db, - "malformed database schema (%s)", zObj); + sqlite3SetString(pData->pzErrMsg, db, + "malformed database schema (%s)", zObj); if( zExtra ){ - *pData->pzErrMsg = sqlite3MAppendf(pData->db, *pData->pzErrMsg, "%s - %s", - *pData->pzErrMsg, zExtra); + *pData->pzErrMsg = sqlite3MAppendf(db, *pData->pzErrMsg, + "%s - %s", *pData->pzErrMsg, zExtra); } } pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT; } @@ -78,19 +76,24 @@ char *zErr; int rc; assert( db->init.busy ); db->init.iDb = iDb; db->init.newTnum = atoi(argv[1]); + db->init.orphanTrigger = 0; rc = sqlite3_exec(db, argv[2], 0, 0, &zErr); db->init.iDb = 0; 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 && rc!=SQLITE_LOCKED ){ - corruptSchema(pData, argv[0], zErr); + if( db->init.orphanTrigger ){ + assert( iDb==1 ); + }else{ + pData->rc = rc; + if( rc==SQLITE_NOMEM ){ + db->mallocFailed = 1; + }else if( rc!=SQLITE_INTERRUPT && rc!=SQLITE_LOCKED ){ + corruptSchema(pData, argv[0], zErr); + } } sqlite3DbFree(db, zErr); } }else if( argv[0]==0 ){ corruptSchema(pData, 0, 0); @@ -126,19 +129,19 @@ ** indicate success or failure. */ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ int rc; int i; - BtCursor *curMain; int size; Table *pTab; Db *pDb; char const *azArg[4]; int meta[5]; InitData initData; char const *zMasterSchema; char const *zMasterName = SCHEMA_TABLE(iDb); + int openedTransaction = 0; /* ** The master database table has a structure like this */ static const char master_schema[] = @@ -208,18 +211,23 @@ if( !OMIT_TEMPDB && ALWAYS(iDb==1) ){ DbSetProperty(db, 1, DB_SchemaLoaded); } return SQLITE_OK; } - curMain = sqlite3MallocZero(sqlite3BtreeCursorSize()); - if( !curMain ){ - rc = SQLITE_NOMEM; - goto error_out; - } + + /* If there is not already a read-only (or read-write) transaction opened + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed before this function returns. */ sqlite3BtreeEnter(pDb->pBt); - rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, curMain); - if( rc==SQLITE_EMPTY ) rc = SQLITE_OK; + if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ + rc = sqlite3BtreeBeginTrans(pDb->pBt, 0); + if( rc!=SQLITE_OK ){ + sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); + goto initone_error_out; + } + openedTransaction = 1; + } /* Get the database meta information. ** ** Meta values are as follows: ** meta[0] Schema cookie. Changes with each schema change. @@ -234,16 +242,12 @@ ** meta[9] unused ** ** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to ** the possible values of meta[4]. */ - for(i=0; rc==SQLITE_OK && ipBt, i+1, (u32 *)&meta[i]); - } - if( rc ){ - sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); - goto initone_error_out; + for(i=0; ipBt, i+1, (u32 *)&meta[i]); } pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1]; /* If opening a non-empty database, check the text encoding. For the ** main database, set sqlite3.enc to the encoding of the main database. @@ -354,12 +358,13 @@ /* Jump here for an error that occurs after successfully allocating ** curMain and calling sqlite3BtreeEnter(). For an error that occurs ** before that point, jump to error_out. */ initone_error_out: - sqlite3BtreeCloseCursor(curMain); - sqlite3_free(curMain); + if( openedTransaction ){ + sqlite3BtreeCommit(pDb->pBt); + } sqlite3BtreeLeave(pDb->pBt); error_out: if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; @@ -433,47 +438,51 @@ } /* ** Check schema cookies in all databases. If any cookie is out -** of date, return 0. If all schema cookies are current, return 1. -*/ -static int schemaIsValid(sqlite3 *db){ - int iDb; - int rc; - BtCursor *curTemp; - int cookie; - int allOk = 1; - - curTemp = (BtCursor *)sqlite3Malloc(sqlite3BtreeCursorSize()); - if( curTemp ){ - assert( sqlite3_mutex_held(db->mutex) ); - for(iDb=0; allOk && iDbnDb; iDb++){ - Btree *pBt; - pBt = db->aDb[iDb].pBt; - if( pBt==0 ) continue; - memset(curTemp, 0, sqlite3BtreeCursorSize()); - rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, curTemp); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); - if( ALWAYS(rc==SQLITE_OK) - && cookie!=db->aDb[iDb].pSchema->schema_cookie ){ - allOk = 0; - } - sqlite3BtreeCloseCursor(curTemp); - } - if( NEVER(rc==SQLITE_NOMEM) || rc==SQLITE_IOERR_NOMEM ){ - db->mallocFailed = 1; - } - } - sqlite3_free(curTemp); - }else{ - allOk = 0; - db->mallocFailed = 1; - } - - return allOk; +** of date set pParse->rc to SQLITE_SCHEMA. If all schema cookies +** make no changes to pParse->rc. +*/ +static void schemaIsValid(Parse *pParse){ + sqlite3 *db = pParse->db; + int iDb; + int rc; + int cookie; + + assert( pParse->checkSchema ); + assert( sqlite3_mutex_held(db->mutex) ); + for(iDb=0; iDbnDb; iDb++){ + int openedTransaction = 0; /* True if a transaction is opened */ + Btree *pBt = db->aDb[iDb].pBt; /* Btree database to read cookie from */ + if( pBt==0 ) continue; + + /* If there is not already a read-only (or read-write) transaction opened + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed immediately after reading the meta-value. */ + if( !sqlite3BtreeIsInReadTrans(pBt) ){ + rc = sqlite3BtreeBeginTrans(pBt, 0); + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ + db->mallocFailed = 1; + } + if( rc!=SQLITE_OK ) return; + openedTransaction = 1; + } + + /* Read the schema cookie from the database. If it does not match the + ** value stored as part of the in the in-memory schema representation, + ** set Parse.rc to SQLITE_SCHEMA. */ + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); + if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ + pParse->rc = SQLITE_SCHEMA; + } + + /* Close the transaction, if one was opened. */ + if( openedTransaction ){ + sqlite3BtreeCommit(pBt); + } + } } /* ** Convert a schema pointer into the iDb index that indicates ** which database file in db->aDb[] the schema refers to. @@ -512,10 +521,11 @@ static int sqlite3Prepare( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ + Vdbe *pReprepare, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ Parse *pParse; /* Parsing context */ char *zErrMsg = 0; /* Error message */ @@ -526,10 +536,11 @@ pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ rc = SQLITE_NOMEM; goto end_prepare; } + pParse->pReprepare = pReprepare; if( sqlite3SafetyOn(db) ){ rc = SQLITE_MISUSE; goto end_prepare; } @@ -573,10 +584,11 @@ goto end_prepare; } } } + sqlite3VtabUnlockList(db); pParse->db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -602,12 +614,12 @@ if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; - if( pParse->checkSchema && !schemaIsValid(db) ){ - pParse->rc = SQLITE_SCHEMA; + if( pParse->checkSchema ){ + schemaIsValid(pParse); } if( pParse->rc==SQLITE_SCHEMA ){ sqlite3ResetInternalSchema(db, 0); } if( db->mallocFailed ){ @@ -661,10 +673,18 @@ sqlite3Error(db, rc, "%s", zErrMsg); sqlite3DbFree(db, zErrMsg); }else{ sqlite3Error(db, rc, 0); } + + /* Delete any TriggerPrg structures allocated while parsing this statement. */ + while( pParse->pTriggerPrg ){ + TriggerPrg *pT = pParse->pTriggerPrg; + pParse->pTriggerPrg = pT->pNext; + sqlite3VdbeProgramDelete(db, pT->pProgram, 0); + sqlite3DbFree(db, pT); + } end_prepare: sqlite3StackFree(db, pParse); rc = sqlite3ApiExit(db, rc); @@ -674,10 +694,11 @@ static int sqlite3LockAndPrepare( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ + Vdbe *pOld, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ int rc; assert( ppStmt!=0 ); @@ -685,14 +706,14 @@ if( !sqlite3SafetyCheckOk(db) ){ return SQLITE_MISUSE; } sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail); + rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); if( rc==SQLITE_SCHEMA ){ sqlite3_finalize(*ppStmt); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail); + rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); } sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); return rc; } @@ -714,11 +735,11 @@ assert( sqlite3_mutex_held(sqlite3VdbeDb(p)->mutex) ); zSql = sqlite3_sql((sqlite3_stmt *)p); assert( zSql!=0 ); /* Reprepare only called for prepare_v2() statements */ db = sqlite3VdbeDb(p); assert( sqlite3_mutex_held(db->mutex) ); - rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0); + rc = sqlite3LockAndPrepare(db, zSql, -1, 0, p, &pNew, 0); if( rc ){ if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; } assert( pNew==0 ); @@ -748,11 +769,11 @@ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,ppStmt,pzTail); + rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } int sqlite3_prepare_v2( sqlite3 *db, /* Database handle. */ @@ -760,11 +781,11 @@ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,ppStmt,pzTail); + rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } @@ -794,11 +815,11 @@ return SQLITE_MISUSE; } sqlite3_mutex_enter(db->mutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes); if( zSql8 ){ - rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8); + rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, 0, ppStmt, &zTail8); } if( zTail8 && pzTail ){ /* If sqlite3_prepare returns a tail pointer, we calculate the ** equivalent pointer into the UTF-16 string by counting the unicode Index: src/printf.c ================================================================== --- src/printf.c +++ src/printf.c @@ -3,12 +3,10 @@ ** the public domain. The original comments are included here for ** completeness. They are very out-of-date but might be useful as ** 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.104 2009/06/03 01:24:54 drh Exp $ -** ************************************************************************** ** ** The following modules is an enhanced replacement for the "printf" subroutines ** found in the standard C library. The following enhancements are ** supported: Index: src/random.c ================================================================== --- src/random.c +++ src/random.c @@ -12,12 +12,10 @@ ** This file contains code to implement a pseudo-random number ** generator (PRNG) for SQLite. ** ** Random numbers are used by some of the database backends in order ** to generate random integer keys for tables or random filenames. -** -** $Id: random.c,v 1.29 2008/12/10 19:26:24 drh Exp $ */ #include "sqliteInt.h" /* All threads share a single random number generator. Index: src/resolve.c ================================================================== --- src/resolve.c +++ src/resolve.c @@ -11,12 +11,10 @@ ************************************************************************* ** ** This file contains routines used for walking the parser tree and ** resolve all identifiers by associating them with a particular ** table and column. -** -** $Id: resolve.c,v 1.30 2009/06/15 23:15:59 drh Exp $ */ #include "sqliteInt.h" #include #include @@ -135,10 +133,11 @@ sqlite3 *db = pParse->db; /* The database connection */ struct SrcList_item *pItem; /* Use for looping over pSrcList items */ struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ + int isTrigger = 0; assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ assert( ~ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) ); @@ -220,46 +219,51 @@ #ifndef SQLITE_OMIT_TRIGGER /* If we have not already resolved the name, then maybe ** it is a new.* or old.* trigger argument reference */ - if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){ - TriggerStack *pTriggerStack = pParse->trigStack; + if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){ + int op = pParse->eTriggerOp; Table *pTab = 0; - u32 *piColMask = 0; - if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){ - pExpr->iTable = pTriggerStack->newIdx; - assert( pTriggerStack->pTab ); - pTab = pTriggerStack->pTab; - piColMask = &(pTriggerStack->newColMask); - }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){ - pExpr->iTable = pTriggerStack->oldIdx; - assert( pTriggerStack->pTab ); - pTab = pTriggerStack->pTab; - piColMask = &(pTriggerStack->oldColMask); + assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); + if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ + pExpr->iTable = 1; + pTab = pParse->pTriggerTab; + }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ + pExpr->iTable = 0; + pTab = pParse->pTriggerTab; } if( pTab ){ int iCol; - Column *pCol = pTab->aCol; - pSchema = pTab->pSchema; cntTab++; - for(iCol=0; iCol < pTab->nCol; iCol++, pCol++) { - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - cnt++; - pExpr->iColumn = iCol==pTab->iPKey ? -1 : (i16)iCol; - pExpr->pTab = pTab; + if( sqlite3IsRowid(zCol) ){ + iCol = -1; + }else{ + for(iCol=0; iColnCol; iCol++){ + Column *pCol = &pTab->aCol[iCol]; + if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ + if( iCol==pTab->iPKey ){ + iCol = -1; + } + break; + } + } + } + if( iColnCol ){ + cnt++; + if( iCol<0 ){ + pExpr->affinity = SQLITE_AFF_INTEGER; + }else if( pExpr->iTable==0 ){ testcase( iCol==31 ); testcase( iCol==32 ); - if( iCol>=32 ){ - *piColMask = 0xffffffff; - }else{ - *piColMask |= ((u32)1)<oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<iColumn = (i16)iCol; + pExpr->pTab = pTab; + isTrigger = 1; } } } #endif /* !defined(SQLITE_OMIT_TRIGGER) */ @@ -367,11 +371,11 @@ */ sqlite3ExprDelete(db, pExpr->pLeft); pExpr->pLeft = 0; sqlite3ExprDelete(db, pExpr->pRight); pExpr->pRight = 0; - pExpr->op = TK_COLUMN; + pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN); lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); /* Increment the nRef value on all name contexts from TopNC up to @@ -385,10 +389,31 @@ return WRC_Prune; } else { return WRC_Abort; } } + +/* +** Allocate and return a pointer to an expression to load the column iCol +** from datasource iSrc datasource in SrcList pSrc. +*/ +Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ + Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); + if( p ){ + struct SrcList_item *pItem = &pSrc->a[iSrc]; + p->pTab = pItem->pTab; + p->iTable = pItem->iCursor; + if( p->pTab->iPKey==iCol ){ + p->iColumn = -1; + }else{ + p->iColumn = (ynVar)iCol; + pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); + } + ExprSetProperty(p, EP_Resolved); + } + return p; +} /* ** This routine is callback for sqlite3WalkExpr(). ** ** Resolve symbolic names into TK_COLUMN operators for the current Index: src/rowset.c ================================================================== --- src/rowset.c +++ src/rowset.c @@ -57,12 +57,10 @@ ** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST ** primitives are constant time. The cost of DESTROY is O(N). ** ** There is an added cost of O(N) when switching between TEST and ** SMALLEST primitives. -** -** $Id: rowset.c,v 1.7 2009/05/22 01:00:13 drh Exp $ */ #include "sqliteInt.h" /* Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. -** -** $Id: select.c,v 1.524 2009/06/12 03:27:27 drh Exp $ */ #include "sqliteInt.h" /* @@ -190,55 +188,49 @@ } return -1; } /* -** Create an expression node for an identifier with the name of zName -*/ -Expr *sqlite3CreateIdExpr(Parse *pParse, const char *zName){ - return sqlite3Expr(pParse->db, TK_ID, zName); -} - -/* -** Add a term to the WHERE expression in *ppExpr that requires the -** zCol column to be equal in the two tables pTab1 and pTab2. +** This function is used to add terms implied by JOIN syntax to the +** WHERE clause expression of a SELECT statement. The new term, which +** is ANDed with the existing WHERE clause, is of the form: +** +** (tab1.col1 = tab2.col2) +** +** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the +** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is +** column iColRight of tab2. */ static void addWhereTerm( - Parse *pParse, /* Parsing context */ - const char *zCol, /* Name of the column */ - const Table *pTab1, /* First table */ - const char *zAlias1, /* Alias for first table. May be NULL */ - const Table *pTab2, /* Second table */ - const char *zAlias2, /* Alias for second table. May be NULL */ - int iRightJoinTable, /* VDBE cursor for the right table */ - Expr **ppExpr, /* Add the equality term to this expression */ - int isOuterJoin /* True if dealing with an OUTER join */ + Parse *pParse, /* Parsing context */ + SrcList *pSrc, /* List of tables in FROM clause */ + int iSrc, /* Index of first table to join in pSrc */ + int iColLeft, /* Index of column in first table */ + int iColRight, /* Index of column in second table */ + int isOuterJoin, /* True if this is an OUTER join */ + Expr **ppWhere /* IN/OUT: The WHERE clause to add to */ ){ - Expr *pE1a, *pE1b, *pE1c; - Expr *pE2a, *pE2b, *pE2c; - Expr *pE; - - pE1a = sqlite3CreateIdExpr(pParse, zCol); - pE2a = sqlite3CreateIdExpr(pParse, zCol); - if( zAlias1==0 ){ - zAlias1 = pTab1->zName; - } - pE1b = sqlite3CreateIdExpr(pParse, zAlias1); - if( zAlias2==0 ){ - zAlias2 = pTab2->zName; - } - pE2b = sqlite3CreateIdExpr(pParse, zAlias2); - pE1c = sqlite3PExpr(pParse, TK_DOT, pE1b, pE1a, 0); - pE2c = sqlite3PExpr(pParse, TK_DOT, pE2b, pE2a, 0); - pE = sqlite3PExpr(pParse, TK_EQ, pE1c, pE2c, 0); - if( pE && isOuterJoin ){ - ExprSetProperty(pE, EP_FromJoin); - assert( !ExprHasAnyProperty(pE, EP_TokenOnly|EP_Reduced) ); - ExprSetIrreducible(pE); - pE->iRightJoinTable = (i16)iRightJoinTable; - } - *ppExpr = sqlite3ExprAnd(pParse->db,*ppExpr, pE); + sqlite3 *db = pParse->db; + Expr *pE1; + Expr *pE2; + Expr *pEq; + + assert( pSrc->nSrc>(iSrc+1) ); + assert( pSrc->a[iSrc].pTab ); + assert( pSrc->a[iSrc+1].pTab ); + + pE1 = sqlite3CreateColumnExpr(db, pSrc, iSrc, iColLeft); + pE2 = sqlite3CreateColumnExpr(db, pSrc, iSrc+1, iColRight); + + pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2, 0); + if( pEq && isOuterJoin ){ + ExprSetProperty(pEq, EP_FromJoin); + assert( !ExprHasAnyProperty(pEq, EP_TokenOnly|EP_Reduced) ); + ExprSetIrreducible(pEq); + pEq->iRightJoinTable = (i16)pE2->iTable; + } + *ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq); } /* ** Set the EP_FromJoin property on all terms of the given expression. ** And set the Expr.iRightJoinTable to iTable for every term in the @@ -316,15 +308,13 @@ "an ON or USING clause", 0); return 1; } for(j=0; jnCol; j++){ char *zName = pLeftTab->aCol[j].zName; - if( columnIndex(pRightTab, zName)>=0 ){ - addWhereTerm(pParse, zName, pLeftTab, pLeft->zAlias, - pRightTab, pRight->zAlias, - pRight->iCursor, &p->pWhere, isOuter); - + int iRightCol = columnIndex(pRightTab, zName); + if( iRightCol>=0 ){ + addWhereTerm(pParse, pSrc, i, j, iRightCol, isOuter, &p->pWhere); } } } /* Disallow both ON and USING clauses in the same join @@ -353,18 +343,18 @@ */ if( pRight->pUsing ){ IdList *pList = pRight->pUsing; for(j=0; jnId; j++){ char *zName = pList->a[j].zName; - if( columnIndex(pLeftTab, zName)<0 || columnIndex(pRightTab, zName)<0 ){ + int iLeftCol = columnIndex(pLeftTab, zName); + int iRightCol = columnIndex(pRightTab, zName); + if( iLeftCol<0 || iRightCol<0 ){ sqlite3ErrorMsg(pParse, "cannot join using column %s - column " "not present in both tables", zName); return 1; } - addWhereTerm(pParse, zName, pLeftTab, pLeft->zAlias, - pRightTab, pRight->zAlias, - pRight->iCursor, &p->pWhere, isOuter); + addWhereTerm(pParse, pSrc, i, iLeftCol, iRightCol, isOuter, &p->pWhere); } } } return 0; } @@ -764,18 +754,20 @@ int regRow; int regRowid; iTab = pOrderBy->iECursor; + regRow = sqlite3GetTempReg(pParse); if( eDest==SRT_Output || eDest==SRT_Coroutine ){ pseudoTab = pParse->nTab++; - sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output, nColumn); + sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn); + regRowid = 0; + }else{ + regRowid = sqlite3GetTempReg(pParse); } addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); codeOffset(v, p, addrContinue); - regRow = sqlite3GetTempReg(pParse); - regRowid = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow); switch( eDest ){ case SRT_Table: case SRT_EphemTab: { testcase( eDest==SRT_Table ); @@ -803,15 +795,16 @@ default: { int i; assert( eDest==SRT_Output || eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); testcase( eDest==SRT_Coroutine ); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, pseudoTab, regRow, regRowid); for(i=0; iiMem+i ); sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i); + if( i==0 ){ + sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); + } } if( eDest==SRT_Output ){ sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn); sqlite3ExprCacheAffinityChange(pParse, pDest->iMem, nColumn); }else{ @@ -891,25 +884,31 @@ pNC = pNC->pNext; } } if( pTab==0 ){ - /* FIX ME: - ** This can occurs if you have something like "SELECT new.x;" inside - ** a trigger. In other words, if you reference the special "new" - ** table in the result set of a select. We do not have a good way - ** to find the actual table type, so call it "TEXT". This is really - ** something of a bug, but I do not know how to fix it. + /* At one time, code such as "SELECT new.x" within a trigger would + ** cause this condition to run. Since then, we have restructured how + ** trigger code is generated and so this condition is no longer + ** possible. However, it can still be true for statements like + ** the following: + ** + ** CREATE TABLE t1(col INTEGER); + ** SELECT (SELECT t1.col) FROM FROM t1; + ** + ** when columnType() is called on the expression "t1.col" in the + ** sub-select. In this case, set the column type to NULL, even + ** though it should really be "INTEGER". ** - ** This code does not produce the correct answer - it just prevents - ** a segfault. See ticket #1229. - */ - zType = "TEXT"; + ** This is not a problem, as the column type of "t1.col" is never + ** used. When columnType() is called on the expression + ** "(SELECT t1.col)", the correct type is returned (see the TK_SELECT + ** branch below. */ break; } - assert( pTab ); + assert( pTab && pExpr->pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ @@ -919,11 +918,11 @@ ** test case misc2.2.2) - it always evaluates to NULL. */ NameContext sNC; Expr *p = pS->pEList->a[iCol].pExpr; sNC.pSrcList = pS->pSrc; - sNC.pNext = 0; + sNC.pNext = pNC; sNC.pParse = pNC->pParse; zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol); } }else if( ALWAYS(pTab->pSchema) ){ /* A real table */ @@ -2353,10 +2352,13 @@ }else{ Expr *pNew; assert( pEList!=0 && pExpr->iColumnnExpr ); assert( pExpr->pLeft==0 && pExpr->pRight==0 ); pNew = sqlite3ExprDup(db, pEList->a[pExpr->iColumn].pExpr, 0); + if( pNew && pExpr->pColl ){ + pNew->pColl = pExpr->pColl; + } sqlite3ExprDelete(db, pExpr); pExpr = pNew; } }else{ pExpr->pLeft = substExpr(db, pExpr->pLeft, iTable, pEList); @@ -2729,12 +2731,13 @@ ** pSubitem->pTab is always non-NULL by test restrictions and tests above. */ if( ALWAYS(pSubitem->pTab!=0) ){ Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nRef==1 ){ - pTabToDel->pNextZombie = pParse->pZombieTab; - pParse->pZombieTab = pTabToDel; + Parse *pToplevel = sqlite3ParseToplevel(pParse); + pTabToDel->pNextZombie = pToplevel->pZombieTab; + pToplevel->pZombieTab = pTabToDel; }else{ pTabToDel->nRef--; } pSubitem->pTab = 0; } Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. -** -** $Id: shell.c,v 1.210 2009/05/31 17:16:10 drh Exp $ */ #if defined(_WIN32) || defined(WIN32) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif @@ -104,13 +102,90 @@ printf("CPU Time: user %f sys %f\n", timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); } } + #define BEGIN_TIMER beginTimer() #define END_TIMER endTimer() #define HAS_TIMER 1 + +#elif (defined(_WIN32) || defined(WIN32)) + +#include + +/* Saved resource information for the beginning of an operation */ +static HANDLE hProcess; +static FILETIME ftKernelBegin; +static FILETIME ftUserBegin; +typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); +static GETPROCTIMES getProcessTimesAddr = NULL; + +/* True if the timer is enabled */ +static int enableTimer = 0; + +/* +** Check to see if we have timer support. Return 1 if necessary +** support found (or found previously). +*/ +static int hasTimer(void){ + if( getProcessTimesAddr ){ + return 1; + } else { + /* GetProcessTimes() isn't supported in WIN95 and some other Windows versions. + ** See if the version we are running on has it, and if it does, save off + ** a pointer to it and the current process handle. + */ + hProcess = GetCurrentProcess(); + if( hProcess ){ + HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); + if( NULL != hinstLib ){ + getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); + if( NULL != getProcessTimesAddr ){ + return 1; + } + FreeLibrary(hinstLib); + } + } + } + return 0; +} + +/* +** Begin timing an operation +*/ +static void beginTimer(void){ + if( enableTimer && getProcessTimesAddr ){ + FILETIME ftCreation, ftExit; + getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin); + } +} + +/* Return the difference of two FILETIME structs in seconds */ +static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ + sqlite_int64 i64Start = *((sqlite_int64 *) pStart); + sqlite_int64 i64End = *((sqlite_int64 *) pEnd); + return (double) ((i64End - i64Start) / 10000000.0); +} + +/* +** Print the timing results. +*/ +static void endTimer(void){ + if( enableTimer && getProcessTimesAddr){ + FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; + getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd); + printf("CPU Time: user %f sys %f\n", + timeDiff(&ftUserBegin, &ftUserEnd), + timeDiff(&ftKernelBegin, &ftKernelEnd)); + } +} + +#define BEGIN_TIMER beginTimer() +#define END_TIMER endTimer() +#define HAS_TIMER hasTimer() + #else #define BEGIN_TIMER #define END_TIMER #define HAS_TIMER 0 #endif @@ -195,11 +270,11 @@ "triggername," /* Name of trigger */ \ "dummy" /* Unused */ \ ")" typedef struct SchemaTable SchemaTable; -struct SchemaTable { +static struct SchemaTable { const char *zName; const char *zObject; const char *zPragma; const char *zSchema; } aSchemaTable[] = { @@ -652,11 +727,11 @@ UNUSED_PARAMETER(nArg); UNUSED_PARAMETER(azCol); return pCb->xData(pCb->pCtx, pCb->eType, azArg[0]); } -int detectSchemaProblem( +static int detectSchemaProblem( sqlite3 *db, /* Database connection */ const char *zMessage, /* English language error message */ const char *zSql, /* SQL statement to run */ GenfkeyCb *pCb ){ @@ -849,11 +924,11 @@ " EXISTS (SELECT 1 FROM /tbl/ WHERE /cond2/)\n" "BEGIN\n" " /delete_action/\n" "END;\n" - /* The "BEFORE DELETE ON " trigger. This trigger's job + /* The "AFTER UPDATE ON " 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. */ @@ -874,12 +949,12 @@ ", '/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, ', ')" + ", '/fkey_list/', sj(dq(to_col), ', ')" + ", '/rkey_list/', sj(dq(from_col), ', ')" ", '/cond1/', sj(multireplace('new./from/ == /to/'" ", '/from/', dq(from_col)" ", '/to/', dq(to_col)" "), ' AND ')" @@ -889,13 +964,13 @@ "), ' AND ')" ", '/update_action/', CASE on_update " "WHEN 'SET NULL' THEN " "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(from_col||' = NULL',', ')" + ", '/setlist/', sj(dq(from_col)||' = NULL',', ')" ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')" + ", '/where/', sj(dq(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)" @@ -906,13 +981,13 @@ "END " ", '/delete_action/', CASE on_delete " "WHEN 'SET NULL' THEN " "multireplace('UPDATE /tbl/ SET /setlist/ WHERE /where/;' " - ", '/setlist/', sj(from_col||' = NULL',', ')" + ", '/setlist/', sj(dq(from_col)||' = NULL',', ')" ", '/tbl/', dq(from_tbl)" - ", '/where/', sj(from_col||' = old.'||dq(to_col),' AND ')" + ", '/where/', sj(dq(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 ')" @@ -1180,11 +1255,11 @@ ** An pointer to an instance of this structure is passed from ** the main program to the callback. This is used to communicate ** state and mode information. */ struct callback_data { - sqlite3 *db; /* The database */ + sqlite3 *db; /* The database */ int echoOn; /* True to echo input commands */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ int mode; /* An output mode setting */ int writableSchema; /* True if PRAGMA writable_schema=ON */ @@ -1198,10 +1273,11 @@ struct previous_mode_data explainPrev; /* Holds the mode information just before ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ + sqlite3_stmt *pStmt; /* Current statement if any. */ }; /* ** These are the allowed modes. */ @@ -1239,10 +1315,21 @@ static int strlen30(const char *z){ const char *z2 = z; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } + +/* +** Output the given string as a hex-encoded blob (eg. X'1234' ) +*/ +static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ + int i; + char *zBlob = (char *)pBlob; + fprintf(out,"X'"); + for(i=0; i0 ){ fprintf(out,"%.*s",i,z); } if( z[i]=='<' ){ fprintf(out,"<"); }else if( z[i]=='&' ){ fprintf(out,"&"); + }else if( z[i]=='>' ){ + fprintf(out,">"); + }else if( z[i]=='\"' ){ + fprintf(out,"""); + }else if( z[i]=='\'' ){ + fprintf(out,"'"); }else{ break; } z += i + 1; } @@ -1392,16 +1491,21 @@ if( db ) sqlite3_interrupt(db); } #endif /* -** This is the callback routine that the SQLite library +** This is the callback routine that the shell ** invokes for each row of a query result. */ -static int callback(void *pArg, int nArg, char **azArg, char **azCol){ +static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int *aiType){ int i; struct callback_data *p = (struct callback_data*)pArg; + + if( p->echoOn && p->cnt==0 && p->pStmt){ + printf("%s\n", sqlite3_sql(p->pStmt)); + } + switch( p->mode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; for(i=0; icnt++==0 && p->showHeader ){ fprintf(p->out,""); for(i=0; iout,"%s",azCol[i]); + fprintf(p->out,""); + output_html_string(p->out, azCol[i]); + fprintf(p->out,"\n"); } fprintf(p->out,"\n"); } if( azArg==0 ) break; fprintf(p->out,""); @@ -1538,16 +1644,27 @@ } fprintf(p->out,"\n"); break; } case MODE_Insert: { + p->cnt++; if( azArg==0 ) break; fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable); for(i=0; i0 ? ",": ""; - if( azArg[i]==0 ){ + if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ fprintf(p->out,"%sNULL",zSep); + }else if( aiType && aiType[i]==SQLITE_TEXT ){ + if( zSep[0] ) fprintf(p->out,"%s",zSep); + output_quoted_string(p->out, azArg[i]); + }else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){ + fprintf(p->out,"%s%s",zSep, azArg[i]); + }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ + const void *pBlob = sqlite3_column_blob(p->pStmt, i); + int nBlob = sqlite3_column_bytes(p->pStmt, i); + if( zSep[0] ) fprintf(p->out,"%s",zSep); + output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ fprintf(p->out,"%s%s",zSep, azArg[i]); }else{ if( zSep[0] ) fprintf(p->out,"%s",zSep); output_quoted_string(p->out, azArg[i]); @@ -1557,10 +1674,19 @@ break; } } return 0; } + +/* +** This is the callback routine that the SQLite library +** invokes for each row of a query result. +*/ +static int callback(void *pArg, int nArg, char **azArg, char **azCol){ + /* since we don't have type info, call the shell_callback with a NULL value */ + return shell_callback(pArg, nArg, azArg, azCol, NULL); +} /* ** Set the destination table field of the callback_data structure to ** the name of the table given. Escape any quote characters in the ** table name. @@ -1583,11 +1709,11 @@ } } if( needQuote ) n += 2; z = p->zDestTable = malloc( n+1 ); if( z==0 ){ - fprintf(stderr,"Out of memory!\n"); + fprintf(stderr,"Error: out of memory\n"); exit(1); } n = 0; if( needQuote ) z[n++] = '\''; for(i=0; zName[i]; i++){ @@ -1672,10 +1798,154 @@ fprintf(out, "%s;\n", sqlite3_column_text(pSelect, 0)); rc = sqlite3_step(pSelect); } return sqlite3_finalize(pSelect); } + +/* +** Allocate space and save off current error string. +*/ +static char *save_err_msg( + sqlite3 *db /* Database to query */ +){ + int nErrMsg = 1+strlen30(sqlite3_errmsg(db)); + char *zErrMsg = sqlite3_malloc(nErrMsg); + if( zErrMsg ){ + memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg); + } + return zErrMsg; +} + +/* +** Execute a statement or set of statements. Print +** any result rows/columns depending on the current mode +** set via the supplied callback. +** +** This is very similar to SQLite's built-in sqlite3_exec() +** function except it takes a slightly different callback +** and callback data argument. +*/ +static int shell_exec( + sqlite3 *db, /* An open database */ + const char *zSql, /* SQL to be evaluated */ + int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */ + /* (not the same as sqlite3_exec) */ + struct callback_data *pArg, /* Pointer to struct callback_data */ + char **pzErrMsg /* Error msg written here */ +){ + sqlite3_stmt *pStmt = NULL; + int rc = SQLITE_OK; + int rc2; + const char *zLeftover; /* Tail of unprocessed SQL */ + + if( pzErrMsg ){ + *pzErrMsg = NULL; + } + + while( zSql[0] && (SQLITE_OK == rc) ){ + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); + if( SQLITE_OK != rc ){ + if( pzErrMsg ){ + *pzErrMsg = save_err_msg(db); + } + }else{ + if( !pStmt ){ + /* this happens for a comment or white-space */ + zSql = zLeftover; + while( isspace(zSql[0]) ) zSql++; + continue; + } + + /* perform the first step. this will tell us if we + ** have a result set or not and how wide it is. + */ + rc = sqlite3_step(pStmt); + /* if we have a result set... */ + if( SQLITE_ROW == rc ){ + /* if we have a callback... */ + if( xCallback ){ + /* allocate space for col name ptr, value ptr, and type */ + int nCol = sqlite3_column_count(pStmt); + void *pData = sqlite3_malloc(3*nCol*sizeof(const char*) + 1); + if( !pData ){ + rc = SQLITE_NOMEM; + }else{ + char **azCols = (char **)pData; /* Names of result columns */ + char **azVals = &azCols[nCol]; /* Results */ + int *aiTypes = (int *)&azVals[nCol]; /* Result types */ + int i; + assert(sizeof(int) <= sizeof(char *)); + /* save off ptrs to column names */ + for(i=0; ipStmt = pStmt; + pArg->cnt = 0; + } + do{ + /* extract the data and data types */ + for(i=0; ipStmt = NULL; + } + } + }else{ + do{ + rc = sqlite3_step(pStmt); + } while( rc == SQLITE_ROW ); + } + } + + /* if the last sqlite3_step() didn't complete successfully... */ + if( (SQLITE_OK != rc) && (SQLITE_DONE != rc) ){ + if( pzErrMsg ){ + *pzErrMsg = save_err_msg(db); + } + }else{ + rc = SQLITE_OK; + } + + rc2 = sqlite3_finalize(pStmt); + /* if the last sqlite3_finalize() didn't complete successfully + ** AND we don't have a saved error from sqlite3_step ... */ + if( (SQLITE_OK != rc2) && (SQLITE_OK == rc) ){ + rc = rc2; + if( pzErrMsg ){ + *pzErrMsg = save_err_msg(db); + } + } + + if( SQLITE_OK == rc ){ + zSql = zLeftover; + while( isspace(zSql[0]) ) zSql++; + } + } + } /* end while */ + + return rc; +} /* ** This is a different callback routine used for dumping the database. ** Each row received by this callback consists of a table name, @@ -1862,13 +2132,16 @@ static char zHelp[] = ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail ON|OFF Stop after hitting an error. Default OFF\n" ".databases List names and files of attached databases\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" + " If TABLE specified, only dump tables matching\n" + " LIKE pattern TABLE.\n" ".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" + ".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n" + " With no args, it turns EXPLAIN on.\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" @@ -1876,11 +2149,13 @@ " 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" - ".indices TABLE Show names of all indices on TABLE\n" + ".indices ?TABLE? Show names of all indices\n" + " If TABLE specified, only show indices for tables\n" + " matching LIKE pattern TABLE.\n" #ifdef SQLITE_ENABLE_IOTRACE ".iotrace FILE Enable I/O diagnostic logging to FILE\n" #endif #ifndef SQLITE_OMIT_LOAD_EXTENSION ".load FILE ?ENTRY? Load an extension library\n" @@ -1900,18 +2175,23 @@ ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".schema ?TABLE? Show the CREATE statements\n" + " If TABLE specified, only show tables matching\n" + " LIKE pattern TABLE.\n" ".separator STRING Change separator used by output mode and .import\n" ".show Show the current values for various settings\n" - ".tables ?PATTERN? List names of tables matching a LIKE pattern\n" + ".tables ?TABLE? List names of tables\n" + " If TABLE specified, only list tables matching\n" + " LIKE pattern TABLE.\n" ".timeout MS Try opening locked tables for MS milliseconds\n" -#if HAS_TIMER + ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" +; + +static char zTimerHelp[] = ".timer ON|OFF Turn the CPU timer measurement on or off\n" -#endif - ".width NUM NUM ... Set column widths for \"column\" mode\n" ; /* Forward reference */ static int process_input(struct callback_data *p, FILE *in); @@ -1926,11 +2206,11 @@ if( db && sqlite3_errcode(db)==SQLITE_OK ){ sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0, shellstaticFunc, 0, 0); } if( db==0 || SQLITE_OK!=sqlite3_errcode(db) ){ - fprintf(stderr,"Unable to open database \"%s\": %s\n", + fprintf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite3_errmsg(db)); exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); @@ -2027,29 +2307,28 @@ } } /* Process the input line. */ - if( nArg==0 ) return rc; + if( nArg==0 ) return 0; /* no tokens, no error */ n = strlen30(azArg[0]); c = azArg[0][0]; - if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 ){ + if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 && nArg<4){ const char *zDestFile; const char *zDb; sqlite3 *pDest; sqlite3_backup *pBackup; - int rc; if( nArg==2 ){ zDestFile = azArg[1]; zDb = "main"; }else{ zDestFile = azArg[2]; zDb = azArg[1]; } rc = sqlite3_open(zDestFile, &pDest); if( rc!=SQLITE_OK ){ - fprintf(stderr, "Error: cannot open %s\n", zDestFile); + fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile); sqlite3_close(pDest); return 1; } open_db(p); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); @@ -2059,22 +2338,23 @@ return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; + rc = 0; }else{ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); + rc = 1; } sqlite3_close(pDest); }else - if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 ){ + if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){ bail_on_error = booleanValue(azArg[1]); }else - if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){ + if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); memcpy(&data, p, sizeof(data)); data.showHeader = 1; @@ -2085,16 +2365,21 @@ data.cnt = 0; sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg); if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); + rc = 1; } }else - if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ + if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){ char *zErrMsg = 0; open_db(p); + /* When playing back a "dump", the content might appear in an order + ** which causes immediate foreign key constraints to be violated. + ** So disable foreign-key constraint enforcement to prevent problems. */ + fprintf(p->out, "PRAGMA foreign_keys=OFF;\n"); fprintf(p->out, "BEGIN TRANSACTION;\n"); p->writableSchema = 0; sqlite3_exec(p->db, "PRAGMA writable_schema=ON", 0, 0, 0); if( nArg==1 ){ run_schema_dump_query(p, @@ -2137,19 +2422,19 @@ }else{ fprintf(p->out, "COMMIT;\n"); } }else - if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){ + if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){ p->echoOn = booleanValue(azArg[1]); }else - if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ + if( c=='e' && strncmp(azArg[0], "exit", n)==0 && nArg==1 ){ rc = 2; }else - if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ + if( c=='e' && strncmp(azArg[0], "explain", n)==0 && nArg<3 ){ int val = nArg>=2 ? booleanValue(azArg[1]) : 1; if(val == 1) { if(!p->explainPrev.valid) { p->explainPrev.valid = 1; p->explainPrev.mode = p->mode; @@ -2192,23 +2477,25 @@ } }else #endif if( c=='h' && (strncmp(azArg[0], "header", n)==0 || - strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){ + strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){ p->showHeader = booleanValue(azArg[1]); }else if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ fprintf(stderr,"%s",zHelp); + if( HAS_TIMER ){ + fprintf(stderr,"%s",zTimerHelp); + } }else - if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg>=3 ){ + if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){ char *zTable = azArg[2]; /* Insert data into this table */ char *zFile = azArg[1]; /* The file from which to extract data */ - sqlite3_stmt *pStmt; /* A statement */ - int rc; /* Result code */ + sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int nSep; /* Number of bytes in p->separator[] */ char *zSql; /* An SQL statement */ @@ -2219,29 +2506,35 @@ int lineno = 0; /* Line number of input file */ open_db(p); nSep = strlen30(p->separator); if( nSep==0 ){ - fprintf(stderr, "non-null separator required for import\n"); - return 0; + fprintf(stderr, "Error: non-null separator required for import\n"); + return 1; } zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable); - if( zSql==0 ) return 0; + if( zSql==0 ){ + fprintf(stderr, "Error: out of memory\n"); + return 1; + } nByte = strlen30(zSql); rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ + if (pStmt) sqlite3_finalize(pStmt); fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); - nCol = 0; - rc = 1; - }else{ - nCol = sqlite3_column_count(pStmt); + return 1; } + nCol = sqlite3_column_count(pStmt); sqlite3_finalize(pStmt); - if( nCol==0 ) return 0; + pStmt = 0; + if( nCol==0 ) return 0; /* no columns, no error */ zSql = malloc( nByte + 20 + nCol*2 ); - if( zSql==0 ) return 0; + if( zSql==0 ){ + fprintf(stderr, "Error: out of memory\n"); + return 1; + } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable); j = strlen30(zSql); for(i=1; idb, zSql, -1, &pStmt, 0); free(zSql); if( rc ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); - sqlite3_finalize(pStmt); + if (pStmt) sqlite3_finalize(pStmt); return 1; } in = fopen(zFile, "rb"); if( in==0 ){ - fprintf(stderr, "cannot open file: %s\n", zFile); + fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); sqlite3_finalize(pStmt); - return 0; + return 1; } azCol = malloc( sizeof(azCol[0])*(nCol+1) ); if( azCol==0 ){ + fprintf(stderr, "Error: out of memory\n"); fclose(in); - return 0; + sqlite3_finalize(pStmt); + return 1; } sqlite3_exec(p->db, "BEGIN", 0, 0, 0); zCommit = "COMMIT"; while( (zLine = local_getline(0, in))!=0 ){ char *z; @@ -2280,18 +2575,20 @@ if( idb, zCommit, 0, 0, 0); }else - if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){ + if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.mode = MODE_List; - zShellStatic = azArg[1]; - sqlite3_exec(p->db, - "SELECT name FROM sqlite_master " - "WHERE type='index' AND tbl_name LIKE shellstatic() " - "UNION ALL " - "SELECT name FROM sqlite_temp_master " - "WHERE type='index' AND tbl_name LIKE shellstatic() " - "ORDER BY 1", - callback, &data, &zErrMsg - ); - zShellStatic = 0; + if( nArg==1 ){ + rc = sqlite3_exec(p->db, + "SELECT name FROM sqlite_master " + "WHERE type='index' AND name NOT LIKE 'sqlite_%' " + "UNION ALL " + "SELECT name FROM sqlite_temp_master " + "WHERE type='index' " + "ORDER BY 1", + callback, &data, &zErrMsg + ); + }else{ + zShellStatic = azArg[1]; + rc = sqlite3_exec(p->db, + "SELECT name FROM sqlite_master " + "WHERE type='index' AND tbl_name LIKE shellstatic() " + "UNION ALL " + "SELECT name FROM sqlite_temp_master " + "WHERE type='index' AND tbl_name LIKE shellstatic() " + "ORDER BY 1", + callback, &data, &zErrMsg + ); + zShellStatic = 0; + } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); + rc = 1; + }else if( rc != SQLITE_OK ){ + fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n"); + rc = 1; } }else #ifdef SQLITE_ENABLE_IOTRACE if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){ @@ -2345,12 +2658,13 @@ sqlite3IoTrace = iotracePrintf; iotrace = stdout; }else{ iotrace = fopen(azArg[1], "w"); if( iotrace==0 ){ - fprintf(stderr, "cannot open \"%s\"\n", azArg[1]); + fprintf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); sqlite3IoTrace = 0; + rc = 1; }else{ sqlite3IoTrace = iotracePrintf; } } }else @@ -2358,55 +2672,63 @@ #ifndef SQLITE_OMIT_LOAD_EXTENSION if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){ const char *zFile, *zProc; char *zErrMsg = 0; - int rc; zFile = azArg[1]; zProc = nArg>=3 ? azArg[2] : 0; open_db(p); rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg); if( rc!=SQLITE_OK ){ - fprintf(stderr, "%s\n", zErrMsg); + fprintf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif - if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){ - int n2 = strlen30(azArg[1]); - if( strncmp(azArg[1],"line",n2)==0 - || - strncmp(azArg[1],"lines",n2)==0 ){ - p->mode = MODE_Line; - }else if( strncmp(azArg[1],"column",n2)==0 - || - strncmp(azArg[1],"columns",n2)==0 ){ - p->mode = MODE_Column; - }else if( strncmp(azArg[1],"list",n2)==0 ){ - p->mode = MODE_List; - }else if( strncmp(azArg[1],"html",n2)==0 ){ - p->mode = MODE_Html; - }else if( strncmp(azArg[1],"tcl",n2)==0 ){ - p->mode = MODE_Tcl; - }else if( strncmp(azArg[1],"csv",n2)==0 ){ + if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){ + int n2 = strlen30(azArg[1]); + if( (n2==4 && strncmp(azArg[1],"line",n2)==0) + || + (n2==5 && strncmp(azArg[1],"lines",n2)==0) ){ + p->mode = MODE_Line; + }else if( (n2==6 && strncmp(azArg[1],"column",n2)==0) + || + (n2==7 && strncmp(azArg[1],"columns",n2)==0) ){ + p->mode = MODE_Column; + }else if( n2==4 && strncmp(azArg[1],"list",n2)==0 ){ + p->mode = MODE_List; + }else if( n2==4 && strncmp(azArg[1],"html",n2)==0 ){ + p->mode = MODE_Html; + }else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){ + p->mode = MODE_Tcl; + }else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->separator), p->separator, ","); - }else if( strncmp(azArg[1],"tabs",n2)==0 ){ + }else if( n2==4 && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); - }else if( strncmp(azArg[1],"insert",n2)==0 ){ + }else if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; - if( nArg>=3 ){ - set_table_name(p, azArg[2]); - }else{ - set_table_name(p, "table"); - } + set_table_name(p, "table"); }else { - fprintf(stderr,"mode should be one of: " + fprintf(stderr,"Error: mode should be one of: " "column csv html insert line list tabs tcl\n"); + rc = 1; + } + }else + + if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==3 ){ + int n2 = strlen30(azArg[1]); + if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){ + p->mode = MODE_Insert; + set_table_name(p, azArg[2]); + }else { + fprintf(stderr, "Error: invalid arguments: " + " \"%s\". Enter \".help\" for help\n", azArg[2]); + rc = 1; } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) { sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, @@ -2421,12 +2743,13 @@ p->out = stdout; sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout"); }else{ p->out = fopen(azArg[1], "wb"); if( p->out==0 ){ - fprintf(stderr,"can't write to \"%s\"\n", azArg[1]); + fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]); p->out = stdout; + rc = 1; } else { sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]); } } }else @@ -2438,30 +2761,30 @@ if( nArg >= 3) { strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else - if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ + if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){ rc = 2; }else if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){ FILE *alt = fopen(azArg[1], "rb"); if( alt==0 ){ - fprintf(stderr,"can't open \"%s\"\n", azArg[1]); + fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); + rc = 1; }else{ - process_input(p, alt); + rc = process_input(p, alt); fclose(alt); } }else - if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 ){ + if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){ const char *zSrcFile; const char *zDb; sqlite3 *pSrc; sqlite3_backup *pBackup; - int rc; int nTimeout = 0; if( nArg==2 ){ zSrcFile = azArg[1]; zDb = "main"; @@ -2469,11 +2792,11 @@ zSrcFile = azArg[2]; zDb = azArg[1]; } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ - fprintf(stderr, "Error: cannot open %s\n", zSrcFile); + fprintf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); sqlite3_close(pSrc); return 1; } open_db(p); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); @@ -2489,20 +2812,22 @@ sqlite3_sleep(100); } } sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; + rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - fprintf(stderr, "source database is busy\n"); + fprintf(stderr, "Error: source database is busy\n"); + rc = 1; }else{ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); + rc = 1; } sqlite3_close(pSrc); }else - if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ + if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){ struct callback_data data; char *zErrMsg = 0; open_db(p); memcpy(&data, p, sizeof(data)); data.showHeader = 0; @@ -2521,10 +2846,11 @@ ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); + rc = SQLITE_OK; }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n" " type text,\n" " name text,\n" @@ -2534,13 +2860,14 @@ ")"; new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; callback(&data, 1, new_argv, new_colv); + rc = SQLITE_OK; }else{ zShellStatic = azArg[1]; - sqlite3_exec(p->db, + rc = sqlite3_exec(p->db, "SELECT sql FROM " " (SELECT sql sql, type type, tbl_name tbl_name, name name" " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " "WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL " @@ -2547,11 +2874,11 @@ "ORDER BY substr(type,2,1), name", callback, &data, &zErrMsg); zShellStatic = 0; } }else{ - sqlite3_exec(p->db, + rc = sqlite3_exec(p->db, "SELECT sql FROM " " (SELECT sql sql, type type, tbl_name tbl_name, name name" " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" @@ -2560,19 +2887,25 @@ ); } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); + rc = 1; + }else if( rc != SQLITE_OK ){ + fprintf(stderr,"Error: querying schema information\n"); + rc = 1; + }else{ + rc = 0; } }else if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){ sqlite3_snprintf(sizeof(p->separator), p->separator, "%.*s", (int)sizeof(p->separator)-1, azArg[1]); }else - if( c=='s' && strncmp(azArg[0], "show", n)==0){ + if( c=='s' && strncmp(azArg[0], "show", n)==0 && nArg==1 ){ int i; fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off"); fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); @@ -2589,19 +2922,19 @@ fprintf(p->out,"%d ",p->colWidth[i]); } fprintf(p->out,"\n"); }else - if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){ + if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){ char **azResult; - int nRow, rc; + int nRow; char *zErrMsg; open_db(p); if( nArg==1 ){ rc = sqlite3_get_table(p->db, "SELECT name FROM sqlite_master " - "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'" + "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' " "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type IN ('table','view') " "ORDER BY 1", &azResult, &nRow, 0, &zErrMsg @@ -2608,24 +2941,27 @@ ); }else{ zShellStatic = azArg[1]; rc = sqlite3_get_table(p->db, "SELECT name FROM sqlite_master " - "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' " + "WHERE type IN ('table','view') AND name LIKE shellstatic() " "UNION ALL " "SELECT name FROM sqlite_temp_master " - "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' " + "WHERE type IN ('table','view') AND name LIKE shellstatic() " "ORDER BY 1", &azResult, &nRow, 0, &zErrMsg ); zShellStatic = 0; } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); - } - if( rc==SQLITE_OK ){ + rc = 1; + }else if( rc != SQLITE_OK ){ + fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n"); + rc = 1; + }else{ int len, maxlen = 0; int i, j; int nPrintCol, nPrintRow; for(i=1; i<=nRow; i++){ if( azResult[i]==0 ) continue; @@ -2640,39 +2976,35 @@ char *zSp = j<=nPrintRow ? "" : " "; printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); } printf("\n"); } - }else{ - rc = 1; } sqlite3_free_table(azResult); }else - if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){ + if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){ open_db(p); sqlite3_busy_timeout(p->db, atoi(azArg[1])); }else - -#if HAS_TIMER - if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg>1 ){ + + if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg==2 ){ enableTimer = booleanValue(azArg[1]); }else -#endif - - if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ + + if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){ int j; assert( nArg<=ArraySize(azArg) ); for(j=1; jcolWidth); j++){ p->colWidth[j-1] = atoi(azArg[j]); } }else - { - fprintf(stderr, "unknown command or invalid arguments: " + fprintf(stderr, "Error: unknown command or invalid arguments: " " \"%s\". Enter \".help\" for help\n", azArg[0]); + rc = 1; } return rc; } @@ -2771,15 +3103,15 @@ if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } lineno++; - if( p->echoOn ) printf("%s\n", zLine); if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue; if( zLine && zLine[0]=='.' && nSql==0 ){ + if( p->echoOn ) printf("%s\n", zLine); rc = do_meta_command(zLine, p); - if( rc==2 ){ + if( rc==2 ){ /* exit requested */ break; }else if( rc ){ errCnt++; } continue; @@ -2793,21 +3125,21 @@ for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){} if( zLine[i]!=0 ){ nSql = strlen30(zLine); zSql = malloc( nSql+3 ); if( zSql==0 ){ - fprintf(stderr, "out of memory\n"); + fprintf(stderr, "Error: out of memory\n"); exit(1); } memcpy(zSql, zLine, nSql+1); startline = lineno; } }else{ int len = strlen30(zLine); zSql = realloc( zSql, nSql + len + 4 ); if( zSql==0 ){ - fprintf(stderr,"%s: out of memory!\n", Argv0); + fprintf(stderr,"Error: out of memory\n"); exit(1); } zSql[nSql++] = '\n'; memcpy(&zSql[nSql], zLine, len+1); nSql += len; @@ -2815,36 +3147,36 @@ if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) && sqlite3_complete(zSql) ){ p->cnt = 0; open_db(p); BEGIN_TIMER; - rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg); + rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; if( in!=0 || !stdin_is_interactive ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, - "SQL error near line %d:", startline); + "Error: near line %d:", startline); }else{ - sqlite3_snprintf(sizeof(zPrefix), zPrefix, "SQL error:"); + sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:"); } if( zErrMsg!=0 ){ - printf("%s %s\n", zPrefix, zErrMsg); + fprintf(stderr, "%s %s\n", zPrefix, zErrMsg); sqlite3_free(zErrMsg); zErrMsg = 0; }else{ - printf("%s %s\n", zPrefix, sqlite3_errmsg(p->db)); + fprintf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } errCnt++; } free(zSql); zSql = 0; nSql = 0; } } if( zSql ){ - if( !_all_whitespace(zSql) ) fprintf(stderr, "Incomplete SQL: %s\n", zSql); + if( !_all_whitespace(zSql) ) fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); free(zSql); } free(zLine); return errCnt; } @@ -2912,55 +3244,59 @@ } /* ** Read input from the file given by sqliterc_override. Or if that ** parameter is NULL, take input from ~/.sqliterc +** +** Returns the number of errors. */ -static void process_sqliterc( +static int process_sqliterc( struct callback_data *p, /* Configuration data */ const char *sqliterc_override /* Name of config file. NULL to use default */ ){ char *home_dir = NULL; const char *sqliterc = sqliterc_override; char *zBuf = 0; FILE *in = NULL; int nBuf; + int rc = 0; if (sqliterc == NULL) { home_dir = find_home_dir(); if( home_dir==0 ){ #if !defined(__RTP__) && !defined(_WRS_KERNEL) - fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0); + fprintf(stderr,"%s: Error: cannot locate your home directory\n", Argv0); #endif - return; + return 1; } nBuf = strlen30(home_dir) + 16; zBuf = malloc( nBuf ); if( zBuf==0 ){ - fprintf(stderr,"%s: out of memory!\n", Argv0); - exit(1); + fprintf(stderr,"%s: Error: out of memory\n",Argv0); + return 1; } sqlite3_snprintf(nBuf, zBuf,"%s/.sqliterc",home_dir); free(home_dir); sqliterc = (const char*)zBuf; } in = fopen(sqliterc,"rb"); if( in ){ if( stdin_is_interactive ){ - printf("-- Loading resources from %s\n",sqliterc); + fprintf(stderr,"-- Loading resources from %s\n",sqliterc); } - process_input(p,in); + rc = process_input(p,in); fclose(in); } free(zBuf); - return; + return rc; } /* ** Show available command line options */ static const char zOptions[] = + " -help show this message\n" " -init filename read/process named file\n" " -echo print commands before execution\n" " -[no]header turn headers on or off\n" " -bail stop after hitting an error\n" " -interactive force interactive I/O\n" @@ -3030,10 +3366,16 @@ if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){ i++; }else if( strcmp(argv[i],"-init")==0 ){ i++; zInitFile = argv[i]; + /* Need to check for batch mode here to so we can avoid printing + ** informational messages (like from process_sqliterc) before + ** we do the actual processing of arguments later in a second pass. + */ + }else if( strcmp(argv[i],"-batch")==0 ){ + stdin_is_interactive = 0; } } if( i0 ){ + return rc; + } /* Make a second pass through the command-line argument and set ** options. This second pass is delayed until after the initialization ** file is processed so that the command-line arguments will override ** settings in the initialization file. @@ -3095,14 +3445,24 @@ }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.separator,",",2); }else if( strcmp(z,"-separator")==0 ){ i++; + if(i>=argc){ + fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); + fprintf(stderr,"Use -help for a list of options.\n"); + return 1; + } sqlite3_snprintf(sizeof(data.separator), data.separator, "%.*s",(int)sizeof(data.separator)-1,argv[i]); }else if( strcmp(z,"-nullvalue")==0 ){ i++; + if(i>=argc){ + fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); + fprintf(stderr,"Use -help for a list of options.\n"); + return 1; + } sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, "%.*s",(int)sizeof(data.nullvalue)-1,argv[i]); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ @@ -3119,29 +3479,31 @@ }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ usage(1); }else{ - fprintf(stderr,"%s: unknown option: %s\n", Argv0, z); + fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } } if( zFirstCmd ){ /* Run just the command that follows the database name */ if( zFirstCmd[0]=='.' ){ - do_meta_command(zFirstCmd, &data); - exit(0); + rc = do_meta_command(zFirstCmd, &data); + return rc; }else{ - int rc; open_db(&data); - rc = sqlite3_exec(data.db, zFirstCmd, callback, &data, &zErrMsg); - if( rc!=0 && zErrMsg!=0 ){ - fprintf(stderr,"SQL error: %s\n", zErrMsg); - exit(1); + rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg); + if( zErrMsg!=0 ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + return rc!=0 ? rc : 1; + }else if( rc!=0 ){ + fprintf(stderr,"Error: unable to process SQL \"%s\"\n", zFirstCmd); + return rc; } } }else{ /* Run commands received from standard input */ @@ -3177,10 +3539,11 @@ } } set_table_name(&data, 0); if( db ){ if( sqlite3_close(db)!=SQLITE_OK ){ - fprintf(stderr,"error closing database: %s\n", sqlite3_errmsg(db)); + fprintf(stderr,"Error: cannot close database \"%s\"\n", sqlite3_errmsg(db)); + rc++; } } return rc; } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -16,23 +16,21 @@ ** notice, and should not be referenced by programs that use SQLite. ** ** Some of the definitions that are in this file are marked as ** "experimental". Experimental interfaces are normally new ** features recently added to SQLite. We do not anticipate changes -** to experimental interfaces but reserve to make minor changes if -** experience from use "in the wild" suggest such changes are prudent. +** to experimental interfaces but reserve the right to make minor changes +** if experience from use "in the wild" suggest such changes are prudent. ** ** The official C-language API documentation for SQLite is derived ** from comments in this file. This file is the authoritative source ** on how SQLite interfaces are suppose to operate. ** ** The name of this file under configuration management is "sqlite.h.in". ** The makefile makes some minor changes to this file (such as inserting ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. -** -** @(#) $Id: sqlite.h.in,v 1.458 2009/06/19 22:50:31 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ #include /* Needed for the definition of va_list */ @@ -52,11 +50,11 @@ #endif /* ** These no-op macros are used in front of interfaces to mark those ** interfaces as either deprecated or experimental. New applications -** should not use deprecated intrfaces - they are support for backwards +** should not use deprecated interfaces - they are support for backwards ** compatibility only. Application writers should be aware that ** experimental interfaces are subject to change in point releases. ** ** These macros used to resolve to various kinds of compiler magic that ** would generate warning messages when they were used. But that @@ -82,55 +80,85 @@ ** ** The SQLITE_VERSION and SQLITE_VERSION_NUMBER #defines in ** the sqlite3.h file specify the version of SQLite with which ** that header file is associated. ** -** The "version" of SQLite is a string of the form "X.Y.Z". -** The phrase "alpha" or "beta" might be appended after the Z. -** The X value is major version number always 3 in SQLite3. -** The X value only changes when backwards compatibility is +** The "version" of SQLite is a string of the form "W.X.Y" or "W.X.Y.Z". +** The W value is major version number and is always 3 in SQLite3. +** The W value only changes when backwards compatibility is ** broken and we intend to never break backwards compatibility. -** The Y value is the minor version number and only changes when +** The X value is the minor version number and only changes when ** there are major feature enhancements that are forwards compatible ** but not backwards compatible. -** The Z value is the release number and is incremented with -** each release but resets back to 0 whenever Y is incremented. +** The Y value is the release number and is incremented with +** each release but resets back to 0 whenever X is incremented. +** The Z value only appears on branch releases. ** -** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()]. +** The SQLITE_VERSION_NUMBER is an integer that is computed as +** follows: +** +**
    +** SQLITE_VERSION_NUMBER = W*1000000 + X*1000 + Y
    +** 
    +** +** Since version 3.6.18, SQLite source code has been stored in the +** fossil configuration management +** system. The SQLITE_SOURCE_ID +** macro is a string which identifies a particular check-in of SQLite +** within its configuration management system. The string contains the +** date and time of the check-in (UTC) and an SHA1 hash of the entire +** source tree. +** +** See also: [sqlite3_libversion()], +** [sqlite3_libversion_number()], [sqlite3_sourceid()], +** [sqlite_version()] and [sqlite_source_id()]. ** ** Requirements: [H10011] [H10014] */ -#define SQLITE_VERSION "--VERS--" -#define SQLITE_VERSION_NUMBER --VERSION-NUMBER-- +#define SQLITE_VERSION "--VERS--" +#define SQLITE_VERSION_NUMBER --VERSION-NUMBER-- +#define SQLITE_SOURCE_ID "--SOURCE-ID--" /* ** CAPI3REF: Run-Time Library Version Numbers {H10020} ** KEYWORDS: sqlite3_version ** -** These features provide the same information as the [SQLITE_VERSION] -** and [SQLITE_VERSION_NUMBER] #defines in the header, but are associated -** with the library instead of the header file. Cautious programmers might -** include a check in their application to verify that -** sqlite3_libversion_number() always returns the value -** [SQLITE_VERSION_NUMBER]. +** These interfaces provide the same information as the [SQLITE_VERSION], +** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] #defines in the header, +** but are associated with the library instead of the header file. Cautious +** programmers might include assert() statements in their application to +** verify that values returned by these interfaces match the macros in +** the header, and thus insure that the application is +** compiled with matching library and header files. +** +**
    +** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
    +** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
    +** assert( strcmp(sqlite3_libversion,SQLITE_VERSION)==0 );
    +** 
    ** ** The sqlite3_libversion() function returns the same information as is ** in the sqlite3_version[] string constant. The function is provided ** for use in DLLs since DLL users usually do not have direct access to string -** constants within the DLL. +** constants within the DLL. Similarly, the sqlite3_sourceid() function +** returns the same information as is in the [SQLITE_SOURCE_ID] #define of +** the header file. +** +** See also: [sqlite_version()] and [sqlite_source_id()]. ** ** Requirements: [H10021] [H10022] [H10023] */ SQLITE_EXTERN const char sqlite3_version[]; const char *sqlite3_libversion(void); +const char *sqlite3_sourceid(void); int sqlite3_libversion_number(void); /* ** CAPI3REF: Test To See If The Library Is Threadsafe {H10100} ** ** SQLite can be compiled with or without mutexes. When -** the [SQLITE_THREADSAFE] C preprocessor macro 1 or 2, mutexes +** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes ** are enabled and SQLite is threadsafe. When the ** [SQLITE_THREADSAFE] macro is 0, ** the mutexes are omitted. Without the mutexes, it is not safe ** to use SQLite concurrently from more than one thread. ** @@ -137,11 +165,11 @@ ** Enabling mutexes incurs a measurable performance penalty. ** So if speed is of utmost importance, it makes sense to disable ** the mutexes. But for maximum safety, mutexes should be enabled. ** The default behavior is for mutexes to be enabled. ** -** This interface can be used by a program to make sure that the +** This interface can be used by an application to make sure that the ** version of SQLite that it is linking against was compiled with ** the desired setting of the [SQLITE_THREADSAFE] macro. ** ** This interface only reports on the compile-time mutex setting ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with @@ -210,23 +238,13 @@ /* ** CAPI3REF: Closing A Database Connection {H12010} ** ** This routine is the destructor for the [sqlite3] object. ** -** Applications should [sqlite3_finalize | finalize] all [prepared statements] +** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with ** the [sqlite3] object prior to attempting to close the object. -** The [sqlite3_next_stmt()] interface can be used to locate all -** [prepared statements] associated with a [database connection] if desired. -** Typical code might look like this: -** -**
    -** sqlite3_stmt *pStmt;
    -** while( (pStmt = sqlite3_next_stmt(db, 0))!=0 ){
    -**     sqlite3_finalize(pStmt);
    -** }
    -** 
    ** ** If [sqlite3_close()] is invoked while a transaction is open, ** the transaction is automatically rolled back. ** ** The C parameter to [sqlite3_close(C)] must be either a NULL @@ -404,10 +422,12 @@ #define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ #define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ /* ** CAPI3REF: Device Characteristics {H10240} ** ** The xDeviceCapabilities method of the [sqlite3_io_methods] @@ -471,12 +491,13 @@ #define SQLITE_SYNC_DATAONLY 0x00010 /* ** CAPI3REF: OS Interface Open File Handle {H11110} ** -** An [sqlite3_file] object represents an open file in the OS -** interface layer. Individual OS interface implementations will +** An [sqlite3_file] object represents an open file in the +** [sqlite3_vfs | OS interface layer]. Individual OS interface +** implementations will ** want to subclass this object by appending additional fields ** for their own use. The pMethods entry is a pointer to an ** [sqlite3_io_methods] object that defines methods for performing ** I/O operations on the open file. */ @@ -797,10 +818,13 @@ ** CAPI3REF: Initialize The SQLite Library {H10130} ** ** The sqlite3_initialize() routine initializes the ** SQLite library. The sqlite3_shutdown() routine ** deallocates any resources that were allocated by sqlite3_initialize(). +** These routines are designed to aid in process initialization and +** shutdown on embedded systems. Workstation applications using +** SQLite normally do not need to invoke either of these routines. ** ** A call to sqlite3_initialize() is an "effective" call if it is ** the first time sqlite3_initialize() is invoked during the lifetime of ** the process, or if it is the first time sqlite3_initialize() is invoked ** following a call to sqlite3_shutdown(). Only an effective call @@ -808,15 +832,21 @@ ** are harmless no-ops. ** ** A call to sqlite3_shutdown() is an "effective" call if it is the first ** call to sqlite3_shutdown() since the last sqlite3_initialize(). Only ** an effective call to sqlite3_shutdown() does any deinitialization. -** All other calls to sqlite3_shutdown() are harmless no-ops. +** All other valid calls to sqlite3_shutdown() are harmless no-ops. ** -** Among other things, sqlite3_initialize() shall invoke +** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() +** is not. The sqlite3_shutdown() interface must only be called from a +** single thread. All open [database connections] must be closed and all +** other SQLite resources must be deallocated prior to invoking +** sqlite3_shutdown(). +** +** Among other things, sqlite3_initialize() will invoke ** sqlite3_os_init(). Similarly, sqlite3_shutdown() -** shall invoke sqlite3_os_end(). +** will invoke sqlite3_os_end(). ** ** The sqlite3_initialize() routine returns [SQLITE_OK] on success. ** If for some reason, sqlite3_initialize() is unable to initialize ** the library (perhaps it is unable to allocate a needed resource such ** as a mutex) it returns an [error code] other than [SQLITE_OK]. @@ -848,12 +878,13 @@ ** or sqlite3_os_end() directly. The application should only invoke ** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() ** interface is called automatically by sqlite3_initialize() and ** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate ** implementations for sqlite3_os_init() and sqlite3_os_end() -** are built into SQLite when it is compiled for unix, windows, or os/2. -** When built for other platforms (using the [SQLITE_OS_OTHER=1] compile-time +** are built into SQLite when it is compiled for Unix, Windows, or OS/2. +** When [custom builds | built for other platforms] +** (using the [SQLITE_OS_OTHER=1] compile-time ** option) the application must supply a suitable implementation for ** sqlite3_os_init() and sqlite3_os_end(). An application-supplied ** implementation of sqlite3_os_init() or sqlite3_os_end() ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. @@ -930,42 +961,69 @@ ** and low-level memory allocation routines. ** ** This object is used in only one place in the SQLite interface. ** A pointer to an instance of this object is the argument to ** [sqlite3_config()] when the configuration option is -** [SQLITE_CONFIG_MALLOC]. By creating an instance of this object -** and passing it to [sqlite3_config()] during configuration, an -** application can specify an alternative memory allocation subsystem -** for SQLite to use for all of its dynamic memory needs. +** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. +** By creating an instance of this object +** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) +** during configuration, an application can specify an alternative +** memory allocation subsystem for SQLite to use for all of its +** dynamic memory needs. ** -** Note that SQLite comes with a built-in memory allocator that is -** perfectly adequate for the overwhelming majority of applications +** Note that SQLite comes with several [built-in memory allocators] +** that are perfectly adequate for the overwhelming majority of applications ** and that this object is only useful to a tiny minority of applications ** with specialized memory allocation requirements. This object is ** also used during testing of SQLite in order to specify an alternative ** memory allocator that simulates memory out-of-memory conditions in ** order to verify that SQLite recovers gracefully from such ** conditions. ** -** The xMalloc, xFree, and xRealloc methods must work like the -** malloc(), free(), and realloc() functions from the standard library. +** The xMalloc and xFree methods must work like the +** malloc() and free() functions from the standard C library. +** The xRealloc method must work like realloc() from the standard C library +** with the exception that if the second argument to xRealloc is zero, +** xRealloc must be a no-op - it must not perform any allocation or +** deallocation. SQLite guaranteeds that the second argument to +** xRealloc is always a value returned by a prior call to xRoundup. +** And so in cases where xRoundup always returns a positive number, +** xRealloc can perform exactly as the standard library realloc() and +** still be in compliance with this specification. ** ** xSize should return the allocated size of a memory allocation ** previously obtained from xMalloc or xRealloc. The allocated size ** is always at least as big as the requested size but may be larger. ** ** The xRoundup method returns what would be the allocated size of ** a memory allocation given a particular requested size. Most memory ** allocators round up memory allocations at least to the next multiple ** of 8. Some allocators round up to a larger multiple or to a power of 2. +** Every memory allocation request coming in through [sqlite3_malloc()] +** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, +** that causes the corresponding memory allocation to fail. ** ** The xInit method initializes the memory allocator. (For example, ** it might allocate any require mutexes or initialize internal data ** structures. The xShutdown method is invoked (indirectly) by ** [sqlite3_shutdown()] and should deallocate any resources acquired ** by xInit. The pAppData pointer is used as the only parameter to ** xInit and xShutdown. +** +** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes +** the xInit method, so the xInit method need not be threadsafe. The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. For all other methods, SQLite +** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the +** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which +** it is by default) and so the methods are automatically serialized. +** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other +** methods must be threadsafe or else make their own arrangements for +** serialization. +** +** SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). */ typedef struct sqlite3_mem_methods sqlite3_mem_methods; struct sqlite3_mem_methods { void *(*xMalloc)(int); /* Memory allocation function */ void (*xFree)(void*); /* Free a prior allocation */ @@ -1115,13 +1173,16 @@ ** routines with a wrapper used to track mutex usage for performance ** profiling or testing, for example. ** **
    SQLITE_CONFIG_LOOKASIDE
    **
    This option takes two arguments that determine the default -** memory allcation lookaside optimization. The first argument is the +** memory allocation lookaside optimization. The first argument is the ** size of each lookaside buffer slot and the second is the number of -** slots allocated to each database connection.
    +** slots allocated to each database connection. This option sets the +** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] +** verb to [sqlite3_db_config()] can be used to change the lookaside +** configuration on individual connections. ** **
    SQLITE_CONFIG_PCACHE
    **
    This option takes a single argument which is a pointer to ** an [sqlite3_pcache_methods] object. This object specifies the interface ** to a custom page cache implementation. SQLite makes a copy of the @@ -1167,16 +1228,19 @@ **
    **
    SQLITE_DBCONFIG_LOOKASIDE
    **
    This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an 8-byte aligned memory buffer to use for lookaside memory. +** pointer to an memory buffer to use for lookaside memory. ** The first argument may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. The second argument is the ** size of each lookaside buffer slot and the third argument is the number of ** slots. The size of the buffer in the first argument must be greater than -** or equal to the product of the second and third arguments.
    +** or equal to the product of the second and third arguments. The buffer +** must be aligned to an 8-byte boundary. If the second argument is not +** a multiple of 8, it is internally rounded down to the next smaller +** multiple of 8. See also: [SQLITE_CONFIG_LOOKASIDE]
    ** ** */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ @@ -1244,12 +1308,13 @@ ** This function returns the number of database rows that were changed ** or inserted or deleted by the most recently completed SQL statement ** on the [database connection] specified by the first parameter. ** Only changes that are directly specified by the [INSERT], [UPDATE], ** or [DELETE] statement are counted. Auxiliary changes caused by -** triggers are not counted. Use the [sqlite3_total_changes()] function -** to find the total number of changes including changes caused by triggers. +** triggers or [foreign key actions] are not counted. Use the +** [sqlite3_total_changes()] function to find the total number of changes +** including changes caused by triggers and foreign key actions. ** ** Changes to a view that are simulated by an [INSTEAD OF trigger] ** are not counted. Only real table changes are counted. ** ** A "row change" is a change to a single row of a single table @@ -1297,12 +1362,12 @@ /* ** CAPI3REF: Total Number Of Rows Modified {H12260} ** ** This function returns the number of row changes caused by [INSERT], ** [UPDATE] or [DELETE] statements since the [database connection] was opened. -** The count includes all changes from all -** [CREATE TRIGGER | trigger] contexts. However, +** The count includes all changes from all [CREATE TRIGGER | trigger] +** contexts and changes made by [foreign key actions]. However, ** the count does not include changes used to implement [REPLACE] constraints, ** do rollbacks or ABORT processing, or [DROP TABLE] processing. The ** count does not include rows of views that fire an [INSTEAD OF trigger], ** though if the INSTEAD OF trigger makes changes of its own, those changes ** are counted. @@ -1576,11 +1641,11 @@ void sqlite3_free_table(char **result); /* ** CAPI3REF: Formatted String Printing Functions {H17400} ** -** These routines are workalikes of the "printf()" family of functions +** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. ** ** The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. ** The strings returned by these two routines should be @@ -1863,11 +1928,11 @@ ** the database connection that invoked the authorizer callback. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** ** When [sqlite3_prepare_v2()] is used to prepare a statement, the -** statement might be reprepared during [sqlite3_step()] due to a +** statement might be re-prepared during [sqlite3_step()] due to a ** schema change. Hence, the application should ensure that the ** correct authorizer callback remains in place during the [sqlite3_step()]. ** ** Note that the authorizer callback is invoked only during ** [sqlite3_prepare()] or its variants. Authorization is not @@ -2030,11 +2095,12 @@ ** ** The sqlite3_open_v2() interface works like sqlite3_open() ** except that it accepts two additional parameters for additional control ** over the new database connection. The flags parameter can take one of ** the following three values, optionally combined with the -** [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags: +** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], +** and/or [SQLITE_OPEN_PRIVATECACHE] flags: ** **
    **
    [SQLITE_OPEN_READONLY]
    **
    The database is opened in read-only mode. If the database does not ** already exist, an error is returned.
    @@ -2050,19 +2116,25 @@ ** sqlite3_open() and sqlite3_open16(). **
    ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined -** with the [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags, +** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], +** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_SHAREDCACHE] flags, ** then the behavior is undefined. ** ** If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection ** opens in the multi-thread [threading mode] as long as the single-thread ** mode has not been set at compile-time or start-time. If the ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens ** in the serialized [threading mode] unless single-thread was ** previously selected at compile-time or start-time. +** The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be +** eligible to use [shared cache mode], regardless of whether or not shared +** cache is enabled using [sqlite3_enable_shared_cache()]. The +** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not +** participate in [shared cache mode] even if it is enabled. ** ** If the filename is ":memory:", then a private, temporary in-memory database ** is created for the connection. This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might ** make use of additional special filenames that begin with the ":" character. @@ -2210,11 +2282,11 @@ */ int sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories {H12790} -** KEYWORDS: {limit category} {limit categories} +** KEYWORDS: {limit category} {*limit categories} ** ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. @@ -2252,10 +2324,13 @@ ** [GLOB] operators. ** **
    SQLITE_LIMIT_VARIABLE_NUMBER
    **
    The maximum number of variables in an SQL statement that can ** be bound.
    +** +**
    SQLITE_LIMIT_TRIGGER_DEPTH
    +**
    The maximum depth of recursion for triggers.
    ** */ #define SQLITE_LIMIT_LENGTH 0 #define SQLITE_LIMIT_SQL_LENGTH 1 #define SQLITE_LIMIT_COLUMN 2 @@ -2264,10 +2339,11 @@ #define SQLITE_LIMIT_VDBE_OP 5 #define SQLITE_LIMIT_FUNCTION_ARG 6 #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 +#define SQLITE_LIMIT_TRIGGER_DEPTH 10 /* ** CAPI3REF: Compiling An SQL Statement {H13010} ** KEYWORDS: {SQL statement compiler} ** @@ -2312,11 +2388,11 @@ ** recommended for all new programs. The two older interfaces are retained ** for backwards compatibility, but their use is discouraged. ** In the "v2" interfaces, the prepared statement ** that is returned (the [sqlite3_stmt] object) contains a copy of the ** original SQL text. This causes the [sqlite3_step()] interface to -** behave a differently in two ways: +** behave differently in three ways: ** **
      **
    1. ** If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL @@ -2334,10 +2410,18 @@ ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code ** and you would have to make a second call to [sqlite3_reset()] in order ** to find the underlying cause of the problem. With the "v2" prepare ** interfaces, the underlying reason for the error is returned immediately. **
    2. +** +**
    3. +** ^If the value of a [parameter | host parameter] in the WHERE clause might +** change the query plan for a statement, then the statement may be +** automatically recompiled (as if there had been a schema change) on the first +** [sqlite3_step()] call following any change to the +** [sqlite3_bind_text | bindings] of the [parameter]. +**
    4. **
    ** ** Requirements: ** [H13011] [H13012] [H13013] [H13014] [H13015] [H13016] [H13019] [H13021] ** @@ -2440,22 +2524,23 @@ ** CAPI3REF: Binding Values To Prepared Statements {H13500} ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} ** ** In the SQL strings input to [sqlite3_prepare_v2()] and its variants, -** literals may be replaced by a [parameter] in one of these forms: +** literals may be replaced by a [parameter] that matches one of following +** templates: ** **
      **
    • ? **
    • ?NNN **
    • :VVV **
    • @VVV **
    • $VVV **
    ** -** In the parameter forms shown above NNN is an integer literal, -** and VVV is an alpha-numeric parameter name. The values of these +** In the templates above, NNN represents an integer literal, +** and VVV represents an alphanumeric identifer. The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** ** The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from @@ -2865,10 +2950,12 @@ ** result row of a query. In every case the first argument is a pointer ** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] ** that was returned from [sqlite3_prepare_v2()] or one of its variants) ** and the second argument is the index of the column for which information ** should be returned. The leftmost column of the result set has the index 0. +** The number of columns in the result can be determined using +** [sqlite3_column_count()]. ** ** If the SQL statement does not currently point to a valid row, or if the ** column index is out of range, the result is undefined. ** These routines may only be called when the most recent call to ** [sqlite3_step()] has returned [SQLITE_ROW] and neither @@ -3103,11 +3190,11 @@ ** ** The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. Any SQL function implementation should be able to work ** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be -** more efficient with one encoding than another. It is allowed to +** more efficient with one encoding than another. An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. ** When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** If there is only a single implementation which does not care what text @@ -3125,11 +3212,11 @@ ** SQL function or aggregate, pass NULL for all three function callbacks. ** ** It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. SQLite will use -** the implementation most closely matches the way in which the +** the implementation that most closely matches the way in which the ** SQL function is used. A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with ** a negative nArg. A function where the preferred text encoding ** matches the database encoding is a better ** match than a function where the encoding is different. @@ -3473,14 +3560,15 @@ ** function result. ** If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has ** finished using that result. -** If the 4th parameter to the sqlite3_result_text* interfaces or +** If the 4th parameter to the sqlite3_result_text* interfaces or to ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite ** assumes that the text or BLOB result is in constant space and does not -** copy the it or call a destructor when it has finished using that result. +** copy the content of the parameter nor call a destructor on the content +** when it has finished using that result. ** If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT ** then SQLite makes a copy of the result into space obtained from ** from [sqlite3_malloc()] before it returns. ** @@ -3866,11 +3954,11 @@ void* ); /* ** CAPI3REF: Enable Or Disable Shared Pager Cache {H10330} -** KEYWORDS: {shared cache} {shared cache mode} +** KEYWORDS: {shared cache} ** ** This routine enables or disables the sharing of the database cache ** and schema data structures between [database connection | connections] ** to the same database. Sharing is enabled if the argument is true ** and disabled if the argument is false. @@ -4329,11 +4417,11 @@ ** is delivered up to the client application, the string will be automatically ** freed by sqlite3_free() and the zErrMsg field will be zeroed. */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* Used internally */ + int nRef; /* NO LONGER USED */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; /* @@ -4426,10 +4514,13 @@ ** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow; ** {END} ** ** If the flags parameter is non-zero, then the BLOB is opened for read ** and write access. If it is zero, the BLOB is opened for read access. +** It is not possible to open a column that is part of an index or primary +** key for writing. ^If [foreign key constraints] are enabled, it is +** not possible to open a column that is part of a [child key] for writing. ** ** Note that the database name is not the filename that contains ** the database but rather the symbolic name of the database that ** is assigned when the database is connected using [ATTACH]. ** For the main database file, the database name is "main". @@ -4455,11 +4546,11 @@ ** rollback by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion. ** ** Use the [sqlite3_blob_bytes()] interface to determine the size of ** the opened blob. The size of a blob may not be changed by this -** underface. Use the [UPDATE] SQL command to change the size of a +** interface. Use the [UPDATE] SQL command to change the size of a ** blob. ** ** The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function can be used, if desired, ** to create an empty, zero-filled blob in which to read or write using @@ -4695,11 +4786,11 @@ ** cases where it really needs one. {END} If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** {H17017} The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. {END} Four static mutexes are +** a pointer to a static preexisting mutex. {END} Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. @@ -4801,10 +4892,25 @@ ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). +** +** The xMutexInit() method must be threadsafe. It must be harmless to +** invoke xMutexInit() mutiple times within the same process and without +** intervening calls to xMutexEnd(). Second and subsequent calls to +** xMutexInit() must be no-ops. +** +** xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] +** and its associates). Similarly, xMutexAlloc() must not use SQLite memory +** allocation for a static mutex. However xMutexAlloc() may use SQLite +** memory allocation for a fast or recursive mutex. +** +** SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is +** called, but only if the prior call to xMutexInit returned SQLITE_OK. +** If xMutexInit fails in any way, it is expected to clean up after itself +** prior to returning. */ typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; struct sqlite3_mutex_methods { int (*xMutexInit)(void); int (*xMutexEnd)(void); @@ -4943,10 +5049,11 @@ #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 +#define SQLITE_TESTCTRL_RESERVE 14 /* ** CAPI3REF: SQLite Runtime Status {H17200} ** EXPERIMENTAL ** @@ -4965,11 +5072,11 @@ ** value. For these latter parameters nothing is written into *pCurrent. ** ** This routine returns SQLITE_OK on success and a non-zero ** [error code] on failure. ** -** This routine is threadsafe but is not atomic. This routine can +** This routine is threadsafe but is not atomic. This routine can be ** called while other threads are running the same or different SQLite ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time ** and it is possible that another thread might change the parameter ** in between the times when *pCurrent and *pHighwater are written. @@ -5088,11 +5195,18 @@ /* ** CAPI3REF: Status Parameters for database connections {H17520} ** EXPERIMENTAL ** -** Status verbs for [sqlite3_db_status()]. +** These constants are the available integer "verbs" that can be passed as +** the second argument to the [sqlite3_db_status()] interface. +** +** New verbs may be added in future releases of SQLite. Existing verbs +** might be discontinued. Applications should check the return code from +** [sqlite3_db_status()] to make sure that the call worked. +** The [sqlite3_db_status()] interface will return a non-zero error code +** if a discontinued or unsupported verb is invoked. ** **
    **
    SQLITE_DBSTATUS_LOOKASIDE_USED
    **
    This parameter returns the number of lookaside memory slots currently ** checked out.
    @@ -5166,99 +5280,113 @@ */ typedef struct sqlite3_pcache sqlite3_pcache; /* ** CAPI3REF: Application Defined Page Cache. +** KEYWORDS: {page cache} ** EXPERIMENTAL ** ** The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an ** instance of the sqlite3_pcache_methods structure. The majority of the -** heap memory used by sqlite is used by the page cache to cache data read +** heap memory used by SQLite is used by the page cache to cache data read ** from, or ready to be written to, the database file. By implementing a ** custom page cache using this API, an application can control more -** precisely the amount of memory consumed by sqlite, the way in which -** said memory is allocated and released, and the policies used to +** precisely the amount of memory consumed by SQLite, the way in which +** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** -** The contents of the structure are copied to an internal buffer by sqlite -** within the call to [sqlite3_config]. +** The contents of the sqlite3_pcache_methods structure are copied to an +** internal buffer by SQLite within the call to [sqlite3_config]. Hence +** the application may discard the parameter after the call to +** [sqlite3_config()] returns. ** ** The xInit() method is called once for each call to [sqlite3_initialize()] ** (usually only once during the lifetime of the process). It is passed ** a copy of the sqlite3_pcache_methods.pArg value. It can be used to set ** up global structures and mutexes required by the custom page cache -** implementation. The xShutdown() method is called from within -** [sqlite3_shutdown()], if the application invokes this API. It can be used -** to clean up any outstanding resources before process shutdown, if required. +** implementation. +** +** The xShutdown() method is called from within [sqlite3_shutdown()], +** if the application invokes this API. It can be used to clean up +** any outstanding resources before process shutdown, if required. +** +** SQLite holds a [SQLITE_MUTEX_RECURSIVE] mutex when it invokes +** the xInit method, so the xInit method need not be threadsafe. The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. All other methods must be threadsafe +** in multithreaded applications. +** +** SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). ** -** The xCreate() method is used to construct a new cache instance. The +** The xCreate() method is used to construct a new cache instance. SQLite +** will typically create one cache instance for each open database file, +** though this is not guaranteed. The ** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. szPage will not be a power of two. The -** second argument, bPurgeable, is true if the cache being created will -** be used to cache database pages read from a file stored on disk, or +** be allocated by the cache. szPage will not be a power of two. szPage +** will the page size of the database file that is to be cached plus an +** increment (here called "R") of about 100 or 200. SQLite will use the +** extra R bytes on each page to store metadata about the underlying +** database page on disk. The value of R depends +** on the SQLite version, the target platform, and how SQLite was compiled. +** R is constant for a particular build of SQLite. The second argument to +** xCreate(), bPurgeable, is true if the cache being created will +** be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation -** does not have to do anything special based on the value of bPurgeable, -** it is purely advisory. +** does not have to do anything special based with the value of bPurgeable; +** it is purely advisory. On a cache where bPurgeable is false, SQLite will +** never invoke xUnpin() except to deliberately delete a page. +** In other words, a cache created with bPurgeable set to false will +** never contain any unpinned pages. ** ** The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using ** the SQLite "[PRAGMA cache_size]" command. As with the bPurgeable parameter, -** the implementation is not required to do anything special with this -** value, it is advisory only. +** the implementation is not required to do anything with this +** value; it is advisory only. ** ** The xPagecount() method should return the number of pages currently -** stored in the cache supplied as an argument. +** stored in the cache. ** ** The xFetch() method is used to fetch a page and return a pointer to it. ** A 'page', in this context, is a buffer of szPage bytes aligned at an ** 8-byte boundary. The page to be fetched is determined by the key. The ** mimimum key value is 1. After it has been retrieved using xFetch, the page -** is considered to be pinned. +** is considered to be "pinned". ** -** If the requested page is already in the page cache, then a pointer to -** the cached buffer should be returned with its contents intact. If the -** page is not already in the cache, then the expected behaviour of the -** cache is determined by the value of the createFlag parameter passed -** to xFetch, according to the following table: +** If the requested page is already in the page cache, then the page cache +** implementation must return a pointer to the page buffer with its content +** intact. If the requested page is not already in the cache, then the +** behavior of the cache implementation is determined by the value of the +** createFlag parameter passed to xFetch, according to the following table: ** ** -**
    createFlagExpected Behaviour -**
    0NULL should be returned. No new cache entry is created. -**
    1If createFlag is set to 1, this indicates that -** SQLite is holding pinned pages that can be unpinned -** by writing their contents to the database file (a -** relatively expensive operation). In this situation the -** cache implementation has two choices: it can return NULL, -** in which case SQLite will attempt to unpin one or more -** pages before re-requesting the same page, or it can -** allocate a new page and return a pointer to it. If a new -** page is allocated, then the first sizeof(void*) bytes of -** it (at least) must be zeroed before it is returned. -**
    2If createFlag is set to 2, then SQLite is not holding any -** pinned pages associated with the specific cache passed -** as the first argument to xFetch() that can be unpinned. The -** cache implementation should attempt to allocate a new -** cache entry and return a pointer to it. Again, the first -** sizeof(void*) bytes of the page should be zeroed before -** it is returned. If the xFetch() method returns NULL when -** createFlag==2, SQLite assumes that a memory allocation -** failed and returns SQLITE_NOMEM to the user. +**
    createFlag Behaviour when page is not already in cache +**
    0 Do not allocate a new page. Return NULL. +**
    1 Allocate a new page if it easy and convenient to do so. +** Otherwise return NULL. +**
    2 Make every effort to allocate a new page. Only return +** NULL if allocating a new page is effectively impossible. **
    +** +** SQLite will normally invoke xFetch() with a createFlag of 0 or 1. If +** a call to xFetch() with createFlag==1 returns NULL, then SQLite will +** attempt to unpin one or more cache pages by spilling the content of +** pinned pages to disk and synching the operating system disk cache. After +** attempting to unpin pages, the xFetch() method will be invoked again with +** a createFlag of 2. ** ** xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. If the third parameter, discard, is non-zero, ** then the page should be evicted from the cache. In this case SQLite ** assumes that the next time the page is retrieved from the cache using ** the xFetch() method, it will be zeroed. If the discard parameter is ** zero, then the page is considered to be unpinned. The cache implementation -** may choose to reclaim (free or recycle) unpinned pages at any time. -** SQLite assumes that next time the page is retrieved from the cache -** it will either be zeroed, or contain the same data that it did when it -** was unpinned. +** may choose to evict unpinned pages at any time. ** ** The cache is not required to perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch(). ** @@ -5610,10 +5738,22 @@ sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ ); + +/* +** CAPI3REF: String Comparison +** EXPERIMENTAL +** +** The [sqlite3_strnicmp()] API allows applications and extensions to +** compare the contents of two buffers containing UTF-8 strings in a +** case-indendent fashion, using the same definition of case independence +** that SQLite uses internally when comparing identifiers. +*/ +int sqlite3_strnicmp(const char *, const char *, int); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT Index: src/sqlite3ext.h ================================================================== --- src/sqlite3ext.h +++ src/sqlite3ext.h @@ -12,12 +12,10 @@ ** This header file defines the SQLite interface for use by ** shared libraries that want to be imported as extensions into ** an SQLite instance. Shared libraries that intend to be loaded ** as extensions by SQLite should #include this file instead of ** sqlite3.h. -** -** @(#) $Id: sqlite3ext.h,v 1.25 2008/10/12 00:27:54 shane Exp $ */ #ifndef _SQLITE3EXT_H_ #define _SQLITE3EXT_H_ #include "sqlite3.h" Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -9,15 +9,41 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.890 2009/06/26 15:14:55 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ +/* +** These #defines should enable >2GB file support on POSIX if the +** underlying operating system supports it. If the OS lacks +** large file support, or if the OS is windows, these should be no-ops. +** +** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any +** system #includes. Hence, this block of code must be the very first +** code in all source files. +** +** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch +** on the compiler command line. This is necessary if you are compiling +** on a recent machine (ex: Red Hat 7.2) but you want your code to work +** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 +** without this option, LFS is enable. But LFS does not exist in the kernel +** in Red Hat 6.0, so the code won't work. Hence, for maximum binary +** portability you should omit LFS. +** +** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + /* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ #ifdef _HAVE_SQLITE_CONFIG_H @@ -48,10 +74,12 @@ #endif #ifdef HAVE_INTTYPES_H #include #endif +#define SQLITE_INDEX_SAMPLES 10 + /* ** This macro is used to "hide" some ugliness in casting an int ** value to a ptr value under the MSVC 64-bit compiler. Casting ** non 64-bit values to ptr types results in a "hard" error with ** the MSVC 64-bit compiler which this attempts to avoid. @@ -80,37 +108,10 @@ #else # define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #endif -/* -** These #defines should enable >2GB file support on POSIX if the -** underlying operating system supports it. If the OS lacks -** large file support, or if the OS is windows, these should be no-ops. -** -** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any -** system #includes. Hence, this block of code must be the very first -** code in all source files. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: Red Hat 7.2) but you want your code to work -** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in Red Hat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - /* ** The SQLITE_THREADSAFE macro must be defined as either 0 or 1. ** Older versions of SQLite used an optional THREADSAFE macro. ** We support that for legacy @@ -303,11 +304,11 @@ */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL -# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<60) +# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 # undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT # undef SQLITE_HAVE_ISNAN @@ -349,10 +350,14 @@ */ #define SQLITE_MAX_FILE_FORMAT 4 #ifndef SQLITE_DEFAULT_FILE_FORMAT # define SQLITE_DEFAULT_FILE_FORMAT 1 #endif + +#ifndef SQLITE_DEFAULT_RECURSIVE_TRIGGERS +# define SQLITE_DEFAULT_RECURSIVE_TRIGGERS 0 +#endif /* ** Provide a default value for SQLITE_TEMP_STORE in case it is not specified ** on the command-line */ @@ -593,10 +598,11 @@ typedef struct FKey FKey; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; +typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; typedef struct Lookaside Lookaside; typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; @@ -607,14 +613,15 @@ typedef struct SrcList SrcList; typedef struct StrAccum StrAccum; typedef struct Table Table; typedef struct TableLock TableLock; typedef struct Token Token; -typedef struct TriggerStack TriggerStack; +typedef struct TriggerPrg TriggerPrg; typedef struct TriggerStep TriggerStep; typedef struct Trigger Trigger; typedef struct UnpackedRecord UnpackedRecord; +typedef struct VTable VTable; typedef struct Walker Walker; typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; typedef struct WhereLevel WhereLevel; @@ -661,10 +668,11 @@ struct Schema { int schema_cookie; /* Database schema version number for this file */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ + Hash fkeyHash; /* All foreign keys by referenced table name */ Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ u8 file_format; /* Schema format version for this file */ u8 enc; /* Text encoding used by this database */ u16 flags; /* Flags associated with this schema */ int cache_size; /* Number of pages to use in the cache */ @@ -698,11 +706,11 @@ /* ** The number of different kinds of things that can be limited ** using the sqlite3_limit() interface. */ -#define SQLITE_N_LIMIT (SQLITE_LIMIT_VARIABLE_NUMBER+1) +#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1) /* ** Lookaside malloc is a set of fixed-size buffers that can be used ** to satisfy small transient memory allocation requests for objects ** associated with a particular database connection. The use of @@ -797,10 +805,11 @@ int aLimit[SQLITE_N_LIMIT]; /* Limits */ struct sqlite3InitInfo { /* Information used during initialization */ int iDb; /* When back is being initialized */ int newTnum; /* Rootpage of table being initialized */ u8 busy; /* TRUE if currently initializing */ + u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ } init; int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ struct Vdbe *pVdbe; /* List of active virtual machines */ int activeVdbeCnt; /* Number of VDBEs currently executing */ @@ -837,12 +846,13 @@ int nProgressOps; /* Number of opcodes for progress callback */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE Hash aModule; /* populated by sqlite3_create_module() */ Table *pVTab; /* vtab with active Connect/Create method */ - sqlite3_vtab **aVTrans; /* Virtual tables with open transactions */ + VTable **aVTrans; /* Virtual tables with open transactions */ int nVTrans; /* Allocated size of aVTrans */ + VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ #endif FuncDefHash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ BusyHandler busyHandler; /* Busy callback */ int busyTimeout; /* Busy handler timeout, in msec */ @@ -849,10 +859,11 @@ Db aDbStatic[2]; /* Static space for the 2 default backends */ 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 */ + i64 nDeferredCons; /* Net deferred constraints this transaction. */ #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. ** @@ -903,13 +914,13 @@ #define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */ #define SQLITE_FullFSync 0x00010000 /* Use full fsync on the backend */ #define SQLITE_LoadExtension 0x00020000 /* Enable load_extension */ #define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */ -#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ -#define SQLITE_CommitBusy 0x00200000 /* In the process of committing */ -#define SQLITE_ReverseOrder 0x00400000 /* Reverse unordered SELECTs */ +#define SQLITE_ReverseOrder 0x00100000 /* Reverse unordered SELECTs */ +#define SQLITE_RecTriggers 0x00200000 /* Enable recursive triggers */ +#define SQLITE_ForeignKeys 0x00400000 /* Enforce foreign key constraints */ /* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other ** than being distinct from one another. @@ -946,10 +957,11 @@ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ #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 */ +#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** used to create the initializers for the FuncDef structures. ** @@ -992,10 +1004,11 @@ ** opened savepoint. Savepoints are added to the list by the vdbe ** OP_Savepoint instruction. */ struct Savepoint { char *zName; /* Savepoint name (nul-terminated) */ + i64 nDeferredCons; /* Number of deferred fk violations */ Savepoint *pNext; /* Parent savepoint (if any) */ }; /* ** The following are used as the second parameter to sqlite3Savepoint(), @@ -1112,10 +1125,61 @@ ** Additional bit values that can be ORed with an affinity without ** changing the affinity. */ #define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ #define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ +#define SQLITE_NULLEQ 0x80 /* NULL=NULL */ + +/* +** An object of this type is created for each virtual table present in +** the database schema. +** +** If the database schema is shared, then there is one instance of this +** structure for each database connection (sqlite3*) that uses the shared +** schema. This is because each database connection requires its own unique +** instance of the sqlite3_vtab* handle used to access the virtual table +** implementation. sqlite3_vtab* handles can not be shared between +** database connections, even when the rest of the in-memory database +** schema is shared, as the implementation often stores the database +** connection handle passed to it via the xConnect() or xCreate() method +** during initialization internally. This database connection handle may +** then used by the virtual table implementation to access real tables +** within the database. So that they appear as part of the callers +** transaction, these accesses need to be made via the same database +** connection as that used to execute SQL operations on the virtual table. +** +** All VTable objects that correspond to a single table in a shared +** database schema are initially stored in a linked-list pointed to by +** the Table.pVTable member variable of the corresponding Table object. +** When an sqlite3_prepare() operation is required to access the virtual +** table, it searches the list for the VTable that corresponds to the +** database connection doing the preparing so as to use the correct +** sqlite3_vtab* handle in the compiled query. +** +** When an in-memory Table object is deleted (for example when the +** schema is being reloaded for some reason), the VTable objects are not +** deleted and the sqlite3_vtab* handles are not xDisconnect()ed +** immediately. Instead, they are moved from the Table.pVTable list to +** another linked list headed by the sqlite3.pDisconnect member of the +** corresponding sqlite3 structure. They are then deleted/xDisconnected +** next time a statement is prepared using said sqlite3*. This is done +** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. +** Refer to comments above function sqlite3VtabUnlockList() for an +** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect +** list without holding the corresponding sqlite3.mutex mutex. +** +** The memory for objects of this type is always allocated by +** sqlite3DbMalloc(), using the connection handle stored in VTable.db as +** the first argument. +*/ +struct VTable { + sqlite3 *db; /* Database connection associated with this table */ + Module *pMod; /* Pointer to module implementation */ + sqlite3_vtab *pVtab; /* Pointer to vtab instance */ + int nRef; /* Number of pointers to this structure */ + VTable *pNext; /* Next in linked list (see above) */ +}; /* ** Each SQL table is represented in memory by an instance of the ** following structure. ** @@ -1164,12 +1228,11 @@ #endif #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - Module *pMod; /* Pointer to the implementation of the module */ - sqlite3_vtab *pVtab; /* Pointer to the module instance */ + VTable *pVTable; /* List of VTable objects. */ 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 */ @@ -1219,18 +1282,20 @@ ** Each REFERENCES clause generates an instance of the following structure ** which is attached to the from-table. The to-table need not exist when ** the from-table is created. The existence of the to-table is not checked. */ struct FKey { - Table *pFrom; /* The table that contains the REFERENCES clause */ + Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */ FKey *pNextFrom; /* Next foreign key in pFrom */ - char *zTo; /* Name of table that the key points to */ + char *zTo; /* Name of table that the key points to (aka: Parent) */ + FKey *pNextTo; /* Next foreign key on table named zTo */ + FKey *pPrevTo; /* Previous foreign key on table named zTo */ int nCol; /* Number of columns in this key */ + /* EV: R-30323-21917 */ u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ - u8 updateConf; /* How to resolve conflicts that occur on UPDATE */ - u8 deleteConf; /* How to resolve conflicts that occur on DELETE */ - u8 insertConf; /* How to resolve conflicts that occur on INSERT */ + u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ + Trigger *apTrigger[2]; /* Triggers for aAction[] actions */ struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ int iFrom; /* Index of column in pFrom */ char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ } aCol[1]; /* One entry for each of nCol column s */ }; @@ -1358,10 +1423,24 @@ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ + IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */ +}; + +/* +** Each sample stored in the sqlite_stat2 table is represented in memory +** using a structure of this type. +*/ +struct IndexSample { + union { + char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ + double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */ + } u; + u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ + u8 nByte; /* Size in byte of text or blob. */ }; /* ** Each token coming out of the lexer is an instance of ** this structure. Tokens are also used as part of an expression. @@ -1417,10 +1496,26 @@ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ int nFuncAlloc; /* Number of slots allocated for aFunc[] */ }; +/* +** The datatype ynVar is a signed integer, either 16-bit or 32-bit. +** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater +** than 32767 we have to make it 32-bit. 16-bit is preferred because +** it uses less memory in the Expr object, which is a big memory user +** in systems with lots of prepared statements. And few applications +** need more than about 10 or 20 variables. But some extreme users want +** to have prepared statements with over 32767 variables, and for them +** the option is available (at compile-time). +*/ +#if SQLITE_MAX_VARIABLE_NUMBER<=32767 +typedef i16 ynVar; +#else +typedef int ynVar; +#endif + /* ** 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 @@ -1508,15 +1603,18 @@ ** space is allocated for the fields below this point. An attempt to ** access them will result in a segfault or malfunction. *********************************************************************/ int iTable; /* TK_COLUMN: cursor number of table holding column - ** TK_REGISTER: register number */ - int iColumn; /* TK_COLUMN: column index. -1 for rowid */ + ** TK_REGISTER: register number + ** TK_TRIGGER: 1 -> new, 0 -> old */ + ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. + ** TK_VARIABLE: variable number (always >= 1). */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ - u16 flags2; /* Second set of flags. EP2_... */ + u8 flags2; /* Second set of flags. EP2_... */ + u8 op2; /* If a TK_REGISTER, the original value of Expr.op */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ Table *pTab; /* Table for TK_COLUMN expressions. */ #if SQLITE_MAX_EXPR_DEPTH>0 int nHeight; /* Height of the tree headed by this node */ #endif @@ -1946,10 +2044,35 @@ */ #ifndef SQLITE_N_COLCACHE # define SQLITE_N_COLCACHE 10 #endif +/* +** At least one instance of the following structure is created for each +** trigger that may be fired while parsing an INSERT, UPDATE or DELETE +** statement. All such objects are stored in the linked list headed at +** Parse.pTriggerPrg and deleted once statement compilation has been +** completed. +** +** A Vdbe sub-program that implements the body and WHEN clause of trigger +** TriggerPrg.pTrigger, assuming a default ON CONFLICT clause of +** TriggerPrg.orconf, is stored in the TriggerPrg.pProgram variable. +** The Parse.pTriggerPrg list never contains two entries with the same +** values for both pTrigger and orconf. +** +** The TriggerPrg.oldmask variable is set to a mask of old.* columns +** accessed (or set to 0 for triggers fired as a result of INSERT +** statements). +*/ +struct TriggerPrg { + Trigger *pTrigger; /* Trigger this program was coded from */ + int orconf; /* Default ON CONFLICT policy */ + SubProgram *pProgram; /* Program implementing pTrigger/orconf */ + u32 oldmask; /* Mask of old.* columns accessed */ + TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ +}; + /* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to ** carry around information that is global to the entire parse. ** @@ -1997,48 +2120,58 @@ int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ u32 writeMask; /* Start a write transaction on these databases */ u32 cookieMask; /* Bitmask of schema verified databases */ + u8 isMultiWrite; /* True if statement may affect/insert multiple rows */ + u8 mayAbort; /* True if statement may throw an ABORT exception */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */ #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ #endif int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRoot; /* Register holding root page number for new objects */ AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ + int nMaxArg; /* Max args passed to user function by sub-program */ + + /* Information used while coding trigger programs. */ + Parse *pToplevel; /* Parse structure for main program (or NULL) */ + Table *pTriggerTab; /* Table triggers are being coded for */ + u32 oldmask; /* Mask of old.* columns referenced */ + u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ + u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ + u8 disableTriggers; /* True to disable triggers */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ int nVar; /* Number of '?' variables seen in the SQL so far */ int nVarExpr; /* Number of used slots in apVarExpr[] */ int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ + Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ int nAlias; /* Number of aliased result set columns */ int nAliasAlloc; /* Number of allocated slots for aAlias[] */ int *aAlias; /* Register used to hold aliased result */ u8 explain; /* True if the EXPLAIN flag is found on the query */ - Token sErrToken; /* The token at which the error occurred */ Token sNameToken; /* Token with unqualified schema object name */ Token sLastToken; /* The last token parsed */ - const char *zSql; /* All SQL text */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ - TriggerStack *trigStack; /* Trigger actions being coded */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ #ifndef SQLITE_OMIT_VIRTUALTABLE Token sArg; /* Complete text of a module argument */ u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ int nVtabLock; /* Number of virtual tables to lock */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif int nHeight; /* Expression tree height of current sub-select */ Table *pZombieTab; /* List of Table objects to delete after code gen */ + TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ }; #ifdef SQLITE_OMIT_VIRTUALTABLE #define IN_DECLARE_VTAB 0 #else @@ -2055,15 +2188,16 @@ }; /* ** Bitfield flags for P5 value in OP_Insert and OP_Delete */ -#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */ -#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */ -#define OPFLAG_ISUPDATE 4 /* This OP_Insert is an sql UPDATE */ -#define OPFLAG_APPEND 8 /* This is likely to be an append */ -#define OPFLAG_USESEEKRESULT 16 /* Try to avoid a seek in BtreeInsert() */ +#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ +#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ +#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ +#define OPFLAG_APPEND 0x08 /* This is likely to be an append */ +#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ +#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ /* * Each trigger present in the database schema is stored as an instance of * struct Trigger. * @@ -2077,11 +2211,11 @@ * * The "step_list" member points to the first element of a linked list * containing the SQL statements specified as the trigger program. */ struct Trigger { - char *name; /* The name of the trigger */ + char *zName; /* The name of the trigger */ char *table; /* The table or view to which the trigger applies */ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */ IdList *pColumns; /* If this is an UPDATE OF trigger, @@ -2139,64 +2273,22 @@ * them to. See sqlite3Update() documentation of "pChanges" * argument. * */ struct TriggerStep { - int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ - int orconf; /* OE_Rollback etc. */ + u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ + u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ - - Select *pSelect; /* Valid for SELECT and sometimes - INSERT steps (when pExprList == 0) */ - Token target; /* Target table for DELETE, UPDATE, INSERT. Quoted */ - Expr *pWhere; /* Valid for DELETE, UPDATE steps */ - ExprList *pExprList; /* Valid for UPDATE statements and sometimes - INSERT steps (when pSelect == 0) */ - IdList *pIdList; /* Valid for INSERT statements only */ + Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ + Token target; /* Target table for DELETE, UPDATE, INSERT */ + Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ + ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */ + IdList *pIdList; /* Column names for INSERT */ TriggerStep *pNext; /* Next in the link-list */ TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */ }; -/* - * An instance of struct TriggerStack stores information required during code - * generation of a single trigger program. While the trigger program is being - * coded, its associated TriggerStack instance is pointed to by the - * "pTriggerStack" member of the Parse structure. - * - * The pTab member points to the table that triggers are being coded on. The - * newIdx member contains the index of the vdbe cursor that points at the temp - * table that stores the new.* references. If new.* references are not valid - * for the trigger being coded (for example an ON DELETE trigger), then newIdx - * is set to -1. The oldIdx member is analogous to newIdx, for old.* references. - * - * The ON CONFLICT policy to be used for the trigger program steps is stored - * as the orconf member. If this is OE_Default, then the ON CONFLICT clause - * specified for individual triggers steps is used. - * - * struct TriggerStack has a "pNext" member, to allow linked lists to be - * constructed. When coding nested triggers (triggers fired by other triggers) - * each nested trigger stores its parent trigger's TriggerStack as the "pNext" - * pointer. Once the nested trigger has been coded, the pNext value is restored - * to the pTriggerStack member of the Parse stucture and coding of the parent - * trigger continues. - * - * Before a nested trigger is coded, the linked list pointed to by the - * pTriggerStack is scanned to ensure that the trigger is not about to be coded - * recursively. If this condition is detected, the nested trigger is not coded. - */ -struct TriggerStack { - Table *pTab; /* Table that triggers are currently being coded on */ - int newIdx; /* Index of vdbe cursor to "new" temp table */ - int oldIdx; /* Index of vdbe cursor to "old" temp table */ - u32 newColMask; - u32 oldColMask; - int orconf; /* Current orconf policy */ - int ignoreJump; /* where to jump to for a RAISE(IGNORE) */ - Trigger *pTrigger; /* The trigger currently being coded */ - TriggerStack *pNext; /* Next trigger down on the trigger stack */ -}; - /* ** The following structure contains information used by the sqliteFix... ** routines as they walk the parse tree to make database references ** explicit. */ @@ -2263,11 +2355,13 @@ int sharedCacheEnabled; /* true if shared-cache mode enabled */ /* The above might be initialized to non-zero. The following need to always ** initially be zero, however. */ int isInit; /* True after initialization has finished */ int inProgress; /* True while initialization in progress */ + int isMutexInit; /* True after mutexes are initialized */ int isMallocInit; /* True after malloc is initialized */ + int isPCacheInit; /* True after malloc is initialized */ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */ int nRefInitMutex; /* Number of users of pInitMutex */ }; /* @@ -2355,13 +2449,13 @@ /* ** Internal function prototypes */ int sqlite3StrICmp(const char *, const char *); -int sqlite3StrNICmp(const char *, const char *, int); int sqlite3IsNumber(const char*, int*, u8); int sqlite3Strlen30(const char*); +#define sqlite3StrNICmp sqlite3_strnicmp int sqlite3MallocInit(void); void sqlite3MallocEnd(void); void *sqlite3Malloc(int); void *sqlite3MallocZero(int); @@ -2517,11 +2611,11 @@ int sqlite3IndexedByLookup(Parse *, struct SrcList_item *); void sqlite3SrcListShiftJoinType(SrcList*); void sqlite3SrcListAssignCursors(Parse*, SrcList*); void sqlite3IdListDelete(sqlite3*, IdList*); void sqlite3SrcListDelete(sqlite3*, SrcList*); -void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, +Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, Token*, int, int); void sqlite3DropIndex(Parse*, SrcList*, int); int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,int,Expr*,Expr*); @@ -2564,11 +2658,10 @@ char *sqlite3NameFromToken(sqlite3*, Token*); int sqlite3ExprCompare(Expr*, Expr*); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); Vdbe *sqlite3GetVdbe(Parse*); -Expr *sqlite3CreateIdExpr(Parse *, const char*); void sqlite3PrngSaveState(void); void sqlite3PrngRestoreState(void); void sqlite3PrngResetState(void); void sqlite3RollbackAll(sqlite3*); void sqlite3CodeVerifySchema(Parse*, int); @@ -2580,18 +2673,21 @@ int sqlite3ExprIsConstant(Expr*); int sqlite3ExprIsConstantNotJoin(Expr*); int sqlite3ExprIsConstantOrFunction(Expr*); int sqlite3ExprIsInteger(Expr*, int*); int sqlite3IsRowid(const char*); -void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int); +void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int); void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int, int*,int,int,int,int,int*); -void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int,int,int); +void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); void sqlite3BeginWriteOperation(Parse*, int, int); +void sqlite3MultiWrite(Parse*); +void sqlite3MayAbort(Parse*); +void sqlite3HaltConstraint(Parse*, int, char*, int); Expr *sqlite3ExprDup(sqlite3*,Expr*,int); ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); IdList *sqlite3IdListDup(sqlite3*,IdList*); Select *sqlite3SelectDup(sqlite3*,Select*,int); @@ -2621,28 +2717,34 @@ void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); void sqlite3DropTrigger(Parse*, SrcList*, int); void sqlite3DropTriggerPtr(Parse*, Trigger*); 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 sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *, + int, int, int); + void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, int, int, int); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*, - ExprList*,Select*,int); - TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, int); + ExprList*,Select*,u8); + TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8); TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*); void sqlite3DeleteTrigger(sqlite3*, Trigger*); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); + u32 sqlite3TriggerOldmask(Parse*,Trigger*,ExprList*,Table*,int); +# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) #else # define sqlite3TriggersExist(B,C,D,E,F) 0 # 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,L) 0 +# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I) +# define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F) # define sqlite3TriggerList(X, Y) 0 +# define sqlite3ParseToplevel(p) p +# define sqlite3TriggerOldmask(A,B,C,D,E) 0 #endif int sqlite3JoinType(Parse*, Token*, Token*, Token*); void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int); void sqlite3DeferForeignKey(Parse*, int); @@ -2649,19 +2751,20 @@ #ifndef SQLITE_OMIT_AUTHORIZATION void sqlite3AuthRead(Parse*,Expr*,Schema*,SrcList*); int sqlite3AuthCheck(Parse*,int, const char*, const char*, const char*); void sqlite3AuthContextPush(Parse*, AuthContext*, const char*); void sqlite3AuthContextPop(AuthContext*); + int sqlite3AuthReadCol(Parse*, const char *, const char *, int); #else # define sqlite3AuthRead(a,b,c,d) # define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); void sqlite3Detach(Parse*, Expr*); -int sqlite3BtreeFactory(const sqlite3 *db, const char *zFilename, +int sqlite3BtreeFactory(sqlite3 *db, const char *zFilename, int omitJournal, int nCache, int flags, Btree **ppBtree); int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); int sqlite3FixSrcList(DbFixer*, SrcList*); int sqlite3FixSelect(DbFixer*, Select*); int sqlite3FixExpr(DbFixer*, Expr*); @@ -2708,11 +2811,11 @@ #define putVarint32(A,B) (u8)(((u32)(B)<(u32)0x80) ? (*(A) = (unsigned char)(B)),1 : sqlite3PutVarint32((A), (B))) #define getVarint sqlite3GetVarint #define putVarint sqlite3PutVarint -void sqlite3IndexAffinityStr(Vdbe *, Index *); +const char *sqlite3IndexAffinityStr(Vdbe *, Index *); void sqlite3TableAffinityStr(Vdbe *, Table *); char sqlite3CompareAffinity(Expr *pExpr, char aff2); int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); char sqlite3ExprAffinity(Expr *pExpr); int sqlite3Atoi64(const char*, i64*); @@ -2734,10 +2837,13 @@ void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(sqlite3 *); char *sqlite3Utf16to8(sqlite3 *, const void*, int); +#ifdef SQLITE_ENABLE_STAT2 +char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *); +#endif int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char sqlite3CtypeMap[]; @@ -2750,25 +2856,26 @@ void sqlite3AlterFunctions(sqlite3*); void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); int sqlite3GetToken(const unsigned char *, int *); void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); -void sqlite3CodeSubselect(Parse *, Expr *, int, int); +int sqlite3CodeSubselect(Parse *, Expr *, int, int); void sqlite3SelectPrep(Parse*, Select*, NameContext*); int sqlite3ResolveExprNames(NameContext*, Expr*); void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); -void sqlite3ColumnDefault(Vdbe *, Table *, int); +void sqlite3ColumnDefault(Vdbe *, Table *, int, int); void sqlite3AlterFinishAddColumn(Parse *, Token *); void sqlite3AlterBeginAddColumn(Parse *, SrcList *); -CollSeq *sqlite3GetCollSeq(sqlite3*, CollSeq *, const char*); +CollSeq *sqlite3GetCollSeq(sqlite3*, u8, CollSeq *, const char*); char sqlite3AffinityType(const char*); void sqlite3Analyze(Parse*, Token*, Token*); int sqlite3InvokeBusyHandler(BusyHandler*); int sqlite3FindDb(sqlite3*, Token*); int sqlite3FindDbName(sqlite3 *, const char *); int sqlite3AnalysisLoad(sqlite3*,int iDB); +void sqlite3DeleteIndexSamples(Index*); void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); void sqlite3MinimumFileFormat(Parse*, int, int); void sqlite3SchemaFree(void *); @@ -2784,10 +2891,11 @@ void sqlite3StrAccumInit(StrAccum*, char*, int, int); void sqlite3StrAccumAppend(StrAccum*,const char*,int); char *sqlite3StrAccumFinish(StrAccum*); void sqlite3StrAccumReset(StrAccum*); void sqlite3SelectDestInit(SelectDest*,int,int); +Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); void sqlite3BackupRestart(sqlite3_backup *); void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); /* @@ -2816,41 +2924,72 @@ #ifdef SQLITE_TEST int sqlite3Utf8To8(unsigned char*); #endif #ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlite3VtabClear(X) +# define sqlite3VtabClear(Y) # define sqlite3VtabSync(X,Y) SQLITE_OK # define sqlite3VtabRollback(X) # define sqlite3VtabCommit(X) # define sqlite3VtabInSync(db) 0 +# define sqlite3VtabLock(X) +# define sqlite3VtabUnlock(X) +# define sqlite3VtabUnlockList(X) #else void sqlite3VtabClear(Table*); int sqlite3VtabSync(sqlite3 *db, char **); int sqlite3VtabRollback(sqlite3 *db); int sqlite3VtabCommit(sqlite3 *db); + void sqlite3VtabLock(VTable *); + void sqlite3VtabUnlock(VTable *); + void sqlite3VtabUnlockList(sqlite3*); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif void sqlite3VtabMakeWritable(Parse*,Table*); -void sqlite3VtabLock(sqlite3_vtab*); -void sqlite3VtabUnlock(sqlite3*, sqlite3_vtab*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*); void sqlite3VtabFinishParse(Parse*, Token*); void sqlite3VtabArgInit(Parse*); void sqlite3VtabArgExtend(Parse*, Token*); int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); int sqlite3VtabCallConnect(Parse*, Table*); int sqlite3VtabCallDestroy(sqlite3*, int, const char *); -int sqlite3VtabBegin(sqlite3 *, sqlite3_vtab *); +int sqlite3VtabBegin(sqlite3 *, VTable *); FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**); int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); +VTable *sqlite3GetVTable(sqlite3*, Table*); +/* Declarations for functions in fkey.c. All of these are replaced by +** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign +** key functionality is available. If OMIT_TRIGGER is defined but +** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In +** this case foreign keys are parsed, but no other functionality is +** provided (enforcement of FK constraints requires the triggers sub-system). +*/ +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) + void sqlite3FkCheck(Parse*, Table*, int, int); + void sqlite3FkDropTable(Parse*, SrcList *, Table*); + void sqlite3FkActions(Parse*, Table*, ExprList*, int); + int sqlite3FkRequired(Parse*, Table*, int*, int); + u32 sqlite3FkOldmask(Parse*, Table*); + FKey *sqlite3FkReferences(Table *); +#else + #define sqlite3FkActions(a,b,c,d) + #define sqlite3FkCheck(a,b,c,d) + #define sqlite3FkDropTable(a,b,c) + #define sqlite3FkOldmask(a,b) 0 + #define sqlite3FkRequired(a,b,c,d) 0 +#endif +#ifndef SQLITE_OMIT_FOREIGN_KEY + void sqlite3FkDelete(Table*); +#else + #define sqlite3FkDelete(a) +#endif /* ** Available fault injectors. Should be numbered beginning with 0. */ Index: src/sqliteLimit.h ================================================================== --- src/sqliteLimit.h +++ src/sqliteLimit.h @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file defines various limits of what SQLite can process. -** -** @(#) $Id: sqliteLimit.h,v 1.10 2009/01/10 16:15:09 danielk1977 Exp $ */ /* ** The maximum length of a TEXT or BLOB in bytes. This also ** limits the size of a row in a table or index. @@ -186,5 +184,16 @@ ** operator. */ #ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH # define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 #endif + +/* +** Maximum depth of recursion for triggers. +** +** A value of 1 means that a trigger program will not be able to itself +** fire any triggers. A value of 0 means that no trigger programs at all +** may be executed. +*/ +#ifndef SQLITE_MAX_TRIGGER_DEPTH +# define SQLITE_MAX_TRIGGER_DEPTH 1000 +#endif Index: src/status.c ================================================================== --- src/status.c +++ src/status.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** ** This module implements the sqlite3_status() interface and related ** functionality. -** -** $Id: status.c,v 1.9 2008/09/02 00:52:52 drh Exp $ */ #include "sqliteInt.h" /* ** Variables in which to record status information. Index: src/table.c ================================================================== --- src/table.c +++ src/table.c @@ -13,12 +13,10 @@ ** interface routines. These are just wrappers around the main ** interface routine of sqlite3_exec(). ** ** These routines are in a separate files so that they will not be linked ** if they are not used. -** -** $Id: table.c,v 1.40 2009/04/10 14:28:00 drh Exp $ */ #include "sqliteInt.h" #include #include Index: src/tclsqlite.c ================================================================== --- src/tclsqlite.c +++ src/tclsqlite.c @@ -10,11 +10,22 @@ ** ************************************************************************* ** 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.241 2009/03/27 12:44:35 drh Exp $ +** Compile-time options: +** +** -DTCLSH=1 Add a "main()" routine that works as a tclsh. +** +** -DSQLITE_TCLMD5 When used in conjuction with -DTCLSH=1, add +** four new commands to the TCL interpreter for +** generating MD5 checksums: md5, md5file, +** md5-10x8, and md5file-10x8. +** +** -DSQLITE_TEST When used in conjuction with -DTCLSH=1, add +** hundreds of new commands used for testing +** SQLite. This option implies -DSQLITE_TCLMD5. */ #include "tcl.h" #include /* @@ -24,12 +35,12 @@ #ifndef SQLITE_AMALGAMATION # include "sqliteInt.h" # include # include # include -# include #endif +#include /* * Windows needs to know which symbols to export. Unix does not. * BUILD_sqlite should be undefined for Unix. */ @@ -84,10 +95,12 @@ SqlPreparedStmt *pNext; /* Next in linked list */ SqlPreparedStmt *pPrev; /* Previous on the list */ sqlite3_stmt *pStmt; /* The prepared statement */ int nSql; /* chars in zSql[] */ const char *zSql; /* Text of the SQL statement */ + int nParm; /* Size of apParm array */ + Tcl_Obj **apParm; /* Array of referenced object pointers */ }; typedef struct IncrblobChannel IncrblobChannel; /* @@ -755,11 +768,11 @@ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); }else{ Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); int n; u8 *data; - char *zType = pVar->typePtr ? pVar->typePtr->name : ""; + const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); char c = zType[0]; if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ /* Only return a BLOB type if the Tcl variable is a bytearray and ** has no string representation. */ data = Tcl_GetByteArrayFromObj(pVar, &n); @@ -932,67 +945,575 @@ return zLine; } /* -** Figure out the column names for the data returned by the statement -** passed as the second argument. -** -** If parameter papColName is not NULL, then *papColName is set to point -** at an array allocated using Tcl_Alloc(). It is the callers responsibility -** to free this array using Tcl_Free(), and to decrement the reference -** count of each Tcl_Obj* member of the array. -** -** The return value of this function is the number of columns of data -** returned by pStmt (and hence the size of the *papColName array). +** This function is part of the implementation of the command: +** +** $db transaction [-deferred|-immediate|-exclusive] SCRIPT +** +** It is invoked after evaluating the script SCRIPT to commit or rollback +** the transaction or savepoint opened by the [transaction] command. +*/ +static int DbTransPostCmd( + ClientData data[], /* data[0] is the Sqlite3Db* for $db */ + Tcl_Interp *interp, /* Tcl interpreter */ + int result /* Result of evaluating SCRIPT */ +){ + static const char *azEnd[] = { + "RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */ + "COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */ + "ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction", + "ROLLBACK" /* rc==TCL_ERROR, nTransaction==0 */ + }; + SqliteDb *pDb = (SqliteDb*)data[0]; + int rc = result; + const char *zEnd; + + pDb->nTransaction--; + zEnd = azEnd[(rc==TCL_ERROR)*2 + (pDb->nTransaction==0)]; + + pDb->disableAuth++; + if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){ + /* This is a tricky scenario to handle. The most likely cause of an + ** error is that the exec() above was an attempt to commit the + ** top-level transaction that returned SQLITE_BUSY. Or, less likely, + ** that an IO-error has occured. In either case, throw a Tcl exception + ** and try to rollback the transaction. + ** + ** But it could also be that the user executed one or more BEGIN, + ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing + ** this method's logic. Not clear how this would be best handled. + */ + if( rc!=TCL_ERROR ){ + Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); + rc = TCL_ERROR; + } + sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0); + } + pDb->disableAuth--; + + return rc; +} + +/* +** Search the cache for a prepared-statement object that implements the +** first SQL statement in the buffer pointed to by parameter zIn. If +** no such prepared-statement can be found, allocate and prepare a new +** one. In either case, bind the current values of the relevant Tcl +** variables to any $var, :var or @var variables in the statement. Before +** returning, set *ppPreStmt to point to the prepared-statement object. +** +** Output parameter *pzOut is set to point to the next SQL statement in +** buffer zIn, or to the '\0' byte at the end of zIn if there is no +** next statement. +** +** If successful, TCL_OK is returned. Otherwise, TCL_ERROR is returned +** and an error message loaded into interpreter pDb->interp. +*/ +static int dbPrepareAndBind( + SqliteDb *pDb, /* Database object */ + char const *zIn, /* SQL to compile */ + char const **pzOut, /* OUT: Pointer to next SQL statement */ + SqlPreparedStmt **ppPreStmt /* OUT: Object used to cache statement */ +){ + const char *zSql = zIn; /* Pointer to first SQL statement in zIn */ + sqlite3_stmt *pStmt; /* Prepared statement object */ + SqlPreparedStmt *pPreStmt; /* Pointer to cached statement */ + int nSql; /* Length of zSql in bytes */ + int nVar; /* Number of variables in statement */ + int iParm = 0; /* Next free entry in apParm */ + int i; + Tcl_Interp *interp = pDb->interp; + + *ppPreStmt = 0; + + /* Trim spaces from the start of zSql and calculate the remaining length. */ + while( isspace(zSql[0]) ){ zSql++; } + nSql = strlen30(zSql); + + for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ + int n = pPreStmt->nSql; + if( nSql>=n + && memcmp(pPreStmt->zSql, zSql, n)==0 + && (zSql[n]==0 || zSql[n-1]==';') + ){ + pStmt = pPreStmt->pStmt; + *pzOut = &zSql[pPreStmt->nSql]; + + /* When a prepared statement is found, unlink it from the + ** cache list. It will later be added back to the beginning + ** of the cache list in order to implement LRU replacement. + */ + if( pPreStmt->pPrev ){ + pPreStmt->pPrev->pNext = pPreStmt->pNext; + }else{ + pDb->stmtList = pPreStmt->pNext; + } + if( pPreStmt->pNext ){ + pPreStmt->pNext->pPrev = pPreStmt->pPrev; + }else{ + pDb->stmtLast = pPreStmt->pPrev; + } + pDb->nStmt--; + nVar = sqlite3_bind_parameter_count(pStmt); + break; + } + } + + /* If no prepared statement was found. Compile the SQL text. Also allocate + ** a new SqlPreparedStmt structure. */ + if( pPreStmt==0 ){ + int nByte; + + if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, pzOut) ){ + Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); + return TCL_ERROR; + } + if( pStmt==0 ){ + if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){ + /* A compile-time error in the statement. */ + Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); + return TCL_ERROR; + }else{ + /* The statement was a no-op. Continue to the next statement + ** in the SQL string. + */ + return TCL_OK; + } + } + + assert( pPreStmt==0 ); + nVar = sqlite3_bind_parameter_count(pStmt); + nByte = sizeof(SqlPreparedStmt) + nVar*sizeof(Tcl_Obj *); + pPreStmt = (SqlPreparedStmt*)Tcl_Alloc(nByte); + memset(pPreStmt, 0, nByte); + + pPreStmt->pStmt = pStmt; + pPreStmt->nSql = (*pzOut - zSql); + pPreStmt->zSql = sqlite3_sql(pStmt); + pPreStmt->apParm = (Tcl_Obj **)&pPreStmt[1]; + } + assert( pPreStmt ); + assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql ); + assert( 0==memcmp(pPreStmt->zSql, zSql, pPreStmt->nSql) ); + + /* Bind values to parameters that begin with $ or : */ + for(i=1; i<=nVar; i++){ + const char *zVar = sqlite3_bind_parameter_name(pStmt, i); + if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){ + Tcl_Obj *pVar = Tcl_GetVar2Ex(interp, &zVar[1], 0, 0); + if( pVar ){ + int n; + u8 *data; + const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); + char c = zType[0]; + if( zVar[0]=='@' || + (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ + /* Load a BLOB type if the Tcl variable is a bytearray and + ** it has no string representation or the host + ** parameter name begins with "@". */ + data = Tcl_GetByteArrayFromObj(pVar, &n); + sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC); + Tcl_IncrRefCount(pVar); + pPreStmt->apParm[iParm++] = pVar; + }else if( c=='b' && strcmp(zType,"boolean")==0 ){ + Tcl_GetIntFromObj(interp, pVar, &n); + sqlite3_bind_int(pStmt, i, n); + }else if( c=='d' && strcmp(zType,"double")==0 ){ + double r; + Tcl_GetDoubleFromObj(interp, pVar, &r); + sqlite3_bind_double(pStmt, i, r); + }else if( (c=='w' && strcmp(zType,"wideInt")==0) || + (c=='i' && strcmp(zType,"int")==0) ){ + Tcl_WideInt v; + Tcl_GetWideIntFromObj(interp, pVar, &v); + sqlite3_bind_int64(pStmt, i, v); + }else{ + data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); + sqlite3_bind_text(pStmt, i, (char *)data, n, SQLITE_STATIC); + Tcl_IncrRefCount(pVar); + pPreStmt->apParm[iParm++] = pVar; + } + }else{ + sqlite3_bind_null(pStmt, i); + } + } + } + pPreStmt->nParm = iParm; + *ppPreStmt = pPreStmt; + + return TCL_OK; +} + + +/* +** Release a statement reference obtained by calling dbPrepareAndBind(). +** There should be exactly one call to this function for each call to +** dbPrepareAndBind(). +** +** If the discard parameter is non-zero, then the statement is deleted +** immediately. Otherwise it is added to the LRU list and may be returned +** by a subsequent call to dbPrepareAndBind(). +*/ +static void dbReleaseStmt( + SqliteDb *pDb, /* Database handle */ + SqlPreparedStmt *pPreStmt, /* Prepared statement handle to release */ + int discard /* True to delete (not cache) the pPreStmt */ +){ + int i; + + /* Free the bound string and blob parameters */ + for(i=0; inParm; i++){ + Tcl_DecrRefCount(pPreStmt->apParm[i]); + } + pPreStmt->nParm = 0; + + if( pDb->maxStmt<=0 || discard ){ + /* If the cache is turned off, deallocated the statement */ + sqlite3_finalize(pPreStmt->pStmt); + Tcl_Free((char *)pPreStmt); + }else{ + /* Add the prepared statement to the beginning of the cache list. */ + pPreStmt->pNext = pDb->stmtList; + pPreStmt->pPrev = 0; + if( pDb->stmtList ){ + pDb->stmtList->pPrev = pPreStmt; + } + pDb->stmtList = pPreStmt; + if( pDb->stmtLast==0 ){ + assert( pDb->nStmt==0 ); + pDb->stmtLast = pPreStmt; + }else{ + assert( pDb->nStmt>0 ); + } + pDb->nStmt++; + + /* If we have too many statement in cache, remove the surplus from + ** the end of the cache list. */ + while( pDb->nStmt>pDb->maxStmt ){ + sqlite3_finalize(pDb->stmtLast->pStmt); + pDb->stmtLast = pDb->stmtLast->pPrev; + Tcl_Free((char*)pDb->stmtLast->pNext); + pDb->stmtLast->pNext = 0; + pDb->nStmt--; + } + } +} + +/* +** Structure used with dbEvalXXX() functions: +** +** dbEvalInit() +** dbEvalStep() +** dbEvalFinalize() +** dbEvalRowInfo() +** dbEvalColumnValue() +*/ +typedef struct DbEvalContext DbEvalContext; +struct DbEvalContext { + SqliteDb *pDb; /* Database handle */ + Tcl_Obj *pSql; /* Object holding string zSql */ + const char *zSql; /* Remaining SQL to execute */ + SqlPreparedStmt *pPreStmt; /* Current statement */ + int nCol; /* Number of columns returned by pStmt */ + Tcl_Obj *pArray; /* Name of array variable */ + Tcl_Obj **apColName; /* Array of column names */ +}; + +/* +** Release any cache of column names currently held as part of +** the DbEvalContext structure passed as the first argument. +*/ +static void dbReleaseColumnNames(DbEvalContext *p){ + if( p->apColName ){ + int i; + for(i=0; inCol; i++){ + Tcl_DecrRefCount(p->apColName[i]); + } + Tcl_Free((char *)p->apColName); + p->apColName = 0; + } + p->nCol = 0; +} + +/* +** Initialize a DbEvalContext structure. ** ** If pArray is not NULL, then it contains the name of a Tcl array ** variable. The "*" member of this array is set to a list containing -** the names of the columns returned by the statement, in order from -** left to right. e.g. if the names of the returned columns are a, b and -** c, it does the equivalent of the tcl command: +** the names of the columns returned by the statement as part of each +** call to dbEvalStep(), in order from left to right. e.g. if the names +** of the returned columns are a, b and c, it does the equivalent of the +** tcl command: ** ** set ${pArray}(*) {a b c} */ -static int -computeColumnNames( - Tcl_Interp *interp, - sqlite3_stmt *pStmt, /* SQL statement */ - Tcl_Obj ***papColName, /* OUT: Array of column names */ - Tcl_Obj *pArray /* Name of array variable (may be null) */ +static void dbEvalInit( + DbEvalContext *p, /* Pointer to structure to initialize */ + SqliteDb *pDb, /* Database handle */ + Tcl_Obj *pSql, /* Object containing SQL script */ + Tcl_Obj *pArray /* Name of Tcl array to set (*) element of */ ){ - int nCol; + memset(p, 0, sizeof(DbEvalContext)); + p->pDb = pDb; + p->zSql = Tcl_GetString(pSql); + p->pSql = pSql; + Tcl_IncrRefCount(pSql); + if( pArray ){ + p->pArray = pArray; + Tcl_IncrRefCount(pArray); + } +} +/* +** Obtain information about the row that the DbEvalContext passed as the +** first argument currently points to. +*/ +static void dbEvalRowInfo( + DbEvalContext *p, /* Evaluation context */ + int *pnCol, /* OUT: Number of column names */ + Tcl_Obj ***papColName /* OUT: Array of column names */ +){ /* Compute column names */ - nCol = sqlite3_column_count(pStmt); - if( papColName ){ - int i; - Tcl_Obj **apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); - for(i=0; iapColName ){ + sqlite3_stmt *pStmt = p->pPreStmt->pStmt; + int i; /* Iterator variable */ + int nCol; /* Number of columns returned by pStmt */ + Tcl_Obj **apColName = 0; /* Array of column names */ + + p->nCol = nCol = sqlite3_column_count(pStmt); + if( nCol>0 && (papColName || p->pArray) ){ + apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); + for(i=0; iapColName = apColName; } /* If results are being stored in an array variable, then create ** the array(*) entry for that array */ - if( pArray ){ + if( p->pArray ){ + Tcl_Interp *interp = p->pDb->interp; Tcl_Obj *pColList = Tcl_NewObj(); Tcl_Obj *pStar = Tcl_NewStringObj("*", -1); - Tcl_IncrRefCount(pColList); + for(i=0; ipArray, pStar, pColList, 0); Tcl_DecrRefCount(pStar); } - *papColName = apColName; + } + + if( papColName ){ + *papColName = p->apColName; + } + if( pnCol ){ + *pnCol = p->nCol; + } +} + +/* +** Return one of TCL_OK, TCL_BREAK or TCL_ERROR. If TCL_ERROR is +** returned, then an error message is stored in the interpreter before +** returning. +** +** A return value of TCL_OK means there is a row of data available. The +** data may be accessed using dbEvalRowInfo() and dbEvalColumnValue(). This +** is analogous to a return of SQLITE_ROW from sqlite3_step(). If TCL_BREAK +** is returned, then the SQL script has finished executing and there are +** no further rows available. This is similar to SQLITE_DONE. +*/ +static int dbEvalStep(DbEvalContext *p){ + while( p->zSql[0] || p->pPreStmt ){ + int rc; + if( p->pPreStmt==0 ){ + rc = dbPrepareAndBind(p->pDb, p->zSql, &p->zSql, &p->pPreStmt); + if( rc!=TCL_OK ) return rc; + }else{ + int rcs; + SqliteDb *pDb = p->pDb; + SqlPreparedStmt *pPreStmt = p->pPreStmt; + sqlite3_stmt *pStmt = pPreStmt->pStmt; + + rcs = sqlite3_step(pStmt); + if( rcs==SQLITE_ROW ){ + return TCL_OK; + } + if( p->pArray ){ + dbEvalRowInfo(p, 0, 0); + } + rcs = sqlite3_reset(pStmt); + + pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); + pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); + dbReleaseColumnNames(p); + p->pPreStmt = 0; + + if( rcs!=SQLITE_OK ){ + /* If a run-time error occurs, report the error and stop reading + ** the SQL. */ + Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db))); + dbReleaseStmt(pDb, pPreStmt, 1); + return TCL_ERROR; + }else{ + dbReleaseStmt(pDb, pPreStmt, 0); + } + } + } + + /* Finished */ + return TCL_BREAK; +} + +/* +** Free all resources currently held by the DbEvalContext structure passed +** as the first argument. There should be exactly one call to this function +** for each call to dbEvalInit(). +*/ +static void dbEvalFinalize(DbEvalContext *p){ + if( p->pPreStmt ){ + sqlite3_reset(p->pPreStmt->pStmt); + dbReleaseStmt(p->pDb, p->pPreStmt, 0); + p->pPreStmt = 0; + } + if( p->pArray ){ + Tcl_DecrRefCount(p->pArray); + p->pArray = 0; + } + Tcl_DecrRefCount(p->pSql); + dbReleaseColumnNames(p); +} + +/* +** Return a pointer to a Tcl_Obj structure with ref-count 0 that contains +** the value for the iCol'th column of the row currently pointed to by +** the DbEvalContext structure passed as the first argument. +*/ +static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){ + sqlite3_stmt *pStmt = p->pPreStmt->pStmt; + switch( sqlite3_column_type(pStmt, iCol) ){ + case SQLITE_BLOB: { + int bytes = sqlite3_column_bytes(pStmt, iCol); + const char *zBlob = sqlite3_column_blob(pStmt, iCol); + if( !zBlob ) bytes = 0; + return Tcl_NewByteArrayObj((u8*)zBlob, bytes); + } + case SQLITE_INTEGER: { + sqlite_int64 v = sqlite3_column_int64(pStmt, iCol); + if( v>=-2147483647 && v<=2147483647 ){ + return Tcl_NewIntObj(v); + }else{ + return Tcl_NewWideIntObj(v); + } + } + case SQLITE_FLOAT: { + return Tcl_NewDoubleObj(sqlite3_column_double(pStmt, iCol)); + } + case SQLITE_NULL: { + return dbTextToObj(p->pDb->zNull); + } + } + + return dbTextToObj((char *)sqlite3_column_text(pStmt, iCol)); +} + +/* +** If using Tcl version 8.6 or greater, use the NR functions to avoid +** recursive evalution of scripts by the [db eval] and [db trans] +** commands. Even if the headers used while compiling the extension +** are 8.6 or newer, the code still tests the Tcl version at runtime. +** This allows stubs-enabled builds to be used with older Tcl libraries. +*/ +#if TCL_MAJOR_VERSION>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6) +# define SQLITE_TCL_NRE 1 +static int DbUseNre(void){ + int major, minor; + Tcl_GetVersion(&major, &minor, 0, 0); + return( (major==8 && minor>=6) || major>8 ); +} +#else +/* +** Compiling using headers earlier than 8.6. In this case NR cannot be +** used, so DbUseNre() to always return zero. Add #defines for the other +** Tcl_NRxxx() functions to prevent them from causing compilation errors, +** even though the only invocations of them are within conditional blocks +** of the form: +** +** if( DbUseNre() ) { ... } +*/ +# define SQLITE_TCL_NRE 0 +# define DbUseNre() 0 +# define Tcl_NRAddCallback(a,b,c,d,e,f) 0 +# define Tcl_NREvalObj(a,b,c) 0 +# define Tcl_NRCreateCommand(a,b,c,d,e,f) 0 +#endif + +/* +** This function is part of the implementation of the command: +** +** $db eval SQL ?ARRAYNAME? SCRIPT +*/ +static int DbEvalNextCmd( + ClientData data[], /* data[0] is the (DbEvalContext*) */ + Tcl_Interp *interp, /* Tcl interpreter */ + int result /* Result so far */ +){ + int rc = result; /* Return code */ + + /* The first element of the data[] array is a pointer to a DbEvalContext + ** structure allocated using Tcl_Alloc(). The second element of data[] + ** is a pointer to a Tcl_Obj containing the script to run for each row + ** returned by the queries encapsulated in data[0]. */ + DbEvalContext *p = (DbEvalContext *)data[0]; + Tcl_Obj *pScript = (Tcl_Obj *)data[1]; + Tcl_Obj *pArray = p->pArray; + + while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){ + int i; + int nCol; + Tcl_Obj **apColName; + dbEvalRowInfo(p, &nCol, &apColName); + for(i=0; idb))); break; } + + /* + ** $db exists $sql + ** $db onecolumn $sql + ** + ** The onecolumn method is the equivalent of: + ** lindex [$db eval $sql] 0 + */ + case DB_EXISTS: + case DB_ONECOLUMN: { + DbEvalContext sEval; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SQL"); + return TCL_ERROR; + } + + dbEvalInit(&sEval, pDb, objv[2], 0); + rc = dbEvalStep(&sEval); + if( choice==DB_ONECOLUMN ){ + if( rc==TCL_OK ){ + Tcl_SetObjResult(interp, dbEvalColumnValue(&sEval, 0)); + } + }else if( rc==TCL_BREAK || rc==TCL_OK ){ + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc==TCL_OK)); + } + dbEvalFinalize(&sEval); + + if( rc==TCL_BREAK ){ + rc = TCL_OK; + } + break; + } /* ** $db eval $sql ?array? ?{ ...code... }? - ** $db onecolumn $sql ** ** The SQL statement in $sql is evaluated. For each row, the values are ** placed in elements of the array named "array" and ...code... is executed. ** If "array" and "code" are omitted, then no callback is every invoked. ** If "array" is an empty string, then the values are placed in variables ** that have the same name as the fields extracted by the query. - ** - ** The onecolumn method is the equivalent of: - ** lindex [$db eval $sql] 0 - */ - case DB_ONECOLUMN: - case DB_EVAL: - case DB_EXISTS: { - char const *zSql; /* Next SQL statement to execute */ - char const *zLeft; /* What is left after first stmt in zSql */ - sqlite3_stmt *pStmt; /* Compiled SQL statment */ - Tcl_Obj *pArray; /* Name of array into which results are written */ - Tcl_Obj *pScript; /* Script to run for each result set */ - Tcl_Obj **apParm; /* Parameters that need a Tcl_DecrRefCount() */ - int nParm; /* Number of entries used in apParm[] */ - Tcl_Obj *aParm[10]; /* Static space for apParm[] in the common case */ - Tcl_Obj *pRet; /* Value to be returned */ - SqlPreparedStmt *pPreStmt; /* Pointer to a prepared statement */ - int rc2; - - if( choice==DB_EVAL ){ - if( objc<3 || objc>5 ){ - Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?"); - return TCL_ERROR; - } - pRet = Tcl_NewObj(); - Tcl_IncrRefCount(pRet); - }else{ - if( objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "SQL"); - return TCL_ERROR; - } - if( choice==DB_EXISTS ){ - pRet = Tcl_NewBooleanObj(0); - Tcl_IncrRefCount(pRet); - }else{ - pRet = 0; - } - } - if( objc==3 ){ - pArray = pScript = 0; - }else if( objc==4 ){ - pArray = 0; - pScript = objv[3]; - }else{ - pArray = objv[3]; - if( Tcl_GetString(pArray)[0]==0 ) pArray = 0; - pScript = objv[4]; - } - - Tcl_IncrRefCount(objv[2]); - zSql = Tcl_GetStringFromObj(objv[2], 0); - while( rc==TCL_OK && zSql[0] ){ - int i; /* Loop counter */ - int nVar; /* Number of bind parameters in the pStmt */ - int nCol = -1; /* Number of columns in the result set */ - Tcl_Obj **apColName = 0; /* Array of column names */ - int len; /* String length of zSql */ - - /* Try to find a SQL statement that has already been compiled and - ** which matches the next sequence of SQL. - */ - pStmt = 0; - len = strlen30(zSql); - for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ - int n = pPreStmt->nSql; - if( len>=n - && memcmp(pPreStmt->zSql, zSql, n)==0 - && (zSql[n]==0 || zSql[n-1]==';') - ){ - pStmt = pPreStmt->pStmt; - zLeft = &zSql[pPreStmt->nSql]; - - /* When a prepared statement is found, unlink it from the - ** cache list. It will later be added back to the beginning - ** of the cache list in order to implement LRU replacement. - */ - if( pPreStmt->pPrev ){ - pPreStmt->pPrev->pNext = pPreStmt->pNext; - }else{ - pDb->stmtList = pPreStmt->pNext; - } - if( pPreStmt->pNext ){ - pPreStmt->pNext->pPrev = pPreStmt->pPrev; - }else{ - pDb->stmtLast = pPreStmt->pPrev; - } - pDb->nStmt--; - break; - } - } - - /* If no prepared statement was found. Compile the SQL text - */ - if( pStmt==0 ){ - if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, &zLeft) ){ - Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); - rc = TCL_ERROR; - break; - } - if( pStmt==0 ){ - if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){ - /* A compile-time error in the statement - */ - Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); - rc = TCL_ERROR; - break; - }else{ - /* The statement was a no-op. Continue to the next statement - ** in the SQL string. - */ - zSql = zLeft; - continue; - } - } - assert( pPreStmt==0 ); - } - - /* Bind values to parameters that begin with $ or : - */ - nVar = sqlite3_bind_parameter_count(pStmt); - nParm = 0; - if( nVar>sizeof(aParm)/sizeof(aParm[0]) ){ - apParm = (Tcl_Obj**)Tcl_Alloc(nVar*sizeof(apParm[0])); - }else{ - apParm = aParm; - } - for(i=1; i<=nVar; i++){ - const char *zVar = sqlite3_bind_parameter_name(pStmt, i); - if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){ - Tcl_Obj *pVar = Tcl_GetVar2Ex(interp, &zVar[1], 0, 0); - if( pVar ){ - int n; - u8 *data; - char *zType = pVar->typePtr ? pVar->typePtr->name : ""; - char c = zType[0]; - if( zVar[0]=='@' || - (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ - /* Load a BLOB type if the Tcl variable is a bytearray and - ** it has no string representation or the host - ** parameter name begins with "@". */ - data = Tcl_GetByteArrayFromObj(pVar, &n); - sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC); - Tcl_IncrRefCount(pVar); - apParm[nParm++] = pVar; - }else if( c=='b' && strcmp(zType,"boolean")==0 ){ - Tcl_GetIntFromObj(interp, pVar, &n); - sqlite3_bind_int(pStmt, i, n); - }else if( c=='d' && strcmp(zType,"double")==0 ){ - double r; - Tcl_GetDoubleFromObj(interp, pVar, &r); - sqlite3_bind_double(pStmt, i, r); - }else if( (c=='w' && strcmp(zType,"wideInt")==0) || - (c=='i' && strcmp(zType,"int")==0) ){ - Tcl_WideInt v; - Tcl_GetWideIntFromObj(interp, pVar, &v); - sqlite3_bind_int64(pStmt, i, v); - }else{ - data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); - sqlite3_bind_text(pStmt, i, (char *)data, n, SQLITE_STATIC); - Tcl_IncrRefCount(pVar); - apParm[nParm++] = pVar; - } - }else{ - sqlite3_bind_null( pStmt, i ); - } - } - } - - /* Execute the SQL - */ - while( rc==TCL_OK && pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ - - /* Compute column names. This must be done after the first successful - ** call to sqlite3_step(), in case the query is recompiled and the - ** number or names of the returned columns changes. - */ - assert(!pArray||pScript); - if (nCol < 0) { - Tcl_Obj ***ap = (pScript?&apColName:0); - nCol = computeColumnNames(interp, pStmt, ap, pArray); - } - - for(i=0; i=-2147483647 && v<=2147483647 ){ - pVal = Tcl_NewIntObj(v); - }else{ - pVal = Tcl_NewWideIntObj(v); - } - break; - } - case SQLITE_FLOAT: { - double r = sqlite3_column_double(pStmt, i); - pVal = Tcl_NewDoubleObj(r); - break; - } - case SQLITE_NULL: { - pVal = dbTextToObj(pDb->zNull); - break; - } - default: { - pVal = dbTextToObj((char *)sqlite3_column_text(pStmt, i)); - break; - } - } - - if( pScript ){ - if( pArray==0 ){ - Tcl_ObjSetVar2(interp, apColName[i], 0, pVal, 0); - }else{ - Tcl_ObjSetVar2(interp, pArray, apColName[i], pVal, 0); - } - }else if( choice==DB_ONECOLUMN ){ - assert( pRet==0 ); - if( pRet==0 ){ - pRet = pVal; - Tcl_IncrRefCount(pRet); - } - rc = TCL_BREAK; - i = nCol; - }else if( choice==DB_EXISTS ){ - Tcl_DecrRefCount(pRet); - pRet = Tcl_NewBooleanObj(1); - Tcl_IncrRefCount(pRet); - rc = TCL_BREAK; - i = nCol; - }else{ - Tcl_ListObjAppendElement(interp, pRet, pVal); - } - } - - if( pScript ){ - pDb->nStep = sqlite3_stmt_status(pStmt, - SQLITE_STMTSTATUS_FULLSCAN_STEP, 0); - pDb->nSort = sqlite3_stmt_status(pStmt, - SQLITE_STMTSTATUS_SORT, 0); - rc = Tcl_EvalObjEx(interp, pScript, 0); - if( rc==TCL_CONTINUE ){ - rc = TCL_OK; - } - } - } - if( rc==TCL_BREAK ){ - rc = TCL_OK; - } - - /* Free the column name objects */ - if( pScript ){ - /* If the query returned no rows, but an array variable was - ** specified, call computeColumnNames() now to populate the - ** arrayname(*) variable. - */ - if (pArray && nCol < 0) { - Tcl_Obj ***ap = (pScript?&apColName:0); - nCol = computeColumnNames(interp, pStmt, ap, pArray); - } - for(i=0; inStep = sqlite3_stmt_status(pStmt, - SQLITE_STMTSTATUS_FULLSCAN_STEP, 1); - pDb->nSort = sqlite3_stmt_status(pStmt, - SQLITE_STMTSTATUS_SORT, 1); - if( SQLITE_OK!=rc2 ){ - /* If a run-time error occurs, report the error and stop reading - ** the SQL - */ - Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); - sqlite3_finalize(pStmt); - rc = TCL_ERROR; - if( pPreStmt ) Tcl_Free((char*)pPreStmt); - break; - }else if( pDb->maxStmt<=0 ){ - /* If the cache is turned off, deallocated the statement */ - if( pPreStmt ) Tcl_Free((char*)pPreStmt); - sqlite3_finalize(pStmt); - }else{ - /* Everything worked and the cache is operational. - ** Create a new SqlPreparedStmt structure if we need one. - ** (If we already have one we can just reuse it.) - */ - if( pPreStmt==0 ){ - len = zLeft - zSql; - pPreStmt = (SqlPreparedStmt*)Tcl_Alloc( sizeof(*pPreStmt) ); - if( pPreStmt==0 ) return TCL_ERROR; - pPreStmt->pStmt = pStmt; - pPreStmt->nSql = len; - pPreStmt->zSql = sqlite3_sql(pStmt); - assert( strlen30(pPreStmt->zSql)==len ); - assert( 0==memcmp(pPreStmt->zSql, zSql, len) ); - } - - /* Add the prepared statement to the beginning of the cache list - */ - pPreStmt->pNext = pDb->stmtList; - pPreStmt->pPrev = 0; - if( pDb->stmtList ){ - pDb->stmtList->pPrev = pPreStmt; - } - pDb->stmtList = pPreStmt; - if( pDb->stmtLast==0 ){ - assert( pDb->nStmt==0 ); - pDb->stmtLast = pPreStmt; - }else{ - assert( pDb->nStmt>0 ); - } - pDb->nStmt++; - - /* If we have too many statement in cache, remove the surplus from the - ** end of the cache list. - */ - while( pDb->nStmt>pDb->maxStmt ){ - sqlite3_finalize(pDb->stmtLast->pStmt); - pDb->stmtLast = pDb->stmtLast->pPrev; - Tcl_Free((char*)pDb->stmtLast->pNext); - pDb->stmtLast->pNext = 0; - pDb->nStmt--; - } - } - - /* Proceed to the next statement */ - zSql = zLeft; - } - Tcl_DecrRefCount(objv[2]); - - if( pRet ){ - if( rc==TCL_OK ){ - Tcl_SetObjResult(interp, pRet); - } - Tcl_DecrRefCount(pRet); - }else if( rc==TCL_OK ){ - Tcl_ResetResult(interp); + */ + case DB_EVAL: { + if( objc<3 || objc>5 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?"); + return TCL_ERROR; + } + + if( objc==3 ){ + DbEvalContext sEval; + Tcl_Obj *pRet = Tcl_NewObj(); + Tcl_IncrRefCount(pRet); + dbEvalInit(&sEval, pDb, objv[2], 0); + while( TCL_OK==(rc = dbEvalStep(&sEval)) ){ + int i; + int nCol; + dbEvalRowInfo(&sEval, &nCol, 0); + for(i=0; inTransaction ){ - zBegin = "SAVEPOINT _tcl_transaction"; - }else if( pDb->nTransaction==0 && objc==4 ){ + if( pDb->nTransaction==0 && objc==4 ){ static const char *TTYPE_strs[] = { "deferred", "exclusive", "immediate", 0 }; enum TTYPE_enum { TTYPE_DEFERRED, TTYPE_EXCLUSIVE, TTYPE_IMMEDIATE @@ -2428,56 +2662,31 @@ case TTYPE_IMMEDIATE: zBegin = "BEGIN IMMEDIATE"; break; } } pScript = objv[objc-1]; + /* Run the SQLite BEGIN command to open a transaction or savepoint. */ pDb->disableAuth++; rc = sqlite3_exec(pDb->db, zBegin, 0, 0, 0); pDb->disableAuth--; if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); return TCL_ERROR; } - - pDb->nTransaction++; - rc = Tcl_EvalObjEx(interp, pScript, 0); - pDb->nTransaction--; - - if( rc!=TCL_ERROR ){ - if( pDb->nTransaction ){ - zEnd = "RELEASE _tcl_transaction"; - }else{ - zEnd = "COMMIT"; - } - }else{ - if( pDb->nTransaction ){ - zEnd = "ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction"; - }else{ - zEnd = "ROLLBACK"; - } - } - - pDb->disableAuth++; - if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){ - /* This is a tricky scenario to handle. The most likely cause of an - ** error is that the exec() above was an attempt to commit the - ** top-level transaction that returned SQLITE_BUSY. Or, less likely, - ** that an IO-error has occured. In either case, throw a Tcl exception - ** and try to rollback the transaction. - ** - ** But it could also be that the user executed one or more BEGIN, - ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing - ** this method's logic. Not clear how this would be best handled. - */ - if( rc!=TCL_ERROR ){ - Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); - rc = TCL_ERROR; - } - sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0); - } - pDb->disableAuth--; - + pDb->nTransaction++; + + /* If using NRE, schedule a callback to invoke the script pScript, then + ** a second callback to commit (or rollback) the transaction or savepoint + ** opened above. If not using NRE, evaluate the script directly, then + ** call function DbTransPostCmd() to commit (or rollback) the transaction + ** or savepoint. */ + if( DbUseNre() ){ + Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); + Tcl_NREvalObj(interp, pScript, 0); + }else{ + rc = DbTransPostCmd(&cd, interp, Tcl_EvalObjEx(interp, pScript, 0)); + } break; } /* ** $db unlock_notify ?script? @@ -2569,10 +2778,25 @@ } /* End of the SWITCH statement */ return rc; } +#if SQLITE_TCL_NRE +/* +** Adaptor that provides an objCmd interface to the NRE-enabled +** interface implementation. +*/ +static int DbObjCmdAdaptor( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ + return Tcl_NRCallObjProc(interp, DbObjCmd, cd, objc, objv); +} +#endif /* SQLITE_TCL_NRE */ + /* ** sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN? ** ?-create BOOLEAN? ?-nomutex BOOLEAN? ** ** This is the main Tcl command. When the "sqlite" Tcl command is @@ -2711,11 +2935,16 @@ return TCL_ERROR; } p->maxStmt = NUM_PREPARED_STMTS; p->interp = interp; zArg = Tcl_GetStringFromObj(objv[1], 0); - Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); + if( DbUseNre() ){ + Tcl_NRCreateCommand(interp, zArg, DbObjCmdAdaptor, DbObjCmd, + (char*)p, DbDeleteCmd); + }else{ + Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); + } return TCL_OK; } /* ** Provide a dummy Tcl_InitStubs if we are using this as a static @@ -2772,13 +3001,422 @@ EXTERN int Tclsqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;} #endif #ifdef TCLSH /***************************************************************************** -** The code that follows is used to build standalone TCL interpreters -** that are statically linked with SQLite. +** All of the code that follows is used to build standalone TCL interpreters +** that are statically linked with SQLite. Enable these by compiling +** with -DTCLSH=n where n can be 1 or 2. An n of 1 generates a standard +** tclsh but with SQLite built in. An n of 2 generates the SQLite space +** analysis program. +*/ + +#if defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* + * If compiled on a machine that doesn't have a 32-bit integer, + * you just set "uint32" to the appropriate datatype for an + * unsigned 32-bit integer. For example: + * + * cc -Duint32='unsigned long' md5.c + * + */ +#ifndef uint32 +# define uint32 unsigned int +#endif + +struct MD5Context { + int isInit; + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; +typedef struct MD5Context MD5Context; + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse (unsigned char *buf, unsigned longs){ + uint32 t; + do { + t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | + ((unsigned)buf[1]<<8 | buf[0]); + *(uint32 *)buf = t; + buf += 4; + } while (--longs); +} +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32 buf[4], const uint32 in[16]){ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void MD5Init(MD5Context *ctx){ + ctx->isInit = 1; + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static +void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = (unsigned char *)ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD5Final(unsigned char digest[16], MD5Context *ctx){ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; + ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse((unsigned char *)ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */ +} + +/* +** Convert a 128-bit MD5 digest into a 32-digit base-16 number. +*/ +static void MD5DigestToBase16(unsigned char *digest, char *zBuf){ + static char const zEncode[] = "0123456789abcdef"; + int i, j; + + for(j=i=0; i<16; i++){ + int a = digest[i]; + zBuf[j++] = zEncode[(a>>4)&0xf]; + zBuf[j++] = zEncode[a & 0xf]; + } + zBuf[j] = 0; +} + + +/* +** Convert a 128-bit MD5 digest into sequency of eight 5-digit integers +** each representing 16 bits of the digest and separated from each +** other by a "-" character. +*/ +static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ + int i, j; + unsigned int x; + for(i=j=0; i<16; i+=2){ + x = digest[i]*256 + digest[i+1]; + if( i>0 ) zDigest[j++] = '-'; + sprintf(&zDigest[j], "%05u", x); + j += 5; + } + zDigest[j] = 0; +} + +/* +** A TCL command for md5. The argument is the text to be hashed. The +** Result is the hash in base64. +*/ +static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){ + MD5Context ctx; + unsigned char digest[16]; + char zBuf[50]; + void (*converter)(unsigned char*, char*); + + if( argc!=2 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " TEXT\"", 0); + return TCL_ERROR; + } + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1])); + MD5Final(digest, &ctx); + converter = (void(*)(unsigned char*,char*))cd; + converter(digest, zBuf); + Tcl_AppendResult(interp, zBuf, (char*)0); + return TCL_OK; +} + +/* +** A TCL command to take the md5 hash of a file. The argument is the +** name of the file. +*/ +static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){ + FILE *in; + MD5Context ctx; + void (*converter)(unsigned char*, char*); + unsigned char digest[16]; + char zBuf[10240]; + + if( argc!=2 ){ + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + " FILENAME\"", 0); + return TCL_ERROR; + } + in = fopen(argv[1],"rb"); + if( in==0 ){ + Tcl_AppendResult(interp,"unable to open file \"", argv[1], + "\" for reading", 0); + return TCL_ERROR; + } + MD5Init(&ctx); + for(;;){ + int n; + n = fread(zBuf, 1, sizeof(zBuf), in); + if( n<=0 ) break; + MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n); + } + fclose(in); + MD5Final(digest, &ctx); + converter = (void(*)(unsigned char*,char*))cd; + converter(digest, zBuf); + Tcl_AppendResult(interp, zBuf, (char*)0); + return TCL_OK; +} + +/* +** Register the four new TCL commands for generating MD5 checksums +** with the TCL interpreter. +*/ +int Md5_Init(Tcl_Interp *interp){ + Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, + MD5DigestToBase16, 0); + Tcl_CreateCommand(interp, "md5-10x8", (Tcl_CmdProc*)md5_cmd, + MD5DigestToBase10x8, 0); + Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, + MD5DigestToBase16, 0); + Tcl_CreateCommand(interp, "md5file-10x8", (Tcl_CmdProc*)md5file_cmd, + MD5DigestToBase10x8, 0); + return TCL_OK; +} +#endif /* defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) */ + +#if defined(SQLITE_TEST) +/* +** During testing, the special md5sum() aggregate function is available. +** inside SQLite. The following routines implement that function. */ +static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){ + MD5Context *p; + int i; + if( argc<1 ) return; + p = sqlite3_aggregate_context(context, sizeof(*p)); + if( p==0 ) return; + if( !p->isInit ){ + MD5Init(p); + } + for(i=0; i=2 || TCLSH==2 ){ + if( argc>=2 ){ int i; char zArgc[32]; sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH)); Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY); @@ -2896,18 +3529,18 @@ Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); for(i=3-TCLSH; i #include @@ -1097,19 +1095,19 @@ sqlite3_result_int(context, p ? p->n : 0); } } } +#ifndef SQLITE_OMIT_DEPRECATED static void legacyCountStep( sqlite3_context *context, int argc, sqlite3_value **argv ){ /* no-op */ } -#ifndef SQLITE_OMIT_DEPRECATED static void legacyCountFinalize(sqlite3_context *context){ sqlite3_result_int(context, sqlite3_aggregate_count(context)); } #endif @@ -2307,20 +2305,24 @@ break; default: assert(0); } + sqlite3BeginBenignMalloc(); pVal = sqlite3ValueNew(0); - sqlite3ValueSetStr(pVal, nA, zA, encin, SQLITE_STATIC); - n = sqlite3_value_bytes(pVal); - Tcl_ListObjAppendElement(i,pX, - Tcl_NewStringObj((char*)sqlite3_value_text(pVal),n)); - sqlite3ValueSetStr(pVal, nB, zB, encin, SQLITE_STATIC); - n = sqlite3_value_bytes(pVal); - Tcl_ListObjAppendElement(i,pX, - Tcl_NewStringObj((char*)sqlite3_value_text(pVal),n)); - sqlite3ValueFree(pVal); + if( pVal ){ + sqlite3ValueSetStr(pVal, nA, zA, encin, SQLITE_STATIC); + n = sqlite3_value_bytes(pVal); + Tcl_ListObjAppendElement(i,pX, + Tcl_NewStringObj((char*)sqlite3_value_text(pVal),n)); + sqlite3ValueSetStr(pVal, nB, zB, encin, SQLITE_STATIC); + n = sqlite3_value_bytes(pVal); + Tcl_ListObjAppendElement(i,pX, + Tcl_NewStringObj((char*)sqlite3_value_text(pVal),n)); + sqlite3ValueFree(pVal); + } + sqlite3EndBenignMalloc(); Tcl_EvalObjEx(i, pX, 0); Tcl_DecrRefCount(pX); Tcl_GetIntFromObj(i, Tcl_GetObjResult(i), &res); return res; @@ -3297,10 +3299,11 @@ 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; rc = sqlite3_prepare(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0); + Tcl_ResetResult(interp); if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( zTail && objc>=5 ){ if( bytes>=0 ){ bytes = bytes - (zTail-zSql); } @@ -3354,10 +3357,11 @@ zSql = Tcl_GetString(objv[2]); if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, objc>=5 ? &zTail : 0); assert(rc==SQLITE_OK || pStmt==0); + Tcl_ResetResult(interp); if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( zTail && objc>=5 ){ if( bytes>=0 ){ bytes = bytes - (zTail-zSql); } @@ -4715,14 +4719,15 @@ { "SQLITE_LIMIT_VDBE_OP", SQLITE_LIMIT_VDBE_OP }, { "SQLITE_LIMIT_FUNCTION_ARG", SQLITE_LIMIT_FUNCTION_ARG }, { "SQLITE_LIMIT_ATTACHED", SQLITE_LIMIT_ATTACHED }, { "SQLITE_LIMIT_LIKE_PATTERN_LENGTH", SQLITE_LIMIT_LIKE_PATTERN_LENGTH }, { "SQLITE_LIMIT_VARIABLE_NUMBER", SQLITE_LIMIT_VARIABLE_NUMBER }, + { "SQLITE_LIMIT_TRIGGER_DEPTH", SQLITE_LIMIT_TRIGGER_DEPTH }, /* Out of range test cases */ { "SQLITE_LIMIT_TOOSMALL", -1, }, - { "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_VARIABLE_NUMBER+1 }, + { "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_TRIGGER_DEPTH+1 }, }; int i, id; int val; const char *zId; @@ -4860,16 +4865,48 @@ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif + +/* +** tcl_objproc COMMANDNAME ARGS... +** +** Run a TCL command using its objProc interface. Throw an error if +** the command has no objProc interface. +*/ +static int runAsObjProc( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + Tcl_CmdInfo cmdInfo; + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "COMMAND ..."); + return TCL_ERROR; + } + if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ + Tcl_AppendResult(interp, "command not found: ", + Tcl_GetString(objv[1]), (char*)0); + return TCL_ERROR; + } + if( cmdInfo.objProc==0 ){ + Tcl_AppendResult(interp, "command has no objProc: ", + Tcl_GetString(objv[1]), (char*)0); + return TCL_ERROR; + } + return cmdInfo.objProc(cmdInfo.objClientData, interp, objc-1, objv+1); +} + /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; + extern int sqlite3_found_count; extern int sqlite3_interrupt_count; extern int sqlite3_open_file_count; extern int sqlite3_sort_count; extern int sqlite3_current_time; #if SQLITE_OS_UNIX && defined(__APPLE__) @@ -4976,10 +5013,11 @@ { "sqlite3_limit", test_limit, 0}, { "save_prng_state", save_prng_state, 0 }, { "restore_prng_state", restore_prng_state, 0 }, { "reset_prng_state", reset_prng_state, 0 }, + { "tcl_objproc", runAsObjProc, 0 }, /* sqlite3_column_*() API */ { "sqlite3_column_count", test_column_count ,0 }, { "sqlite3_data_count", test_data_count ,0 }, { "sqlite3_column_type", test_column_type ,0 }, @@ -5086,10 +5124,12 @@ Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, aObjCmd[i].clientData, 0); } Tcl_LinkVar(interp, "sqlite_search_count", (char*)&sqlite3_search_count, TCL_LINK_INT); + Tcl_LinkVar(interp, "sqlite_found_count", + (char*)&sqlite3_found_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_sort_count", (char*)&sqlite3_sort_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite3_max_blobsize", (char*)&sqlite3_max_blobsize, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_like_count", Index: src/test2.c ================================================================== --- src/test2.c +++ src/test2.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** Code for testing the pager.c module in SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. -** -** $Id: test2.c,v 1.71 2009/06/18 17:22:39 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include @@ -53,10 +51,17 @@ /* ** Page size and reserved size used for testing. */ static int test_pagesize = 1024; + +/* +** Dummy page reinitializer +*/ +static void pager_test_reiniter(DbPage *pNotUsed){ + return; +} /* ** Usage: pager_open FILENAME N-PAGE ** ** Open a new pager @@ -77,11 +82,12 @@ " FILENAME N-PAGE\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR; rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB, + pager_test_reiniter); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } sqlite3PagerSetCachesize(pPager, nPage); @@ -340,11 +346,14 @@ " ID PGNO\"", 0); return TCL_ERROR; } pPager = sqlite3TestTextToPtr(argv[1]); if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; - rc = sqlite3PagerGet(pPager, pgno, &pPage); + rc = sqlite3PagerSharedLock(pPager); + if( rc==SQLITE_OK ){ + rc = sqlite3PagerGet(pPager, pgno, &pPage); + } if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage); @@ -621,11 +630,10 @@ extern int sqlite3_io_error_pending; extern int sqlite3_io_error_hit; extern int sqlite3_io_error_hardhit; extern int sqlite3_diskfull_pending; extern int sqlite3_diskfull; - extern int sqlite3_pager_n_sort_bucket; static struct { char *zName; Tcl_CmdProc *xProc; } aCmd[] = { { "pager_open", (Tcl_CmdProc*)pager_open }, @@ -666,9 +674,7 @@ (char*)&sqlite3_diskfull_pending, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull", (char*)&sqlite3_diskfull, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_pending_byte", (char*)&sqlite3PendingByte, TCL_LINK_INT | TCL_LINK_READ_ONLY); - Tcl_LinkVar(interp, "sqlite_pager_n_sort_bucket", - (char*)&sqlite3_pager_n_sort_bucket, TCL_LINK_INT); return TCL_OK; } Index: src/test3.c ================================================================== --- src/test3.c +++ src/test3.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** Code for testing the btree.c module in SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. -** -** $Id: test3.c,v 1.104 2009/05/04 11:42:30 danielk1977 Exp $ */ #include "sqliteInt.h" #include "btreeInt.h" #include "tcl.h" #include @@ -155,329 +153,10 @@ return TCL_ERROR; } return TCL_OK; } -/* -** Usage: btree_rollback ID -** -** Rollback changes -*/ -static int btree_rollback( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeRollback(pBt); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_commit ID -** -** Commit all changes -*/ -static int btree_commit( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeCommit(pBt); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_begin_statement ID -** -** Start a new statement transaction -*/ -static int btree_begin_statement( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeBeginStmt(pBt, 1); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_rollback_statement ID -** -** Rollback changes -*/ -static int btree_rollback_statement( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(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); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_commit_statement ID -** -** Commit all changes -*/ -static int btree_commit_statement( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_create_table ID FLAGS -** -** Create a new table in the database -*/ -static int btree_create_table( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc, iTable, flags; - char zBuf[30]; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID FLAGS\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &flags) ) return TCL_ERROR; - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeCreateTable(pBt, &iTable, flags); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iTable); - Tcl_AppendResult(interp, zBuf, 0); - return TCL_OK; -} - -/* -** Usage: btree_drop_table ID TABLENUM -** -** Delete an entire table from the database -*/ -static int btree_drop_table( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int iTable; - int rc; - int notUsed1; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID TABLENUM\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeDropTable(pBt, iTable, ¬Used1); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_clear_table ID TABLENUM -** -** Remove all entries from the given table but keep the table around. -*/ -static int btree_clear_table( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int iTable; - int rc; - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID TABLENUM\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; - sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeClearTable(pBt, iTable, 0); - sqlite3BtreeLeave(pBt); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return TCL_OK; -} - -/* -** Usage: btree_get_meta ID -** -** Return meta data -*/ -static int btree_get_meta( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int rc; - int i; - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - for(i=0; idb->mutex); return TCL_OK; } -/* -** Usage: btree_integrity_check ID ROOT ... -** -** Look through every page of the given BTree file to verify correct -** formatting and linkage. Return a line of text for each problem found. -** Return an empty string if everything worked. -*/ -static int btree_integrity_check( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - int nRoot; - int *aRoot; - int i; - int nErr; - char *zResult; - - if( argc<3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID ROOT ...\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - nRoot = argc-2; - aRoot = (int*)sqlite3_malloc( sizeof(int)*(argc-2) ); - for(i=0; ipBtree); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ - int iKey; - if( Tcl_GetInt(interp, argv[2], &iKey) ){ - sqlite3BtreeLeave(pCur->pBtree); - return TCL_ERROR; - } - rc = sqlite3BtreeMovetoUnpacked(pCur, 0, iKey, 0, &res); - }else{ - rc = sqlite3BtreeMoveto(pCur, argv[2], strlen(argv[2]), 0, &res); - } - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - if( res<0 ) res = -1; - if( res>0 ) res = 1; - sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_delete ID -** -** Delete the entry that the cursor is pointing to -*/ -static int btree_delete( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreeDelete(pCur); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return SQLITE_OK; -} - -/* -** Usage: btree_insert ID KEY DATA ?NZERO? -** -** Create a new entry with the given key and data. If an entry already -** exists with the same key the old entry is overwritten. -*/ -static int btree_insert( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - BtCursor *pCur; - int rc; - int nZero; - - if( objc!=4 && objc!=5 ){ - Tcl_WrongNumArgs(interp, 1, objv, "ID KEY DATA ?NZERO?"); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); - if( objc==5 ){ - if( Tcl_GetIntFromObj(interp, objv[4], &nZero) ) return TCL_ERROR; - }else{ - nZero = 0; - } - sqlite3BtreeEnter(pCur->pBtree); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ - i64 iKey; - int len; - unsigned char *pBuf; - if( Tcl_GetWideIntFromObj(interp, objv[2], &iKey) ){ - sqlite3BtreeLeave(pCur->pBtree); - return TCL_ERROR; - } - pBuf = Tcl_GetByteArrayFromObj(objv[3], &len); - rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0, 0); - }else{ - int keylen; - int dlen; - unsigned char *pKBuf; - unsigned char *pDBuf; - pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen); - pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen); - rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0, 0); - } - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - return SQLITE_OK; -} - /* ** Usage: btree_next ID ** ** Move the cursor to the next entry in the table. Return 0 on success ** or 1 if the cursor was already on the last entry in the table or if @@ -826,46 +308,10 @@ pCur = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pCur->pBtree); rc = sqlite3BtreeNext(pCur, &res); sqlite3BtreeLeave(pCur->pBtree); if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_prev ID -** -** Move the cursor to the previous entry in the table. Return 0 on -** success and 1 if the cursor was already on the first entry in -** the table or if the table was empty. -*/ -static int btree_prev( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int res = 0; - char zBuf[100]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreePrevious(pCur, &res); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); Tcl_AppendResult(interp, zBuf, 0); @@ -905,45 +351,10 @@ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); Tcl_AppendResult(interp, zBuf, 0); return SQLITE_OK; } -/* -** Usage: btree_last ID -** -** Move the cursor to the last entry in the table. Return 0 if the -** cursor was left point to something and 1 if the table is empty. -*/ -static int btree_last( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int res = 0; - char zBuf[100]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreeLast(pCur, &res); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - /* ** Usage: btree_eof ID ** ** Return TRUE if the given cursor is not pointing at a valid entry. ** Return FALSE if the cursor does point to a valid entry. @@ -970,206 +381,10 @@ sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc); Tcl_AppendResult(interp, zBuf, 0); return SQLITE_OK; } -/* -** Usage: btree_keysize ID -** -** Return the number of bytes of key. For an INTKEY table, this -** returns the key itself. -*/ -static int btree_keysize( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - u64 n; - char zBuf[50]; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeKeySize(pCur, (i64*)&n); - sqlite3BtreeLeave(pCur->pBtree); - sqlite3_snprintf(sizeof(zBuf),zBuf, "%llu", n); - Tcl_AppendResult(interp, zBuf, 0); - return SQLITE_OK; -} - -/* -** Usage: btree_key ID -** -** Return the key for the entry at which the cursor is pointing. -*/ -static int btree_key( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - u64 n; - char *zBuf; - - if( argc!=2 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeKeySize(pCur, (i64*)&n); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ - char zBuf2[60]; - sqlite3_snprintf(sizeof(zBuf2),zBuf2, "%llu", n); - Tcl_AppendResult(interp, zBuf2, 0); - }else{ - zBuf = sqlite3_malloc( n+1 ); - rc = sqlite3BtreeKey(pCur, 0, n, zBuf); - if( rc ){ - sqlite3BtreeLeave(pCur->pBtree); - Tcl_AppendResult(interp, errorName(rc), 0); - return TCL_ERROR; - } - zBuf[n] = 0; - Tcl_AppendResult(interp, zBuf, 0); - sqlite3_free(zBuf); - } - sqlite3BtreeLeave(pCur->pBtree); - return SQLITE_OK; -} - -/* -** Usage: btree_data ID ?N? -** -** Return the data for the entry at which the cursor is pointing. -*/ -static int btree_data( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - u32 n; - char *zBuf; - - if( argc!=2 && argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - sqlite3BtreeEnter(pCur->pBtree); - if( argc==2 ){ - sqlite3BtreeDataSize(pCur, &n); - }else{ - n = atoi(argv[2]); - } - zBuf = sqlite3_malloc( n+1 ); - rc = sqlite3BtreeData(pCur, 0, n, zBuf); - sqlite3BtreeLeave(pCur->pBtree); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - sqlite3_free(zBuf); - return TCL_ERROR; - } - zBuf[n] = 0; - Tcl_AppendResult(interp, zBuf, 0); - sqlite3_free(zBuf); - return SQLITE_OK; -} - -/* -** Usage: btree_fetch_key ID AMT -** -** Use the sqlite3BtreeKeyFetch() routine to get AMT bytes of the key. -** If sqlite3BtreeKeyFetch() fails, return an empty string. -*/ -static int btree_fetch_key( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int n; - int amt; - u64 nKey; - const char *zBuf; - char zStatic[1000]; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID AMT\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeKeySize(pCur, (i64*)&nKey); - zBuf = sqlite3BtreeKeyFetch(pCur, &amt); - if( zBuf && amt>=n ){ - assert( nKey0 ) nKey = n; - memcpy(zStatic, zBuf, (int)nKey); - zStatic[nKey] = 0; - Tcl_AppendResult(interp, zStatic, 0); - } - sqlite3BtreeLeave(pCur->pBtree); - return TCL_OK; -} - -/* -** Usage: btree_fetch_data ID AMT -** -** Use the sqlite3BtreeDataFetch() routine to get AMT bytes of the key. -** If sqlite3BtreeDataFetch() fails, return an empty string. -*/ -static int btree_fetch_data( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int n; - int amt; - u32 nData; - const char *zBuf; - char zStatic[1000]; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID AMT\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; - sqlite3BtreeEnter(pCur->pBtree); - sqlite3BtreeDataSize(pCur, &nData); - zBuf = sqlite3BtreeDataFetch(pCur, &amt); - if( zBuf && amt>=n ){ - assert( nData0 ) nData = n; - memcpy(zStatic, zBuf, (int)nData); - zStatic[nData] = 0; - Tcl_AppendResult(interp, zStatic, 0); - } - sqlite3BtreeLeave(pCur->pBtree); - return TCL_OK; -} - /* ** Usage: btree_payload_size ID ** ** Return the number of bytes of payload */ @@ -1189,175 +404,25 @@ " ID\"", 0); return TCL_ERROR; } pCur = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pCur->pBtree); - if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ + + /* The cursor may be in "require-seek" state. If this is the case, the + ** call to BtreeDataSize() will fix it. */ + sqlite3BtreeDataSize(pCur, (u32*)&n2); + if( pCur->apPage[pCur->iPage]->intKey ){ n1 = 0; }else{ sqlite3BtreeKeySize(pCur, (i64*)&n1); } - sqlite3BtreeDataSize(pCur, (u32*)&n2); sqlite3BtreeLeave(pCur->pBtree); sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2)); Tcl_AppendResult(interp, zBuf, 0); return SQLITE_OK; } -/* -** Usage: btree_cursor_info ID ?UP-CNT? -** -** Return integers containing information about the entry the -** cursor is pointing to: -** -** aResult[0] = The page number -** aResult[1] = The entry number -** aResult[2] = Total number of entries on this page -** aResult[3] = Cell size (local payload + header) -** aResult[4] = Number of free bytes on this page -** aResult[5] = Number of free blocks on the page -** aResult[6] = Total payload size (local + overflow) -** aResult[7] = Header size in bytes -** aResult[8] = Local payload size -** aResult[9] = Parent page number -** aResult[10]= Page number of the first overflow page -*/ -static int btree_cursor_info( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - BtCursor *pCur; - int rc; - int i, j; - int up; - int aResult[11]; - char zBuf[400]; - - if( argc!=2 && argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID ?UP-CNT?\"", 0); - return TCL_ERROR; - } - pCur = sqlite3TestTextToPtr(argv[1]); - if( argc==3 ){ - if( Tcl_GetInt(interp, argv[2], &up) ) return TCL_ERROR; - }else{ - up = 0; - } - sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreeCursorInfo(pCur, aResult, up); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - sqlite3BtreeLeave(pCur->pBtree); - return TCL_ERROR; - } - j = 0; - for(i=0; ipBtree); - Tcl_AppendResult(interp, &zBuf[1], 0); - return SQLITE_OK; -} - -/* -** Copied from btree.c: -*/ -static u32 t4Get4byte(unsigned char *p){ - return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; -} - -/* -** btree_ovfl_info BTREE CURSOR -** -** Given a cursor, return the sequence of pages number that form the -** overflow pages for the data of the entry that the cursor is point -** to. -*/ -static int btree_ovfl_info( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - Btree *pBt; - BtCursor *pCur; - Pager *pPager; - int rc; - int n; - int dataSize; - u32 pgno; - void *pPage; - int aResult[11]; - char zElem[100]; - Tcl_DString str; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " BTREE CURSOR", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - pCur = sqlite3TestTextToPtr(argv[2]); - if( (*(void**)pCur) != (void*)pBt ){ - Tcl_AppendResult(interp, "Cursor ", argv[2], " does not belong to btree ", - argv[1], 0); - return TCL_ERROR; - } - sqlite3BtreeEnter(pBt); - pPager = sqlite3BtreePager(pBt); - rc = sqlite3BtreeCursorInfo(pCur, aResult, 0); - if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); - sqlite3BtreeLeave(pBt); - return TCL_ERROR; - } - dataSize = pBt->pBt->usableSize; - Tcl_DStringInit(&str); - n = aResult[6] - aResult[8]; - n = (n + dataSize - 1)/dataSize; - pgno = (u32)aResult[10]; - while( pgno && n-- ){ - DbPage *pDbPage; - sprintf(zElem, "%d", pgno); - Tcl_DStringAppendElement(&str, zElem); - if( sqlite3PagerGet(pPager, pgno, &pDbPage)!=SQLITE_OK ){ - Tcl_DStringFree(&str); - Tcl_AppendResult(interp, "unable to get page ", zElem, 0); - sqlite3BtreeLeave(pBt); - return TCL_ERROR; - } - pPage = sqlite3PagerGetData(pDbPage); - pgno = t4Get4byte((unsigned char*)pPage); - sqlite3PagerUnref(pDbPage); - } - sqlite3BtreeLeave(pBt); - Tcl_DStringResult(interp, &str); - return SQLITE_OK; -} - -/* -** The command is provided for the purpose of setting breakpoints. -** in regression test scripts. -** -** By setting a GDB breakpoint on this procedure and executing the -** btree_breakpoint command in a test script, we can stop GDB at -** the point in the script where the btree_breakpoint command is -** inserted. This is useful for debugging. -*/ -static int btree_breakpoint( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - return TCL_OK; -} - /* ** usage: varint_test START MULTIPLIER COUNT INCREMENT ** ** This command tests the putVarint() and getVarint() ** routines, both for accuracy and for speed. @@ -1482,42 +547,10 @@ sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt); Tcl_SetResult(interp, zBuf, TCL_VOLATILE); return TCL_OK; } - -/* -** usage: btree_set_cache_size ID NCACHE -** -** Set the size of the cache used by btree $ID. -*/ -static int btree_set_cache_size( - void *NotUsed, - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int argc, /* Number of arguments */ - const char **argv /* Text of each argument */ -){ - int nCache; - Btree *pBt; - - if( argc!=3 ){ - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " BT NCACHE\"", 0); - return TCL_ERROR; - } - pBt = sqlite3TestTextToPtr(argv[1]); - if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; - - sqlite3_mutex_enter(pBt->db->mutex); - sqlite3BtreeEnter(pBt); - sqlite3BtreeSetCacheSize(pBt, nCache); - sqlite3BtreeLeave(pBt); - sqlite3_mutex_leave(pBt->db->mutex); - - return TCL_OK; -} - /* ** Usage: btree_ismemdb ID ** ** Return true if the B-Tree is in-memory. */ @@ -1543,10 +576,41 @@ sqlite3_mutex_leave(pBt->db->mutex); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res)); return SQLITE_OK; } +/* +** usage: btree_set_cache_size ID NCACHE +** +** Set the size of the cache used by btree $ID. +*/ +static int btree_set_cache_size( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + int nCache; + Btree *pBt; + + if( argc!=3 ){ + Tcl_AppendResult( + interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0); + return TCL_ERROR; + } + pBt = sqlite3TestTextToPtr(argv[1]); + if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; + + sqlite3_mutex_enter(pBt->db->mutex); + sqlite3BtreeEnter(pBt); + sqlite3BtreeSetCacheSize(pBt, nCache); + sqlite3BtreeLeave(pBt); + sqlite3_mutex_leave(pBt->db->mutex); + return TCL_OK; +} + + /* ** Register commands with the TCL interpreter. */ int Sqlitetest3_Init(Tcl_Interp *interp){ @@ -1555,54 +619,25 @@ Tcl_CmdProc *xProc; } aCmd[] = { { "btree_open", (Tcl_CmdProc*)btree_open }, { "btree_close", (Tcl_CmdProc*)btree_close }, { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction }, - { "btree_commit", (Tcl_CmdProc*)btree_commit }, - { "btree_rollback", (Tcl_CmdProc*)btree_rollback }, - { "btree_create_table", (Tcl_CmdProc*)btree_create_table }, - { "btree_drop_table", (Tcl_CmdProc*)btree_drop_table }, - { "btree_clear_table", (Tcl_CmdProc*)btree_clear_table }, - { "btree_get_meta", (Tcl_CmdProc*)btree_get_meta }, - { "btree_update_meta", (Tcl_CmdProc*)btree_update_meta }, { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats }, { "btree_cursor", (Tcl_CmdProc*)btree_cursor }, { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor }, - { "btree_move_to", (Tcl_CmdProc*)btree_move_to }, - { "btree_delete", (Tcl_CmdProc*)btree_delete }, { "btree_next", (Tcl_CmdProc*)btree_next }, - { "btree_prev", (Tcl_CmdProc*)btree_prev }, { "btree_eof", (Tcl_CmdProc*)btree_eof }, - { "btree_keysize", (Tcl_CmdProc*)btree_keysize }, - { "btree_key", (Tcl_CmdProc*)btree_key }, - { "btree_data", (Tcl_CmdProc*)btree_data }, - { "btree_fetch_key", (Tcl_CmdProc*)btree_fetch_key }, - { "btree_fetch_data", (Tcl_CmdProc*)btree_fetch_data }, { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, { "btree_first", (Tcl_CmdProc*)btree_first }, - { "btree_last", (Tcl_CmdProc*)btree_last }, - { "btree_integrity_check", (Tcl_CmdProc*)btree_integrity_check }, - { "btree_breakpoint", (Tcl_CmdProc*)btree_breakpoint }, { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, - { "btree_begin_statement", (Tcl_CmdProc*)btree_begin_statement }, - { "btree_commit_statement", (Tcl_CmdProc*)btree_commit_statement }, - { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement }, { "btree_from_db", (Tcl_CmdProc*)btree_from_db }, - { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size }, - { "btree_cursor_info", (Tcl_CmdProc*)btree_cursor_info }, - { "btree_ovfl_info", (Tcl_CmdProc*)btree_ovfl_info }, - { "btree_cursor_list", (Tcl_CmdProc*)btree_cursor_list }, { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb }, + { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size } }; int i; for(i=0; i @@ -647,10 +645,40 @@ sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db); threadset[i].db = 0; Tcl_AppendResult(interp, zBuf, (char*)0); return TCL_OK; } + +/* +** Usage: thread_db_put ID DB +** +*/ +static int tcl_thread_db_put( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + int i; + extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); + extern void *sqlite3TestTextToPtr(const char *); + if( argc!=3 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID DB", 0); + return TCL_ERROR; + } + i = parse_thread_id(interp, argv[1]); + if( i<0 ) return TCL_ERROR; + if( !threadset[i].busy ){ + Tcl_AppendResult(interp, "no such thread", 0); + return TCL_ERROR; + } + thread_wait(&threadset[i]); + assert( !threadset[i].db ); + threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]); + return TCL_OK; +} /* ** Usage: thread_stmt_get ID ** ** Return the database stmt pointer for the given thread. Then @@ -702,10 +730,11 @@ { "thread_compile", (Tcl_CmdProc*)tcl_thread_compile }, { "thread_step", (Tcl_CmdProc*)tcl_thread_step }, { "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize }, { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap }, { "thread_db_get", (Tcl_CmdProc*)tcl_thread_db_get }, + { "thread_db_put", (Tcl_CmdProc*)tcl_thread_db_put }, { "thread_stmt_get", (Tcl_CmdProc*)tcl_thread_stmt_get }, }; int i; for(i=0; i Index: src/test6.c ================================================================== --- src/test6.c +++ src/test6.c @@ -11,12 +11,10 @@ ****************************************************************************** ** ** This file contains code that modified the OS layer in order to simulate ** the effect on the database file of an OS crash or power failure. This ** is used to test the ability of SQLite to recover from those situations. -** -** $Id: test6.c,v 1.43 2009/02/11 14:27:04 danielk1977 Exp $ */ #if SQLITE_TEST /* This file is used for testing only */ #include "sqliteInt.h" #include "tcl.h" Index: src/test7.c ================================================================== --- src/test7.c +++ src/test7.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the client/server version of the SQLite library. ** Derived from test4.c. -** -** $Id: test7.c,v 1.13 2008/10/12 00:27:54 shane Exp $ */ #include "sqliteInt.h" #include "tcl.h" /* Index: src/test8.c ================================================================== --- src/test8.c +++ src/test8.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** Code for testing the virtual table interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. -** -** $Id: test8.c,v 1.78 2009/04/29 11:50:54 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include Index: src/test9.c ================================================================== --- src/test9.c +++ src/test9.c @@ -11,12 +11,10 @@ ************************************************************************* ** ** This file contains obscure tests of the C-interface required ** 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.7 2009/04/02 18:32:27 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include Index: src/test_async.c ================================================================== --- src/test_async.c +++ src/test_async.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** -** $Id: test_async.c,v 1.62 2009/04/28 13:01:09 drh Exp $ -** ** This file contains a binding of the asynchronous IO extension interface ** (defined in ext/async/sqlite3async.h) to Tcl. */ #define TCL_THREADS Index: src/test_autoext.c ================================================================== --- src/test_autoext.c +++ src/test_autoext.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Test extension for testing the sqlite3_auto_extension() function. -** -** $Id: test_autoext.c,v 1.5 2008/07/08 02:12:37 drh Exp $ */ #include "tcl.h" #include "sqlite3ext.h" #ifndef SQLITE_OMIT_LOAD_EXTENSION Index: src/test_backup.c ================================================================== --- src/test_backup.c +++ src/test_backup.c @@ -7,12 +7,12 @@ ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* +** This file contains test logic for the sqlite3_backup() interface. ** -** $Id: test_backup.c,v 1.3 2009/03/30 12:56:52 drh Exp $ */ #include "tcl.h" #include #include Index: src/test_btree.c ================================================================== --- src/test_btree.c +++ src/test_btree.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** Code for testing the btree.c module in SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. -** -** $Id: test_btree.c,v 1.8 2008/09/29 11:49:48 danielk1977 Exp $ */ #include "btreeInt.h" #include /* @@ -60,83 +58,5 @@ (pCur->eState==CURSOR_VALID) ? "" : " eof" ); } #endif } - - -/* -** Fill aResult[] with information about the entry and page that the -** cursor is pointing to. -** -** aResult[0] = The page number -** aResult[1] = The entry number -** aResult[2] = Total number of entries on this page -** aResult[3] = Cell size (local payload + header) -** aResult[4] = Number of free bytes on this page -** aResult[5] = Number of free blocks on the page -** aResult[6] = Total payload size (local + overflow) -** aResult[7] = Header size in bytes -** aResult[8] = Local payload size -** aResult[9] = Parent page number -** aResult[10]= Page number of the first overflow page -** -** This routine is used for testing and debugging only. -*/ -int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){ -#if 0 - int cnt, idx; - MemPage *pPage = pCur->apPage[pCur->iPage]; - BtCursor tmpCur; - int rc; - - if( pCur->eState==CURSOR_REQUIRESEEK ){ - rc = sqlite3BtreeRestoreCursorPosition(pCur); - if( rc!=SQLITE_OK ){ - return rc; - } - } - - assert( pPage->isInit ); - sqlite3BtreeGetTempCursor(pCur, &tmpCur); - while( upCnt-- ){ - sqlite3BtreeMoveToParent(&tmpCur); - } - pPage = tmpCur.pPage; - aResult[0] = sqlite3PagerPagenumber(pPage->pDbPage); - assert( aResult[0]==pPage->pgno ); - aResult[1] = tmpCur.idx; - aResult[2] = pPage->nCell; - if( tmpCur.idx>=0 && tmpCur.idxnCell ){ - sqlite3BtreeParseCell(tmpCur.pPage, tmpCur.idx, &tmpCur.info); - aResult[3] = tmpCur.info.nSize; - aResult[6] = tmpCur.info.nData; - aResult[7] = tmpCur.info.nHeader; - aResult[8] = tmpCur.info.nLocal; - }else{ - aResult[3] = 0; - aResult[6] = 0; - aResult[7] = 0; - aResult[8] = 0; - } - aResult[4] = pPage->nFree; - cnt = 0; - idx = get2byte(&pPage->aData[pPage->hdrOffset+1]); - while( idx>0 && idxpBt->usableSize ){ - cnt++; - idx = get2byte(&pPage->aData[idx]); - } - aResult[5] = cnt; - if( pPage->pParent==0 || sqlite3BtreeIsRootPage(pPage) ){ - aResult[9] = 0; - }else{ - aResult[9] = pPage->pParent->pgno; - } - if( tmpCur.info.iOverflow ){ - aResult[10] = get4byte(&tmpCur.info.pCell[tmpCur.info.iOverflow]); - }else{ - aResult[10] = 0; - } - sqlite3BtreeReleaseTempCursor(&tmpCur); -#endif - return SQLITE_OK; -} Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -13,12 +13,10 @@ ** This file contains code used for testing the SQLite system. ** None of the code in this file goes into a deliverable build. ** ** The focus of this file is providing the TCL testing layer ** access to compile-time constants. -** -** $Id: test_config.c,v 1.50 2009/06/19 14:06:03 drh Exp $ */ #include "sqliteLimit.h" #include "sqliteInt.h" @@ -393,10 +391,16 @@ #ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS Tcl_SetVar2(interp, "sqlite_options", "schema_version", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY); #endif + +#ifdef SQLITE_ENABLE_STAT2 + Tcl_SetVar2(interp, "sqlite_options", "stat2", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "stat2", "0", TCL_GLOBAL_ONLY); +#endif #if !defined(SQLITE_ENABLE_LOCKING_STYLE) # if defined(__APPLE__) # define SQLITE_ENABLE_LOCKING_STYLE 1 # else @@ -529,10 +533,11 @@ LINKVAR( MAX_FUNCTION_ARG ); LINKVAR( MAX_VARIABLE_NUMBER ); LINKVAR( MAX_PAGE_SIZE ); LINKVAR( MAX_PAGE_COUNT ); LINKVAR( MAX_LIKE_PATTERN_LENGTH ); + LINKVAR( MAX_TRIGGER_DEPTH ); LINKVAR( DEFAULT_TEMP_CACHE_SIZE ); LINKVAR( DEFAULT_CACHE_SIZE ); LINKVAR( DEFAULT_PAGE_SIZE ); LINKVAR( DEFAULT_FILE_FORMAT ); LINKVAR( MAX_ATTACHED ); Index: src/test_devsym.c ================================================================== --- src/test_devsym.c +++ src/test_devsym.c @@ -11,12 +11,10 @@ ****************************************************************************** ** ** This file contains code that modified the OS layer in order to simulate ** different device types (by overriding the return values of the ** xDeviceCharacteristics() and xSectorSize() methods). -** -** $Id: test_devsym.c,v 1.9 2008/12/09 01:32:03 drh Exp $ */ #if SQLITE_TEST /* This file is used for testing only */ #include "sqlite3.h" #include "sqliteInt.h" Index: src/test_func.c ================================================================== --- src/test_func.c +++ src/test_func.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** 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.15 2009/05/07 13:43:49 drh Exp $ */ #include "sqlite3.h" #include "tcl.h" #include #include @@ -154,17 +152,19 @@ 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){ +#ifndef SQLITE_OMIT_UTF16 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); +#endif } /* ** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata() ** interface. @@ -308,10 +308,112 @@ sqlite3_result_text(pCtx, zErr, -1, sqlite3_free); sqlite3_result_error_code(pCtx, rc); } } + +/* +** convert one character from hex to binary +*/ +static int testHexChar(char c){ + if( c>='0' && c<='9' ){ + return c - '0'; + }else if( c>='a' && c<='f' ){ + return c - 'a' + 10; + }else if( c>='A' && c<='F' ){ + return c - 'A' + 10; + } + return 0; +} + +/* +** Convert hex to binary. +*/ +static void testHexToBin(const char *zIn, char *zOut){ + while( zIn[0] && zIn[1] ){ + *(zOut++) = (testHexChar(zIn[0])<<4) + testHexChar(zIn[1]); + zIn += 2; + } +} + +/* +** hex_to_utf16be(HEX) +** +** Convert the input string from HEX into binary. Then return the +** result using sqlite3_result_text16le(). +*/ +static void testHexToUtf16be( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **argv +){ + int n; + const char *zIn; + char *zOut; + assert( nArg==1 ); + n = sqlite3_value_bytes(argv[0]); + zIn = (const char*)sqlite3_value_text(argv[0]); + zOut = sqlite3_malloc( n/2 ); + if( zOut==0 ){ + sqlite3_result_error_nomem(pCtx); + }else{ + testHexToBin(zIn, zOut); + sqlite3_result_text16be(pCtx, zOut, n/2, sqlite3_free); + } +} + +/* +** hex_to_utf8(HEX) +** +** Convert the input string from HEX into binary. Then return the +** result using sqlite3_result_text16le(). +*/ +static void testHexToUtf8( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **argv +){ + int n; + const char *zIn; + char *zOut; + assert( nArg==1 ); + n = sqlite3_value_bytes(argv[0]); + zIn = (const char*)sqlite3_value_text(argv[0]); + zOut = sqlite3_malloc( n/2 ); + if( zOut==0 ){ + sqlite3_result_error_nomem(pCtx); + }else{ + testHexToBin(zIn, zOut); + sqlite3_result_text(pCtx, zOut, n/2, sqlite3_free); + } +} + +/* +** hex_to_utf16le(HEX) +** +** Convert the input string from HEX into binary. Then return the +** result using sqlite3_result_text16le(). +*/ +static void testHexToUtf16le( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **argv +){ + int n; + const char *zIn; + char *zOut; + assert( nArg==1 ); + n = sqlite3_value_bytes(argv[0]); + zIn = (const char*)sqlite3_value_text(argv[0]); + zOut = sqlite3_malloc( n/2 ); + if( zOut==0 ){ + sqlite3_result_error_nomem(pCtx); + }else{ + testHexToBin(zIn, zOut); + sqlite3_result_text16le(pCtx, zOut, n/2, sqlite3_free); + } +} static int registerTestFunctions(sqlite3 *db){ static const struct { char *zName; signed char nArg; @@ -320,11 +422,14 @@ } aFuncs[] = { { "randstr", 2, SQLITE_UTF8, randStr }, { "test_destructor", 1, SQLITE_UTF8, test_destructor}, #ifndef SQLITE_OMIT_UTF16 { "test_destructor16", 1, SQLITE_UTF8, test_destructor16}, + { "hex_to_utf16be", 1, SQLITE_UTF8, testHexToUtf16be}, + { "hex_to_utf16le", 1, SQLITE_UTF8, testHexToUtf16le}, #endif + { "hex_to_utf8", 1, SQLITE_UTF8, testHexToUtf8}, { "test_destructor_count", 0, SQLITE_UTF8, test_destructor_count}, { "test_auxdata", -1, SQLITE_UTF8, test_auxdata}, { "test_error", 1, SQLITE_UTF8, test_error}, { "test_error", 2, SQLITE_UTF8, test_error}, { "test_eval", 1, SQLITE_UTF8, test_eval}, @@ -442,12 +547,10 @@ abuse_err: Tcl_AppendResult(interp, "sqlite3_create_function abused test failed", (char*)0); return TCL_ERROR; } - - /* ** Register commands with the TCL interpreter. */ int Sqlitetest_func_Init(Tcl_Interp *interp){ Index: src/test_hexio.c ================================================================== --- src/test_hexio.c +++ src/test_hexio.c @@ -14,12 +14,10 @@ ** database files and displaying the content of those files as ** hexadecimal. We could, in theory, use the built-in "binary" ** command of TCL to do a lot of this, but there are some issues ** with historical versions of the "binary" command. So it seems ** easier and safer to build our own mechanism. -** -** $Id: test_hexio.c,v 1.7 2008/05/12 16:17:42 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include ADDED src/test_init.c Index: src/test_init.c ================================================================== --- /dev/null +++ src/test_init.c @@ -0,0 +1,287 @@ +/* +** 2009 August 17 +** +** 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. +** +************************************************************************* +** +** The code in this file is used for testing SQLite. It is not part of +** the source code used in production systems. +** +** Specifically, this file tests the effect of errors while initializing +** the various pluggable sub-systems from within sqlite3_initialize(). +** If an error occurs in sqlite3_initialize() the following should be +** true: +** +** 1) An error code is returned to the user, and +** 2) A subsequent call to sqlite3_shutdown() calls the shutdown method +** of those subsystems that were initialized, and +** 3) A subsequent call to sqlite3_initialize() attempts to initialize +** the remaining, uninitialized, subsystems. +*/ + +#include "sqliteInt.h" +#include +#include + +static struct Wrapped { + sqlite3_pcache_methods pcache; + sqlite3_mem_methods mem; + sqlite3_mutex_methods mutex; + + int mem_init; /* True if mem subsystem is initalized */ + int mem_fail; /* True to fail mem subsystem inialization */ + int mutex_init; /* True if mutex subsystem is initalized */ + int mutex_fail; /* True to fail mutex subsystem inialization */ + int pcache_init; /* True if pcache subsystem is initalized */ + int pcache_fail; /* True to fail pcache subsystem inialization */ +} wrapped; + +static int wrMemInit(void *pAppData){ + int rc; + if( wrapped.mem_fail ){ + rc = SQLITE_ERROR; + }else{ + rc = wrapped.mem.xInit(wrapped.mem.pAppData); + } + if( rc==SQLITE_OK ){ + wrapped.mem_init = 1; + } + return rc; +} +static void wrMemShutdown(void *pAppData){ + wrapped.mem.xShutdown(wrapped.mem.pAppData); + wrapped.mem_init = 0; +} +static void *wrMemMalloc(int n) {return wrapped.mem.xMalloc(n);} +static void wrMemFree(void *p) {wrapped.mem.xFree(p);} +static void *wrMemRealloc(void *p, int n) {return wrapped.mem.xRealloc(p, n);} +static int wrMemSize(void *p) {return wrapped.mem.xSize(p);} +static int wrMemRoundup(int n) {return wrapped.mem.xRoundup(n);} + + +static int wrMutexInit(void){ + int rc; + if( wrapped.mutex_fail ){ + rc = SQLITE_ERROR; + }else{ + rc = wrapped.mutex.xMutexInit(); + } + if( rc==SQLITE_OK ){ + wrapped.mutex_init = 1; + } + return rc; +} +static int wrMutexEnd(void){ + wrapped.mutex.xMutexEnd(); + wrapped.mutex_init = 0; + return SQLITE_OK; +} +static sqlite3_mutex *wrMutexAlloc(int e){ + return wrapped.mutex.xMutexAlloc(e); +} +static void wrMutexFree(sqlite3_mutex *p){ + wrapped.mutex.xMutexFree(p); +} +static void wrMutexEnter(sqlite3_mutex *p){ + wrapped.mutex.xMutexEnter(p); +} +static int wrMutexTry(sqlite3_mutex *p){ + return wrapped.mutex.xMutexTry(p); +} +static void wrMutexLeave(sqlite3_mutex *p){ + wrapped.mutex.xMutexLeave(p); +} +static int wrMutexHeld(sqlite3_mutex *p){ + return wrapped.mutex.xMutexHeld(p); +} +static int wrMutexNotheld(sqlite3_mutex *p){ + return wrapped.mutex.xMutexNotheld(p); +} + + + +static int wrPCacheInit(void *pArg){ + int rc; + if( wrapped.pcache_fail ){ + rc = SQLITE_ERROR; + }else{ + rc = wrapped.pcache.xInit(wrapped.pcache.pArg); + } + if( rc==SQLITE_OK ){ + wrapped.pcache_init = 1; + } + return rc; +} +static void wrPCacheShutdown(void *pArg){ + wrapped.pcache.xShutdown(wrapped.pcache.pArg); + wrapped.pcache_init = 0; +} + +static sqlite3_pcache *wrPCacheCreate(int a, int b){ + return wrapped.pcache.xCreate(a, b); +} +static void wrPCacheCachesize(sqlite3_pcache *p, int n){ + wrapped.pcache.xCachesize(p, n); +} +static int wrPCachePagecount(sqlite3_pcache *p){ + return wrapped.pcache.xPagecount(p); +} +static void *wrPCacheFetch(sqlite3_pcache *p, unsigned a, int b){ + return wrapped.pcache.xFetch(p, a, b); +} +static void wrPCacheUnpin(sqlite3_pcache *p, void *a, int b){ + wrapped.pcache.xUnpin(p, a, b); +} +static void wrPCacheRekey(sqlite3_pcache *p, void *a, unsigned b, unsigned c){ + wrapped.pcache.xRekey(p, a, b, c); +} +static void wrPCacheTruncate(sqlite3_pcache *p, unsigned a){ + wrapped.pcache.xTruncate(p, a); +} +static void wrPCacheDestroy(sqlite3_pcache *p){ + wrapped.pcache.xDestroy(p); +} + +static void installInitWrappers(void){ + sqlite3_mutex_methods mutexmethods = { + wrMutexInit, wrMutexEnd, wrMutexAlloc, + wrMutexFree, wrMutexEnter, wrMutexTry, + wrMutexLeave, wrMutexHeld, wrMutexNotheld + }; + sqlite3_pcache_methods pcachemethods = { + 0, + wrPCacheInit, wrPCacheShutdown, wrPCacheCreate, + wrPCacheCachesize, wrPCachePagecount, wrPCacheFetch, + wrPCacheUnpin, wrPCacheRekey, wrPCacheTruncate, + wrPCacheDestroy + }; + sqlite3_mem_methods memmethods = { + wrMemMalloc, wrMemFree, wrMemRealloc, + wrMemSize, wrMemRoundup, wrMemInit, + wrMemShutdown, + 0 + }; + + memset(&wrapped, 0, sizeof(wrapped)); + + sqlite3_shutdown(); + sqlite3_config(SQLITE_CONFIG_GETMUTEX, &wrapped.mutex); + sqlite3_config(SQLITE_CONFIG_GETMALLOC, &wrapped.mem); + sqlite3_config(SQLITE_CONFIG_GETPCACHE, &wrapped.pcache); + sqlite3_config(SQLITE_CONFIG_MUTEX, &mutexmethods); + sqlite3_config(SQLITE_CONFIG_MALLOC, &memmethods); + sqlite3_config(SQLITE_CONFIG_PCACHE, &pcachemethods); +} + +static int init_wrapper_install( + ClientData clientData, /* Unused */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int i; + installInitWrappers(); + for(i=1; i +#include + + +/* +** Definition of the sqlite3_intarray object. +** +** The internal representation of an intarray object is subject +** to change, is not externally visible, and should be used by +** the implementation of intarray only. This object is opaque +** to users. +*/ +struct sqlite3_intarray { + int n; /* Number of elements in the array */ + sqlite3_int64 *a; /* Contents of the array */ + void (*xFree)(void*); /* Function used to free a[] */ +}; + +/* Objects used internally by the virtual table implementation */ +typedef struct intarray_vtab intarray_vtab; +typedef struct intarray_cursor intarray_cursor; + +/* A intarray table object */ +struct intarray_vtab { + sqlite3_vtab base; /* Base class */ + sqlite3_intarray *pContent; /* Content of the integer array */ +}; + +/* A intarray cursor object */ +struct intarray_cursor { + sqlite3_vtab_cursor base; /* Base class */ + int i; /* Current cursor position */ +}; + +/* +** None of this works unless we have virtual tables. +*/ +#ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** Free an sqlite3_intarray object. +*/ +static void intarrayFree(sqlite3_intarray *p){ + if( p->xFree ){ + p->xFree(p->a); + } + sqlite3_free(p); +} + +/* +** Table destructor for the intarray module. +*/ +static int intarrayDestroy(sqlite3_vtab *p){ + intarray_vtab *pVtab = (intarray_vtab*)p; + sqlite3_free(pVtab); + return 0; +} + +/* +** Table constructor for the intarray module. +*/ +static int intarrayCreate( + sqlite3 *db, /* Database where module is created */ + void *pAux, /* clientdata for the module */ + int argc, /* Number of arguments */ + const char *const*argv, /* Value for all arguments */ + sqlite3_vtab **ppVtab, /* Write the new virtual table object here */ + char **pzErr /* Put error message text here */ +){ + int rc = SQLITE_NOMEM; + intarray_vtab *pVtab = sqlite3_malloc(sizeof(intarray_vtab)); + + if( pVtab ){ + memset(pVtab, 0, sizeof(intarray_vtab)); + pVtab->pContent = (sqlite3_intarray*)pAux; + rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"); + } + *ppVtab = (sqlite3_vtab *)pVtab; + return rc; +} + +/* +** Open a new cursor on the intarray table. +*/ +static int intarrayOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + int rc = SQLITE_NOMEM; + intarray_cursor *pCur; + pCur = sqlite3_malloc(sizeof(intarray_cursor)); + if( pCur ){ + memset(pCur, 0, sizeof(intarray_cursor)); + *ppCursor = (sqlite3_vtab_cursor *)pCur; + rc = SQLITE_OK; + } + return rc; +} + +/* +** Close a intarray table cursor. +*/ +static int intarrayClose(sqlite3_vtab_cursor *cur){ + intarray_cursor *pCur = (intarray_cursor *)cur; + sqlite3_free(pCur); + return SQLITE_OK; +} + +/* +** Retrieve a column of data. +*/ +static int intarrayColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + intarray_cursor *pCur = (intarray_cursor*)cur; + intarray_vtab *pVtab = (intarray_vtab*)cur->pVtab; + if( pCur->i>=0 && pCur->ipContent->n ){ + sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]); + } + return SQLITE_OK; +} + +/* +** Retrieve the current rowid. +*/ +static int intarrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + intarray_cursor *pCur = (intarray_cursor *)cur; + *pRowid = pCur->i; + return SQLITE_OK; +} + +static int intarrayEof(sqlite3_vtab_cursor *cur){ + intarray_cursor *pCur = (intarray_cursor *)cur; + intarray_vtab *pVtab = (intarray_vtab *)cur->pVtab; + return pCur->i>=pVtab->pContent->n; +} + +/* +** Advance the cursor to the next row. +*/ +static int intarrayNext(sqlite3_vtab_cursor *cur){ + intarray_cursor *pCur = (intarray_cursor *)cur; + pCur->i++; + return SQLITE_OK; +} + +/* +** Reset a intarray table cursor. +*/ +static int intarrayFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + intarray_cursor *pCur = (intarray_cursor *)pVtabCursor; + pCur->i = 0; + return SQLITE_OK; +} + +/* +** Analyse the WHERE condition. +*/ +static int intarrayBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + return SQLITE_OK; +} + +/* +** A virtual table module that merely echos method calls into TCL +** variables. +*/ +static sqlite3_module intarrayModule = { + 0, /* iVersion */ + intarrayCreate, /* xCreate - create a new virtual table */ + intarrayCreate, /* xConnect - connect to an existing vtab */ + intarrayBestIndex, /* xBestIndex - find the best query index */ + intarrayDestroy, /* xDisconnect - disconnect a vtab */ + intarrayDestroy, /* xDestroy - destroy a vtab */ + intarrayOpen, /* xOpen - open a cursor */ + intarrayClose, /* xClose - close a cursor */ + intarrayFilter, /* xFilter - configure scan constraints */ + intarrayNext, /* xNext - advance a cursor */ + intarrayEof, /* xEof */ + intarrayColumn, /* xColumn - read data */ + intarrayRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ + +/* +** Invoke this routine to create a specific instance of an intarray object. +** The new intarray object is returned by the 3rd parameter. +** +** Each intarray object corresponds to a virtual table in the TEMP table +** with a name of zName. +** +** Destroy the intarray object by dropping the virtual table. If not done +** explicitly by the application, the virtual table will be dropped implicitly +** by the system when the database connection is closed. +*/ +int sqlite3_intarray_create( + sqlite3 *db, + const char *zName, + sqlite3_intarray **ppReturn +){ + int rc; + sqlite3_intarray *p; + + *ppReturn = p = sqlite3_malloc( sizeof(*p) ); + if( p==0 ){ + return SQLITE_NOMEM; + } + memset(p, 0, sizeof(*p)); + rc = sqlite3_create_module_v2(db, zName, &intarrayModule, p, + (void(*)(void*))intarrayFree); + if( rc==SQLITE_OK ){ + char *zSql; + zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE temp.%Q USING %Q", + zName, zName); + rc = sqlite3_exec(db, zSql, 0, 0, 0); + sqlite3_free(zSql); + } + return rc; +} + +/* +** Bind a new array array of integers to a specific intarray object. +** +** The array of integers bound must be unchanged for the duration of +** any query against the corresponding virtual table. If the integer +** array does change or is deallocated undefined behavior will result. +*/ +int sqlite3_intarray_bind( + sqlite3_intarray *pIntArray, /* The intarray object to bind to */ + int nElements, /* Number of elements in the intarray */ + sqlite3_int64 *aElements, /* Content of the intarray */ + void (*xFree)(void*) /* How to dispose of the intarray when done */ +){ + if( pIntArray->xFree ){ + pIntArray->xFree(pIntArray->a); + } + pIntArray->n = nElements; + pIntArray->a = aElements; + pIntArray->xFree = xFree; + return SQLITE_OK; +} + + +/***************************************************************************** +** Everything below is interface for testing this module. +*/ +#ifdef SQLITE_TEST +#include + +/* +** Routines to encode and decode pointers +*/ +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); +extern int sqlite3TestTextToPtr(const char*); +extern int sqlite3TestMakePointerStr(Tcl_Interp*, char *zPtr, void*); +extern const char *sqlite3TestErrorName(int); + +/* +** sqlite3_intarray_create DB NAME +** +** Invoke the sqlite3_intarray_create interface. A string that becomes +** the first parameter to sqlite3_intarray_bind. +*/ +static int test_intarray_create( + ClientData clientData, /* Not used */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + const char *zName; + sqlite3_intarray *pArray; + int rc; + char zPtr[100]; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zName = Tcl_GetString(objv[2]); + rc = sqlite3_intarray_create(db, zName, &pArray); + if( rc!=SQLITE_OK ){ + assert( pArray==0 ); + Tcl_AppendResult(interp, sqlite3TestErrorName(rc), (char*)0); + return TCL_ERROR; + } + sqlite3TestMakePointerStr(interp, zPtr, pArray); + Tcl_AppendResult(interp, zPtr, (char*)0); + return TCL_OK; +} + +/* +** sqlite3_intarray_bind INTARRAY ?VALUE ...? +** +** Invoke the sqlite3_intarray_bind interface on the given array of integers. +*/ +static int test_intarray_bind( + ClientData clientData, /* Not used */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3_intarray *pArray; + int rc; + int i, n; + sqlite3_int64 *a; + + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "INTARRAY"); + return TCL_ERROR; + } + pArray = (sqlite3_intarray*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); + n = objc - 2; + a = sqlite3_malloc( sizeof(a[0])*n ); + if( a==0 ){ + Tcl_AppendResult(interp, "SQLITE_NOMEM", (char*)0); + return TCL_ERROR; + } + for(i=0; i #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 Index: src/test_malloc.c ================================================================== --- src/test_malloc.c +++ src/test_malloc.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** ** This file contains code used to implement test interfaces to the ** memory allocation subsystem. -** -** $Id: test_malloc.c,v 1.54 2009/04/07 11:21:29 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include @@ -1339,10 +1337,28 @@ } rc = faultsimInstall(isInstall); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); return TCL_OK; } + +/* +** sqlite3_install_memsys3 +*/ +static int test_install_memsys3( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc = SQLITE_MISUSE; +#ifdef SQLITE_ENABLE_MEMSYS3 + const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); + rc = sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetMemsys3()); +#endif + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + return TCL_OK; +} /* ** Register commands with the TCL interpreter. */ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ @@ -1376,14 +1392,15 @@ { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, + { "sqlite3_install_memsys3", test_install_memsys3 ,0 }, }; int i; for(i=0; i -#include -#include "sqlite3.h" - -/* - * If compiled on a machine that doesn't have a 32-bit integer, - * you just set "uint32" to the appropriate datatype for an - * unsigned 32-bit integer. For example: - * - * cc -Duint32='unsigned long' md5.c - * - */ -#ifndef uint32 -# define uint32 unsigned int -#endif - -struct Context { - int isInit; - uint32 buf[4]; - uint32 bits[2]; - unsigned char in[64]; -}; -typedef struct Context MD5Context; - -/* - * Note: this code is harmless on little-endian machines. - */ -static void byteReverse (unsigned char *buf, unsigned longs){ - uint32 t; - do { - t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | - ((unsigned)buf[1]<<8 | buf[0]); - *(uint32 *)buf = t; - buf += 4; - } while (--longs); -} -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void MD5Transform(uint32 buf[4], const uint32 in[16]){ - register uint32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -static void MD5Init(MD5Context *ctx){ - ctx->isInit = 1; - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -static -void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){ - struct Context *ctx = (struct Context *)pCtx; - uint32 t; - - /* Update bitcount */ - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if ( t ) { - unsigned char *p = (unsigned char *)ctx->in + t; - - t = 64-t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - buf += t; - len -= t; - } - - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -static void MD5Final(unsigned char digest[16], MD5Context *pCtx){ - struct Context *ctx = (struct Context *)pCtx; - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count-8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; - ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; - - MD5Transform(ctx->buf, (uint32 *)ctx->in); - byteReverse((unsigned char *)ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */ -} - -/* -** Convert a digest into base-16. digest should be declared as -** "unsigned char digest[16]" in the calling function. The MD5 -** digest is stored in the first 16 bytes. zBuf should -** be "char zBuf[33]". -*/ -static void DigestToBase16(unsigned char *digest, char *zBuf){ - static char const zEncode[] = "0123456789abcdef"; - int i, j; - - for(j=i=0; i<16; i++){ - int a = digest[i]; - zBuf[j++] = zEncode[(a>>4)&0xf]; - zBuf[j++] = zEncode[a & 0xf]; - } - zBuf[j] = 0; -} - -/* -** A TCL command for md5. The argument is the text to be hashed. The -** Result is the hash in base64. -*/ -static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){ - MD5Context ctx; - unsigned char digest[16]; - char zBuf[33]; - - if( argc!=2 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], - " TEXT\"", 0); - return TCL_ERROR; - } - MD5Init(&ctx); - MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1])); - MD5Final(digest, &ctx); - DigestToBase16(digest, zBuf); - Tcl_AppendResult(interp, zBuf, (char*)0); - return TCL_OK; -} - -/* -** A TCL command to take the md5 hash of a file. The argument is the -** name of the file. -*/ -static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){ - FILE *in; - MD5Context ctx; - unsigned char digest[16]; - char zBuf[10240]; - - if( argc!=2 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], - " FILENAME\"", 0); - return TCL_ERROR; - } - in = fopen(argv[1],"rb"); - if( in==0 ){ - Tcl_AppendResult(interp,"unable to open file \"", argv[1], - "\" for reading", 0); - return TCL_ERROR; - } - MD5Init(&ctx); - for(;;){ - int n; - n = fread(zBuf, 1, sizeof(zBuf), in); - if( n<=0 ) break; - MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n); - } - fclose(in); - MD5Final(digest, &ctx); - DigestToBase16(digest, zBuf); - Tcl_AppendResult(interp, zBuf, (char*)0); - return TCL_OK; -} - -/* -** Register the two TCL commands above with the TCL interpreter. -*/ -int Md5_Init(Tcl_Interp *interp){ - Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0); - Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0); - return TCL_OK; -} - -/* -** During testing, the special md5sum() aggregate function is available. -** inside SQLite. The following routines implement that function. -*/ -static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){ - MD5Context *p; - int i; - if( argc<1 ) return; - p = sqlite3_aggregate_context(context, sizeof(*p)); - if( p==0 ) return; - if( !p->isInit ){ - MD5Init(p); - } - for(i=0; i #include Index: src/test_schema.c ================================================================== --- src/test_schema.c +++ src/test_schema.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** Code for testing the virtual table interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. -** -** $Id: test_schema.c,v 1.15 2008/07/07 14:50:14 drh Exp $ */ /* 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. Index: src/test_server.c ================================================================== --- src/test_server.c +++ src/test_server.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** -** $Id: test_server.c,v 1.8 2008/06/26 10:41:19 danielk1977 Exp $ -** ** This file contains demonstration code. Nothing in this file gets compiled ** or linked into the SQLite library unless you use a non-standard option: ** ** -DSQLITE_SERVER=1 ** Index: src/test_tclvar.c ================================================================== --- src/test_tclvar.c +++ src/test_tclvar.c @@ -13,12 +13,10 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** The emphasis of this file is a virtual table that provides ** access to TCL variables. -** -** $Id: test_tclvar.c,v 1.17 2008/08/12 14:48:41 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include Index: src/test_thread.c ================================================================== --- src/test_thread.c +++ src/test_thread.c @@ -11,12 +11,10 @@ ************************************************************************* ** ** This file contains the implementation of some Tcl commands used to ** 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.15 2009/03/27 12:32:56 drh Exp $ */ #include "sqliteInt.h" #include Index: src/test_wsd.c ================================================================== --- src/test_wsd.c +++ src/test_wsd.c @@ -11,12 +11,10 @@ ************************************************************************* ** ** The code in this file contains sample implementations of the ** 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.4 2009/03/23 04:33:33 danielk1977 Exp $ */ #if defined(SQLITE_OMIT_WSD) && defined(SQLITE_TEST) #include "sqliteInt.h" Index: src/tokenize.c ================================================================== --- src/tokenize.c +++ src/tokenize.c @@ -12,12 +12,10 @@ ** An tokenizer for SQL ** ** This file contains C code that splits an SQL input string up into ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. -** -** $Id: tokenize.c,v 1.162 2009/06/23 20:28:54 drh Exp $ */ #include "sqliteInt.h" #include /* @@ -408,11 +406,11 @@ mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->activeVdbeCnt==0 ){ db->u1.isInterrupted = 0; } pParse->rc = SQLITE_OK; - pParse->zTail = pParse->zSql = zSql; + pParse->zTail = zSql; i = 0; assert( pzErrMsg!=0 ); pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc); if( pEngine==0 ){ db->mallocFailed = 1; Index: src/trigger.c ================================================================== --- src/trigger.c +++ src/trigger.c @@ -6,13 +6,11 @@ ** 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. ** ************************************************************************* -** -** -** $Id: trigger.c,v 1.141 2009/05/28 01:00:55 drh Exp $ +** This file contains the implementation for TRIGGERs */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_TRIGGER /* @@ -47,10 +45,14 @@ ** pTab as well as the triggers lised in pTab->pTrigger. */ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; Trigger *pList = 0; /* List of triggers to return */ + + if( pParse->disableTriggers ){ + return 0; + } if( pTmpSchema!=pTab->pSchema ){ HashElem *p; for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ Trigger *pTrig = (Trigger *)sqliteHashData(p); @@ -84,18 +86,18 @@ SrcList *pTableName,/* The name of the table/view the trigger applies to */ Expr *pWhen, /* WHEN clause */ int isTemp, /* True if the TEMPORARY keyword is present */ int noErr /* Suppress errors if the trigger already exists */ ){ - Trigger *pTrigger = 0; - Table *pTab; + Trigger *pTrigger = 0; /* The new trigger */ + Table *pTab; /* Table that the trigger fires off of */ char *zName = 0; /* Name of the trigger */ - sqlite3 *db = pParse->db; + sqlite3 *db = pParse->db; /* The database connection */ int iDb; /* The database to store the trigger in */ Token *pName; /* The unqualified db name */ - DbFixer sFix; - int iTabDb; + DbFixer sFix; /* State vector for the DB fixer */ + int iTabDb; /* Index of the database holding pTab */ assert( pName1!=0 ); /* pName1->z might be NULL, but not pName1 itself */ assert( pName2!=0 ); assert( op==TK_INSERT || op==TK_UPDATE || op==TK_DELETE ); assert( op>0 && op<0xff ); @@ -136,10 +138,21 @@ goto trigger_cleanup; } pTab = sqlite3SrcListLookup(pParse, pTableName); if( !pTab ){ /* The table does not exist. */ + if( db->init.iDb==1 ){ + /* Ticket #3810. + ** Normally, whenever a table is dropped, all associated triggers are + ** dropped too. But if a TEMP trigger is created on a non-TEMP table + ** and the table is dropped by a different database connection, the + ** trigger is not visible to the database connection that does the + ** drop so the trigger cannot be dropped. This results in an + ** "orphaned trigger" - a trigger whose associated table is missing. + */ + db->init.orphanTrigger = 1; + } goto trigger_cleanup; } if( IsVirtual(pTab) ){ sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); goto trigger_cleanup; @@ -206,11 +219,11 @@ } /* Build the Trigger object */ pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger)); if( pTrigger==0 ) goto trigger_cleanup; - pTrigger->name = zName; + pTrigger->zName = zName; zName = 0; pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName); pTrigger->pSchema = db->aDb[iDb].pSchema; pTrigger->pTabSchema = pTab->pSchema; pTrigger->op = (u8)op; @@ -249,18 +262,18 @@ Token nameToken; /* Trigger name for error reporting */ pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; - zName = pTrig->name; + zName = pTrig->zName; iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); pTrig->step_list = pStepList; while( pStepList ){ pStepList->pTrig = pTrig; pStepList = pStepList->pNext; } - nameToken.z = pTrig->name; + nameToken.z = pTrig->zName; nameToken.n = sqlite3Strlen30(nameToken.z); if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken) && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){ goto triggerfinish_cleanup; } @@ -335,11 +348,11 @@ ** ** If an OOM error occurs, NULL is returned and db->mallocFailed is set. */ static TriggerStep *triggerStepAllocate( sqlite3 *db, /* Database connection */ - int op, /* Trigger opcode */ + u8 op, /* Trigger opcode */ Token *pName /* The target name */ ){ TriggerStep *pTriggerStep; pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n); @@ -364,11 +377,11 @@ sqlite3 *db, /* The database connection */ Token *pTableName, /* Name of the table into which we insert */ IdList *pColumn, /* List of columns in pTableName to insert into */ ExprList *pEList, /* The VALUE clause: a list of values to be inserted */ Select *pSelect, /* A SELECT statement that supplies values */ - int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ + u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ ){ TriggerStep *pTriggerStep; assert(pEList == 0 || pSelect == 0); assert(pEList != 0 || pSelect != 0 || db->mallocFailed); @@ -396,11 +409,11 @@ TriggerStep *sqlite3TriggerUpdateStep( sqlite3 *db, /* The database connection */ Token *pTableName, /* Name of the table to be updated */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ - int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ + u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ ){ TriggerStep *pTriggerStep; pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName); if( pTriggerStep ){ @@ -438,11 +451,11 @@ ** Recursively delete a Trigger structure */ void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){ if( pTrigger==0 ) return; sqlite3DeleteTriggerStep(db, pTrigger->step_list); - sqlite3DbFree(db, pTrigger->name); + sqlite3DbFree(db, pTrigger->zName); sqlite3DbFree(db, pTrigger->table); sqlite3ExprDelete(db, pTrigger->pWhen); sqlite3IdListDelete(db, pTrigger->pColumns); sqlite3DbFree(db, pTrigger); } @@ -518,11 +531,11 @@ { int code = SQLITE_DROP_TRIGGER; const char *zDb = db->aDb[iDb].zName; const char *zTab = SCHEMA_TABLE(iDb); if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER; - if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) || + if( sqlite3AuthCheck(pParse, code, pTrigger->zName, pTable->zName, zDb) || sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ return; } } #endif @@ -545,15 +558,15 @@ }; sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3OpenMasterTable(pParse, iDb); base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); - sqlite3VdbeChangeP4(v, base+1, pTrigger->name, 0); + sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, 0); sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Close, 0, 0); - sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0); + sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0); if( pParse->nMem<3 ){ pParse->nMem = 3; } } } @@ -653,209 +666,412 @@ } return pSrc; } /* -** Generate VDBE code for zero or more statements inside the body of a -** trigger. +** Generate VDBE code for the statements inside the body of a single +** trigger. */ static int codeTriggerProgram( Parse *pParse, /* The parser context */ TriggerStep *pStepList, /* List of statements inside the trigger body */ - int orconfin /* Conflict algorithm. (OE_Abort, etc) */ + int orconf /* Conflict algorithm. (OE_Abort, etc) */ ){ - TriggerStep * pTriggerStep = pStepList; - int orconf; + TriggerStep *pStep; Vdbe *v = pParse->pVdbe; sqlite3 *db = pParse->db; - assert( pTriggerStep!=0 ); + assert( pParse->pTriggerTab && pParse->pToplevel ); + assert( pStepList ); assert( v!=0 ); - sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0); - VdbeComment((v, "begin trigger %s", pStepList->pTrig->name)); - while( pTriggerStep ){ - sqlite3ExprCacheClear(pParse); - orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; - pParse->trigStack->orconf = orconf; - switch( pTriggerStep->op ){ + for(pStep=pStepList; pStep; pStep=pStep->pNext){ + /* Figure out the ON CONFLICT policy that will be used for this step + ** of the trigger program. If the statement that caused this trigger + ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use + ** the ON CONFLICT policy that was specified as part of the trigger + ** step statement. Example: + ** + ** CREATE TRIGGER AFTER INSERT ON t1 BEGIN; + ** INSERT OR REPLACE INTO t2 VALUES(new.a, new.b); + ** END; + ** + ** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy + ** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy + */ + pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf; + + switch( pStep->op ){ case TK_UPDATE: { - SrcList *pSrc; - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); - sqlite3Update(pParse, pSrc, - sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), - sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf); - sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); + sqlite3Update(pParse, + targetSrcList(pParse, pStep), + sqlite3ExprListDup(db, pStep->pExprList, 0), + sqlite3ExprDup(db, pStep->pWhere, 0), + pParse->eOrconf + ); break; } case TK_INSERT: { - SrcList *pSrc; - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); - sqlite3Insert(pParse, pSrc, - sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), - sqlite3SelectDup(db, pTriggerStep->pSelect, 0), - sqlite3IdListDup(db, pTriggerStep->pIdList), orconf); - sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); + sqlite3Insert(pParse, + targetSrcList(pParse, pStep), + sqlite3ExprListDup(db, pStep->pExprList, 0), + sqlite3SelectDup(db, pStep->pSelect, 0), + sqlite3IdListDup(db, pStep->pIdList), + pParse->eOrconf + ); break; } case TK_DELETE: { - SrcList *pSrc; - sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3DeleteFrom(pParse, pSrc, - sqlite3ExprDup(db, pTriggerStep->pWhere, 0)); - sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); - break; - } - default: assert( pTriggerStep->op==TK_SELECT ); { - Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0); - if( ss ){ - SelectDest dest; - - sqlite3SelectDestInit(&dest, SRT_Discard, 0); - sqlite3Select(pParse, ss, &dest); - sqlite3SelectDelete(db, ss); - } - break; - } - } - pTriggerStep = pTriggerStep->pNext; - } - sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); - VdbeComment((v, "end trigger %s", pStepList->pTrig->name)); + sqlite3DeleteFrom(pParse, + targetSrcList(pParse, pStep), + sqlite3ExprDup(db, pStep->pWhere, 0) + ); + break; + } + default: assert( pStep->op==TK_SELECT ); { + SelectDest sDest; + Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0); + sqlite3SelectDestInit(&sDest, SRT_Discard, 0); + sqlite3Select(pParse, pSelect, &sDest); + sqlite3SelectDelete(db, pSelect); + break; + } + } + if( pStep->op!=TK_SELECT ){ + sqlite3VdbeAddOp0(v, OP_ResetCount); + } + } return 0; } + +#ifdef SQLITE_DEBUG +/* +** This function is used to add VdbeComment() annotations to a VDBE +** program. It is not used in production code, only for debugging. +*/ +static const char *onErrorText(int onError){ + switch( onError ){ + case OE_Abort: return "abort"; + case OE_Rollback: return "rollback"; + case OE_Fail: return "fail"; + case OE_Replace: return "replace"; + case OE_Ignore: return "ignore"; + case OE_Default: return "default"; + } + return "n/a"; +} +#endif + +/* +** Parse context structure pFrom has just been used to create a sub-vdbe +** (trigger program). If an error has occurred, transfer error information +** from pFrom to pTo. +*/ +static void transferParseError(Parse *pTo, Parse *pFrom){ + assert( pFrom->zErrMsg==0 || pFrom->nErr ); + assert( pTo->zErrMsg==0 || pTo->nErr ); + if( pTo->nErr==0 ){ + pTo->zErrMsg = pFrom->zErrMsg; + pTo->nErr = pFrom->nErr; + }else{ + sqlite3DbFree(pFrom->db, pFrom->zErrMsg); + } +} + +/* +** Create and populate a new TriggerPrg object with a sub-program +** implementing trigger pTrigger with ON CONFLICT policy orconf. +*/ +static TriggerPrg *codeRowTrigger( + Parse *pParse, /* Current parse context */ + Trigger *pTrigger, /* Trigger to code */ + Table *pTab, /* The table pTrigger is attached to */ + int orconf /* ON CONFLICT policy to code trigger program with */ +){ + Parse *pTop = sqlite3ParseToplevel(pParse); + sqlite3 *db = pParse->db; /* Database handle */ + TriggerPrg *pPrg; /* Value to return */ + Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */ + Vdbe *v; /* Temporary VM */ + NameContext sNC; /* Name context for sub-vdbe */ + SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ + Parse *pSubParse; /* Parse context for sub-vdbe */ + int iEndTrigger = 0; /* Label to jump to if WHEN is false */ + + assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); + + /* Allocate the TriggerPrg and SubProgram objects. To ensure that they + ** are freed if an error occurs, link them into the Parse.pTriggerPrg + ** list of the top-level Parse object sooner rather than later. */ + pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg)); + if( !pPrg ) return 0; + pPrg->pNext = pTop->pTriggerPrg; + pTop->pTriggerPrg = pPrg; + pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram)); + if( !pProgram ) return 0; + pProgram->nRef = 1; + pPrg->pTrigger = pTrigger; + pPrg->orconf = orconf; + pPrg->oldmask = 0xffffffff; + + /* Allocate and populate a new Parse context to use for coding the + ** trigger sub-program. */ + pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); + if( !pSubParse ) return 0; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pSubParse; + pSubParse->db = db; + pSubParse->pTriggerTab = pTab; + pSubParse->pToplevel = pTop; + pSubParse->zAuthContext = pTrigger->zName; + pSubParse->eTriggerOp = pTrigger->op; + + v = sqlite3GetVdbe(pSubParse); + if( v ){ + VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", + pTrigger->zName, onErrorText(orconf), + (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), + (pTrigger->op==TK_UPDATE ? "UPDATE" : ""), + (pTrigger->op==TK_INSERT ? "INSERT" : ""), + (pTrigger->op==TK_DELETE ? "DELETE" : ""), + pTab->zName + )); +#ifndef SQLITE_OMIT_TRACE + sqlite3VdbeChangeP4(v, -1, + sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC + ); +#endif + + /* If one was specified, code the WHEN clause. If it evaluates to false + ** (or NULL) the sub-vdbe is immediately halted by jumping to the + ** OP_Halt inserted at the end of the program. */ + if( pTrigger->pWhen ){ + pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0); + if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) + && db->mallocFailed==0 + ){ + iEndTrigger = sqlite3VdbeMakeLabel(v); + sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); + } + sqlite3ExprDelete(db, pWhen); + } + + /* Code the trigger program into the sub-vdbe. */ + codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); + + /* Insert an OP_Halt at the end of the sub-program. */ + if( iEndTrigger ){ + sqlite3VdbeResolveLabel(v, iEndTrigger); + } + sqlite3VdbeAddOp0(v, OP_Halt); + VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); + + transferParseError(pParse, pSubParse); + if( db->mallocFailed==0 ){ + pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); + } + pProgram->nMem = pSubParse->nMem; + pProgram->nCsr = pSubParse->nTab; + pProgram->token = (void *)pTrigger; + pPrg->oldmask = pSubParse->oldmask; + sqlite3VdbeDelete(v); + } + + assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); + assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); + sqlite3StackFree(db, pSubParse); + + return pPrg; +} + +/* +** Return a pointer to a TriggerPrg object containing the sub-program for +** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such +** TriggerPrg object exists, a new object is allocated and populated before +** being returned. +*/ +static TriggerPrg *getRowTrigger( + Parse *pParse, /* Current parse context */ + Trigger *pTrigger, /* Trigger to code */ + Table *pTab, /* The table trigger pTrigger is attached to */ + int orconf /* ON CONFLICT algorithm. */ +){ + Parse *pRoot = sqlite3ParseToplevel(pParse); + TriggerPrg *pPrg; + + assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); + + /* It may be that this trigger has already been coded (or is in the + ** process of being coded). If this is the case, then an entry with + ** a matching TriggerPrg.pTrigger field will be present somewhere + ** in the Parse.pTriggerPrg list. Search for such an entry. */ + for(pPrg=pRoot->pTriggerPrg; + pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf); + pPrg=pPrg->pNext + ); + + /* If an existing TriggerPrg could not be located, create a new one. */ + if( !pPrg ){ + pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); + } + + return pPrg; +} + +/* +** Generate code for the trigger program associated with trigger p on +** table pTab. The reg, orconf and ignoreJump parameters passed to this +** function are the same as those described in the header function for +** sqlite3CodeRowTrigger() +*/ +void sqlite3CodeRowTriggerDirect( + Parse *pParse, /* Parse context */ + Trigger *p, /* Trigger to code */ + Table *pTab, /* The table to code triggers from */ + int reg, /* Reg array containing OLD.* and NEW.* values */ + int orconf, /* ON CONFLICT policy */ + int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ +){ + Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ + TriggerPrg *pPrg; + pPrg = getRowTrigger(pParse, p, pTab, orconf); + assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); + + /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program + ** is a pointer to the sub-vdbe containing the trigger program. */ + if( pPrg ){ + sqlite3VdbeAddOp3(v, OP_Program, reg, ignoreJump, ++pParse->nMem); + pPrg->pProgram->nRef++; + sqlite3VdbeChangeP4(v, -1, (const char *)pPrg->pProgram, P4_SUBPROGRAM); + VdbeComment( + (v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf))); + + /* Set the P5 operand of the OP_Program instruction to non-zero if + ** recursive invocation of this trigger program is disallowed. Recursive + ** invocation is disallowed if (a) the sub-program is really a trigger, + ** not a foreign key action, and (b) the flag to enable recursive triggers + ** is clear. */ + sqlite3VdbeChangeP5(v, (u8)(p->zName && !(pParse->db->flags&SQLITE_RecTriggers))); + } +} /* -** This is called to code FOR EACH ROW triggers. -** -** When the code that this function generates is executed, the following -** must be true: -** -** 1. No cursors may be open in the main database. (But newIdx and oldIdx -** can be indices of cursors in temporary tables. See below.) -** -** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then -** a temporary vdbe cursor (index newIdx) must be open and pointing at -** a row containing values to be substituted for new.* expressions in the -** trigger program(s). -** -** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then -** a temporary vdbe cursor (index oldIdx) must be open and pointing at -** a row containing values to be substituted for old.* expressions in the -** trigger program(s). -** -** If they are not NULL, the piOldColMask and piNewColMask output variables -** are set to values that describe the columns used by the trigger program -** in the OLD.* and NEW.* tables respectively. If column N of the -** pseudo-table is read at least once, the corresponding bit of the output -** mask is set. If a column with an index greater than 32 is read, the -** output mask is set to the special value 0xffffffff. -** -*/ -int sqlite3CodeRowTrigger( +** This is called to code the required FOR EACH ROW triggers for an operation +** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE) +** is given by the op paramater. The tr_tm parameter determines whether the +** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then +** parameter pChanges is passed the list of columns being modified. +** +** If there are no triggers that fire at the specified time for the specified +** operation on pTab, this function is a no-op. +** +** The reg argument is the address of the first in an array of registers +** that contain the values substituted for the new.* and old.* references +** in the trigger program. If N is the number of columns in table pTab +** (a copy of pTab->nCol), then registers are populated as follows: +** +** Register Contains +** ------------------------------------------------------ +** reg+0 OLD.rowid +** reg+1 OLD.* value of left-most column of pTab +** ... ... +** reg+N OLD.* value of right-most column of pTab +** reg+N+1 NEW.rowid +** reg+N+2 OLD.* value of left-most column of pTab +** ... ... +** reg+N+N+1 NEW.* value of right-most column of pTab +** +** For ON DELETE triggers, the registers containing the NEW.* values will +** never be accessed by the trigger program, so they are not allocated or +** populated by the caller (there is no data to populate them with anyway). +** Similarly, for ON INSERT triggers the values stored in the OLD.* registers +** are never accessed, and so are not allocated by the caller. So, for an +** ON INSERT trigger, the value passed to this function as parameter reg +** is not a readable register, although registers (reg+N) through +** (reg+N+N+1) are. +** +** Parameter orconf is the default conflict resolution algorithm for the +** trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump +** is the instruction that control should jump to if a trigger program +** raises an IGNORE exception. +*/ +void 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 */ Table *pTab, /* The table to code triggers from */ - int newIdx, /* The indice of the "new" row to access */ - int oldIdx, /* The indice of the "old" row to access */ + int reg, /* The first in an array of registers (see above) */ int orconf, /* ON CONFLICT policy */ - int ignoreJump, /* Instruction to jump to for RAISE(IGNORE) */ - u32 *piOldColMask, /* OUT: Mask of columns used from the OLD.* table */ - u32 *piNewColMask /* OUT: Mask of columns used from the NEW.* table */ + int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ ){ - Trigger *p; - sqlite3 *db = pParse->db; - TriggerStack trigStackEntry; - - trigStackEntry.oldColMask = 0; - trigStackEntry.newColMask = 0; - - assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); - assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); - - assert(newIdx != -1 || oldIdx != -1); + Trigger *p; /* Used to iterate through pTrigger list */ + + assert( op==TK_UPDATE || op==TK_INSERT || op==TK_DELETE ); + assert( tr_tm==TRIGGER_BEFORE || tr_tm==TRIGGER_AFTER ); + assert( (op==TK_UPDATE)==(pChanges!=0) ); for(p=pTrigger; p; p=p->pNext){ - int fire_this = 0; /* Sanity checking: The schema for the trigger and for the table are ** always defined. The trigger must be in the same schema as the table ** or else it must be a TEMP trigger. */ assert( p->pSchema!=0 ); assert( p->pTabSchema!=0 ); - assert( p->pSchema==p->pTabSchema || p->pSchema==db->aDb[1].pSchema ); + assert( p->pSchema==p->pTabSchema + || p->pSchema==pParse->db->aDb[1].pSchema ); /* Determine whether we should code this trigger */ - if( - p->op==op && - p->tr_tm==tr_tm && - checkColumnOverlap(p->pColumns,pChanges) - ){ - TriggerStack *pS; /* Pointer to trigger-stack entry */ - for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){} - if( !pS ){ - fire_this = 1; - } -#if 0 /* Give no warning for recursive triggers. Just do not do them */ - else{ - sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)", - p->name); - return SQLITE_ERROR; - } -#endif - } - - if( fire_this ){ - int endTrigger; - Expr * whenExpr; - AuthContext sContext; - NameContext sNC; - -#ifndef SQLITE_OMIT_TRACE - sqlite3VdbeAddOp4(pParse->pVdbe, OP_Trace, 0, 0, 0, - sqlite3MPrintf(db, "-- TRIGGER %s", p->name), - P4_DYNAMIC); -#endif - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - - /* Push an entry on to the trigger stack */ - trigStackEntry.pTrigger = p; - trigStackEntry.newIdx = newIdx; - trigStackEntry.oldIdx = oldIdx; - trigStackEntry.pTab = pTab; - trigStackEntry.pNext = pParse->trigStack; - trigStackEntry.ignoreJump = ignoreJump; - pParse->trigStack = &trigStackEntry; - sqlite3AuthContextPush(pParse, &sContext, p->name); - - /* code the WHEN clause */ - endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); - whenExpr = sqlite3ExprDup(db, p->pWhen, 0); - if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){ - pParse->trigStack = trigStackEntry.pNext; - sqlite3ExprDelete(db, whenExpr); - return 1; - } - sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL); - sqlite3ExprDelete(db, whenExpr); - - codeTriggerProgram(pParse, p->step_list, orconf); - - /* Pop the entry off the trigger stack */ - pParse->trigStack = trigStackEntry.pNext; - sqlite3AuthContextPop(&sContext); - - sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger); - } - } - if( piOldColMask ) *piOldColMask |= trigStackEntry.oldColMask; - if( piNewColMask ) *piNewColMask |= trigStackEntry.newColMask; - return 0; -} + if( p->op==op + && p->tr_tm==tr_tm + && checkColumnOverlap(p->pColumns, pChanges) + ){ + sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); + } + } +} + +/* +** Triggers fired by UPDATE or DELETE statements may access values stored +** in the old.* pseudo-table. This function returns a 32-bit bitmask +** indicating which columns of the old.* table actually are used by +** triggers. This information may be used by the caller to avoid having +** to load the entire old.* record into memory when executing an UPDATE +** or DELETE command. +** +** Bit 0 of the returned mask is set if the left-most column of the +** table may be accessed using an old. reference. Bit 1 is set if +** the second leftmost column value is required, and so on. If there +** are more than 32 columns in the table, and at least one of the columns +** with an index greater than 32 may be accessed, 0xffffffff is returned. +** +** It is not possible to determine if the old.rowid column is accessed +** by triggers. The caller must always assume that it is. +** +** There is no equivalent function for new.* references. +*/ +u32 sqlite3TriggerOldmask( + Parse *pParse, /* Parse context */ + Trigger *pTrigger, /* List of triggers on table pTab */ + ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ + Table *pTab, /* The table to code triggers from */ + int orconf /* Default ON CONFLICT policy for trigger steps */ +){ + const int op = pChanges ? TK_UPDATE : TK_DELETE; + u32 mask = 0; + Trigger *p; + + for(p=pTrigger; p; p=p->pNext){ + if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){ + TriggerPrg *pPrg; + pPrg = getRowTrigger(pParse, p, pTab, orconf); + if( pPrg ){ + mask |= pPrg->oldmask; + } + } + } + + return mask; +} + #endif /* !defined(SQLITE_OMIT_TRIGGER) */ Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. -** -** $Id: update.c,v 1.204 2009/06/27 11:17:35 drh Exp $ */ #include "sqliteInt.h" #ifndef SQLITE_OMIT_VIRTUALTABLE /* Forward declaration */ @@ -51,12 +49,17 @@ ** ** Therefore, the P4 parameter is only required if the default value for ** the column is a literal number, string or null. The sqlite3ValueFromExpr() ** function is capable of transforming these types of expressions into ** sqlite3_value objects. +** +** If parameter iReg is not negative, code an OP_RealAffinity instruction +** on register iReg. This is used when an equivalent integer value is +** stored in place of an 8-byte floating point value in order to save +** space. */ -void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i){ +void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ assert( pTab!=0 ); if( !pTab->pSelect ){ sqlite3_value *pValue; u8 enc = ENC(sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; @@ -65,10 +68,15 @@ sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc, pCol->affinity, &pValue); if( pValue ){ sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); } +#ifndef SQLITE_OMIT_FLOATING_POINT + if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ + sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); + } +#endif } } /* ** Process an UPDATE statement. @@ -103,33 +111,27 @@ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ int j1; /* Addresses of jump instructions */ int okOnePass; /* True for one-pass algorithm without the FIFO */ + int hasFK; /* True if foreign key processing is required */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* Trying to update a view */ 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 */ - int iBeginBeforeTrigger = 0; /* Address of before trigger program */ - int iEndBeforeTrigger = 0; /* Exit of before trigger program */ - u32 old_col_mask = 0; /* Mask of OLD.* columns in use */ - u32 new_col_mask = 0; /* Mask of NEW.* columns in use */ - - int newIdx = -1; /* index of trigger "new" temp table */ - int oldIdx = -1; /* index of trigger "old" temp table */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ - int regData; /* New data for the row */ + int regNew; + int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ + int regRec; /* Register used for new table record to insert */ - sContext.pParse = 0; + memset(&sContext, 0, sizeof(sContext)); db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto update_cleanup; } assert( pTabList->nSrc==1 ); @@ -139,11 +141,11 @@ pTab = sqlite3SrcListLookup(pParse, pTabList); if( pTab==0 ) goto update_cleanup; iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); /* Figure out if we have any triggers and if the table being - ** updated is a view + ** updated is a view. */ #ifndef SQLITE_OMIT_TRIGGER pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0); isView = pTab->pSelect!=0; #else @@ -153,28 +155,20 @@ #ifdef SQLITE_OMIT_VIEW # undef isView # define isView 0 #endif - if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ - goto update_cleanup; - } if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto update_cleanup; + } + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ + goto update_cleanup; } aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); if( aXRef==0 ) goto update_cleanup; for(i=0; inCol; i++) aXRef[i] = -1; - /* If there are FOR EACH ROW triggers, allocate cursors for the - ** special OLD and NEW tables - */ - if( pTrigger ){ - newIdx = pParse->nTab++; - oldIdx = pParse->nTab++; - } - /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and ** allocate enough space, just in case. */ @@ -229,10 +223,12 @@ aXRef[j] = -1; } } #endif } + + hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid); /* Allocate memory for the array aRegIdx[]. There is one entry in the ** array for each index associated with table being updated. Fill in ** the value with a register number for indices that are to be used ** and with zero for unused indices. @@ -256,28 +252,11 @@ } } aRegIdx[j] = reg; } - /* Allocate a block of register used to store the change record - ** sent to sqlite3GenerateConstraintChecks(). There are either - ** one or two registers for holding the rowid. One rowid register - ** is used if chngRowid is false and two are used if chngRowid is - ** true. Following these are pTab->nCol register holding column - ** data. - */ - regOldRowid = regNewRowid = pParse->nMem + 1; - pParse->nMem += pTab->nCol + 1; - if( chngRowid ){ - regNewRowid++; - pParse->nMem++; - } - regData = regNewRowid+1; - - - /* Begin generating code. - */ + /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto update_cleanup; if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); sqlite3BeginWriteOperation(pParse, 1, iDb); @@ -290,45 +269,28 @@ pTabList = 0; goto update_cleanup; } #endif - /* Start the view context - */ + /* Allocate required registers. */ + regOldRowid = regNewRowid = ++pParse->nMem; + if( pTrigger || hasFK ){ + regOld = pParse->nMem + 1; + pParse->nMem += pTab->nCol; + } + if( chngRowid || pTrigger || hasFK ){ + regNewRowid = ++pParse->nMem; + } + regNew = pParse->nMem + 1; + pParse->nMem += pTab->nCol; + regRec = ++pParse->nMem; + + /* Start the view context. */ if( isView ){ sqlite3AuthContextPush(pParse, &sContext, pTab->zName); } - /* Generate the code for triggers. - */ - if( pTrigger ){ - int iGoto; - - /* Create pseudo-tables for NEW and OLD - */ - 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, 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, 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); - sqlite3VdbeJumpHere(v, iGoto); - } - /* If we are trying to update a view, realize that view into ** a ephemeral table. */ #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) if( isView ){ @@ -362,11 +324,11 @@ */ sqlite3WhereEnd(pWInfo); /* Initialize the count of updated rows */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack ){ + if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ regRowCount = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } if( !isView ){ @@ -395,15 +357,10 @@ (char*)pKey, P4_KEYINFO_HANDOFF); assert( pParse->nTab>iCur+i+1 ); } } } - - /* Jump back to this point if a trigger encounters an IGNORE constraint. */ - if( pTrigger ){ - sqlite3VdbeResolveLabel(v, addr); - } /* Top of the update loop */ if( okOnePass ){ int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid); addr = sqlite3VdbeAddOp0(v, OP_Goto); @@ -410,143 +367,121 @@ sqlite3VdbeJumpHere(v, a1); }else{ addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); } - if( pTrigger ){ - int regRowid; - int regRow; - int regCols; - - /* Make cursor iCur point to the record that is being updated. - */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - - /* Generate the OLD table - */ - regRowid = sqlite3GetTempReg(pParse); - regRow = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid); - if( !old_col_mask ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regRow); - }else{ - sqlite3VdbeAddOp2(v, OP_RowData, iCur, regRow); - } - sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, regRow, regRowid); - - /* Generate the NEW table - */ - if( chngRowid ){ - sqlite3ExprCodeAndCache(pParse, pRowidExpr, regRowid); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); - }else{ - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid); - } - regCols = sqlite3GetTempRange(pParse, pTab->nCol); - for(i=0; inCol; i++){ - if( i==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i); - continue; - } - j = aXRef[i]; - if( (i<32 && (new_col_mask&((u32)1<a[j].pExpr, regCols+i); - } - }else{ - sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i); - } - } - sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRow); - if( !isView ){ - sqlite3TableAffinityStr(v, pTab); - sqlite3ExprCacheAffinityChange(pParse, regCols, pTab->nCol); - } - sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol); - /* if( pParse->nErr ) goto update_cleanup; */ - sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRow, regRowid); - sqlite3ReleaseTempReg(pParse, regRowid); - sqlite3ReleaseTempReg(pParse, regRow); - - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger); - sqlite3VdbeJumpHere(v, iEndBeforeTrigger); - } - - if( !isView ){ - /* Loop over every record that needs updating. We have to load - ** the old data for each record to be updated because some columns - ** might not change and we will need to copy the old value. - ** Also, the old data is needed to delete the old index entries. - ** So make the cursor point at the old record. - */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - - /* If the record number will change, push the record number as it - ** will be after the update. (The old record number is currently - ** on top of the stack.) - */ - if( chngRowid ){ - sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); - } - - /* Compute new data for this record. - */ - for(i=0; inCol; i++){ - if( i==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regData+i); - continue; - } - j = aXRef[i]; - if( j<0 ){ - sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regData+i); - sqlite3ColumnDefault(v, pTab, i); - }else{ - sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regData+i); - } - } - - /* Do constraint checks - */ - sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, - aRegIdx, chngRowid, 1, - onError, addr, 0); - - /* Delete the old indices for the current record. - */ - j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); - sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); - - /* If changing the record number, delete the old record. - */ - if( chngRowid ){ + /* Make cursor iCur point to the record that is being updated. If + ** this record does not exist for some reason (deleted by a trigger, + ** for example, then jump to the next iteration of the RowSet loop. */ + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); + + /* If the record number will change, set register regNewRowid to + ** contain the new value. If the record number is not being modified, + ** then regNewRowid is the same register as regOldRowid, which is + ** already populated. */ + assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid ); + if( chngRowid ){ + sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); + } + + /* If there are triggers on this table, populate an array of registers + ** with the required old.* column data. */ + if( hasFK || pTrigger ){ + u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); + oldmask |= sqlite3TriggerOldmask(pParse, pTrigger, pChanges, pTab, onError); + for(i=0; inCol; i++){ + if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<nCol; i++){ + if( i==pTab->iPKey ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); + }else{ + j = aXRef[i]; + if( j<0 ){ + sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); + sqlite3ColumnDefault(v, pTab, i, regNew+i); + }else{ + sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); + } + } + } + + /* Fire any BEFORE UPDATE triggers. This happens before constraints are + ** verified. One could argue that this is wrong. */ + if( pTrigger ){ + sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol); + sqlite3TableAffinityStr(v, pTab); + sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, + TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); + + /* The row-trigger may have deleted the row being updated. In this + ** case, jump to the next row. No updates or AFTER triggers are + ** required. This behaviour - what happens when the row being updated + ** is deleted or renamed by a BEFORE trigger - is left undefined in the + ** documentation. */ + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); + } + + if( !isView ){ + + /* Do constraint checks. */ + sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, + aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0); + + /* Do FK constraint checks. */ + if( hasFK ){ + sqlite3FkCheck(pParse, pTab, regOldRowid, 0); + } + + /* Delete the index entries associated with the current record. */ + j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); + sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); + + /* If changing the record number, delete the old record. */ + if( hasFK || chngRowid ){ sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); } sqlite3VdbeJumpHere(v, j1); - /* Create the new index entries and the new record. - */ - sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, - aRegIdx, 1, -1, 0, 0); + if( hasFK ){ + sqlite3FkCheck(pParse, pTab, 0, regNewRowid); + } + + /* Insert the new index entries and the new record. */ + sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0); + + /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to + ** handle rows (possibly in other tables) that refer via a foreign key + ** to the row just updated. */ + if( hasFK ){ + sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); + } } /* Increment the row counter */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack){ + if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } - /* If there are triggers, close all the cursors after each iteration - ** through the loop. The fire the after triggers. - */ - if( pTrigger ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); - sqlite3VdbeJumpHere(v, iEndAfterTrigger); - } + sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, + TRIGGER_AFTER, pTab, regOldRowid, onError, addr); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); @@ -557,29 +492,25 @@ if( openAll || aRegIdx[i]>0 ){ sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0); } } sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); - if( pTrigger ){ - sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0); - sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0); - } /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ - if( pParse->nested==0 && pParse->trigStack==0 ){ + if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); } /* ** Return the number of rows that were changed. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){ + if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){ sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); } @@ -590,10 +521,19 @@ sqlite3SrcListDelete(db, pTabList); sqlite3ExprListDelete(db, pChanges); sqlite3ExprDelete(db, pWhere); return; } +/* Make sure "isView" and other macros defined above are undefined. Otherwise +** thely may interfere with compilation of other functions in this file +** (or in another file, if this file becomes part of the amalgamation). */ +#ifdef isView + #undef isView +#endif +#ifdef pTrigger + #undef pTrigger +#endif #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Generate code for an UPDATE of a virtual table. ** @@ -629,28 +569,27 @@ int ephemTab; /* Table holding the result of the SELECT */ int i; /* Loop counter */ int addr; /* Address of top of loop */ int iReg; /* First register in set passed to OP_VUpdate */ sqlite3 *db = pParse->db; /* Database connection */ - const char *pVtab = (const char*)pTab->pVtab; + const char *pVTab = (const char*)sqlite3GetVTable(db, pTab); SelectDest dest; /* Construct the SELECT statement that will find the new values for ** all updated rows. */ - pEList = sqlite3ExprListAppend(pParse, 0, - sqlite3CreateIdExpr(pParse, "_rowid_")); + pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_")); if( pRowid ){ pEList = sqlite3ExprListAppend(pParse, pEList, sqlite3ExprDup(db, pRowid, 0)); } assert( pTab->iPKey<0 ); for(i=0; inCol; i++){ if( aXRef[i]>=0 ){ pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0); }else{ - pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName); + pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName); } pEList = sqlite3ExprListAppend(pParse, pEList, pExpr); } pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); @@ -674,19 +613,15 @@ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1); for(i=0; inCol; i++){ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); } sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB); + sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); /* Cleanup */ sqlite3SelectDelete(db, pSelect); } #endif /* SQLITE_OMIT_VIRTUALTABLE */ - -/* Make sure "isView" gets undefined in case this file becomes part of -** the amalgamation - so that subsequent files do not see isView as a -** macro. */ -#undef isView Index: src/utf.c ================================================================== --- src/utf.c +++ src/utf.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** This file contains routines used to translate between UTF-8, ** UTF-16, UTF-16BE, and UTF-16LE. ** -** $Id: utf.c,v 1.73 2009/04/01 18:40:32 drh Exp $ -** ** Notes on UTF-8: ** ** Byte-0 Byte-1 Byte-2 Byte-3 Value ** 0xxxxxxx 00000000 00000000 0xxxxxxx ** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx @@ -105,24 +103,24 @@ *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ *zOut++ = (u8)(c&0x00FF); \ } \ } -#define READ_UTF16LE(zIn, c){ \ +#define READ_UTF16LE(zIn, TERM, c){ \ c = (*zIn++); \ c += ((*zIn++)<<8); \ - if( c>=0xD800 && c<0xE000 ){ \ + if( c>=0xD800 && c<0xE000 && TERM ){ \ int c2 = (*zIn++); \ c2 += ((*zIn++)<<8); \ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ } \ } -#define READ_UTF16BE(zIn, c){ \ +#define READ_UTF16BE(zIn, TERM, c){ \ c = ((*zIn++)<<8); \ c += (*zIn++); \ - if( c>=0xD800 && c<0xE000 ){ \ + if( c>=0xD800 && c<0xE000 && TERM ){ \ int c2 = ((*zIn++)<<8); \ c2 += (*zIn++); \ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ } \ } @@ -303,17 +301,17 @@ }else{ assert( desiredEnc==SQLITE_UTF8 ); if( pMem->enc==SQLITE_UTF16LE ){ /* UTF-16 Little-endian -> UTF-8 */ while( zIn UTF-8 */ while( zInn = (int)(z - zOut); } @@ -453,35 +451,53 @@ assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); return (m.flags & MEM_Dyn)!=0 ? m.z : sqlite3DbStrDup(db, m.z); } /* -** pZ is a UTF-16 encoded unicode string at least nChar characters long. +** Convert a UTF-8 string to the UTF-16 encoding specified by parameter +** enc. A pointer to the new string is returned, and the value of *pnOut +** is set to the length of the returned string in bytes. The call should +** arrange to call sqlite3DbFree() on the returned pointer when it is +** no longer required. +** +** If a malloc failure occurs, NULL is returned and the db.mallocFailed +** flag set. +*/ +#ifdef SQLITE_ENABLE_STAT2 +char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){ + Mem m; + memset(&m, 0, sizeof(m)); + m.db = db; + sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC); + if( sqlite3VdbeMemTranslate(&m, enc) ){ + assert( db->mallocFailed ); + return 0; + } + assert( m.z==m.zMalloc ); + *pnOut = m.n; + return m.z; +} +#endif + +/* +** zIn 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){ int c; unsigned char const *z = zIn; int n = 0; + if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){ - /* Using an "if (SQLITE_UTF16NATIVE==SQLITE_UTF16BE)" construct here - ** and in other parts of this file means that at one branch will - ** not be covered by coverage testing on any single host. But coverage - ** will be complete if the tests are run on both a little-endian and - ** big-endian host. Because both the UTF16NATIVE and SQLITE_UTF16BE - ** macros are constant at compile time the compiler can determine - ** which branch will be followed. It is therefore assumed that no runtime - ** penalty is paid for this "if" statement. - */ while( n0 && n<=4 ); z[0] = 0; z = zBuf; - READ_UTF16LE(z, c); + READ_UTF16LE(z, 1, c); assert( c==i ); assert( (z-zBuf)==n ); } for(i=0; i<0x00110000; i++){ if( i>=0xD800 && i<0xE000 ) continue; @@ -531,12 +547,12 @@ WRITE_UTF16BE(z, i); n = (int)(z-zBuf); assert( n>0 && n<=4 ); z[0] = 0; z = zBuf; - READ_UTF16BE(z, c); + READ_UTF16BE(z, 1, c); assert( c==i ); assert( (z-zBuf)==n ); } } #endif /* SQLITE_TEST */ #endif /* SQLITE_OMIT_UTF16 */ Index: src/util.c ================================================================== --- src/util.c +++ src/util.c @@ -12,11 +12,10 @@ ** Utility functions used throughout sqlite. ** ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.261 2009/06/24 10:26:33 drh Exp $ */ #include "sqliteInt.h" #include #ifdef SQLITE_HAVE_ISNAN # include @@ -223,11 +222,11 @@ a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } -int sqlite3StrNICmp(const char *zLeft, const char *zRight, int N){ +int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; @@ -271,11 +270,11 @@ } return *z==0; } /* -** The string z[] is an ascii representation of a real number. +** The string z[] is an ASCII representation of a real number. ** Convert this string to a double. ** ** This routine assumes that z[] really is a valid number. If it ** is not, the result is undefined. ** @@ -284,74 +283,130 @@ ** of "." depending on how locale is set. But that would cause problems ** for SQL. So this routine always uses "." regardless of locale. */ int sqlite3AtoF(const char *z, double *pResult){ #ifndef SQLITE_OMIT_FLOATING_POINT - int sign = 1; const char *zBegin = z; - LONGDOUBLE_TYPE v1 = 0.0; - int nSignificant = 0; + /* sign * significand * (10 ^ (esign * exponent)) */ + int sign = 1; /* sign of significand */ + i64 s = 0; /* significand */ + int d = 0; /* adjust exponent for shifting decimal point */ + int esign = 1; /* sign of exponent */ + int e = 0; /* exponent */ + double result; + int nDigits = 0; + + /* skip leading spaces */ while( sqlite3Isspace(*z) ) z++; + /* get sign of significand */ if( *z=='-' ){ sign = -1; z++; }else if( *z=='+' ){ z++; } - while( z[0]=='0' ){ - z++; - } - while( sqlite3Isdigit(*z) ){ - v1 = v1*10.0 + (*z - '0'); - z++; - nSignificant++; - } - if( *z=='.' ){ - LONGDOUBLE_TYPE divisor = 1.0; - z++; - if( nSignificant==0 ){ - while( z[0]=='0' ){ - divisor *= 10.0; - z++; - } - } - while( sqlite3Isdigit(*z) ){ - if( nSignificant<18 ){ - v1 = v1*10.0 + (*z - '0'); - divisor *= 10.0; - nSignificant++; - } - z++; - } - v1 /= divisor; - } + /* skip leading zeroes */ + while( z[0]=='0' ) z++, nDigits++; + + /* copy max significant digits to significand */ + while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ + s = s*10 + (*z - '0'); + z++, nDigits++; + } + /* skip non-significant significand digits + ** (increase exponent by d to shift decimal left) */ + while( sqlite3Isdigit(*z) ) z++, nDigits++, d++; + + /* if decimal point is present */ + if( *z=='.' ){ + z++; + /* copy digits from after decimal to significand + ** (decrease exponent by d to shift decimal right) */ + while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ + s = s*10 + (*z - '0'); + z++, nDigits++, d--; + } + /* skip non-significant digits */ + while( sqlite3Isdigit(*z) ) z++, nDigits++; + } + + /* if exponent is present */ if( *z=='e' || *z=='E' ){ - int esign = 1; - int eval = 0; - LONGDOUBLE_TYPE scale = 1.0; z++; + /* get sign of exponent */ if( *z=='-' ){ esign = -1; z++; }else if( *z=='+' ){ z++; } + /* copy digits to exponent */ while( sqlite3Isdigit(*z) ){ - eval = eval*10 + *z - '0'; + e = e*10 + (*z - '0'); z++; } - while( eval>=64 ){ scale *= 1.0e+64; eval -= 64; } - while( eval>=16 ){ scale *= 1.0e+16; eval -= 16; } - while( eval>=4 ){ scale *= 1.0e+4; eval -= 4; } - while( eval>=1 ){ scale *= 1.0e+1; eval -= 1; } - if( esign<0 ){ - v1 /= scale; + } + + /* adjust exponent by d, and update sign */ + e = (e*esign) + d; + if( e<0 ) { + esign = -1; + e *= -1; + } else { + esign = 1; + } + + /* if 0 significand */ + if( !s ) { + /* In the IEEE 754 standard, zero is signed. + ** Add the sign if we've seen at least one digit */ + result = (sign<0 && nDigits) ? -(double)0 : (double)0; + } else { + /* attempt to reduce exponent */ + if( esign>0 ){ + while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10; }else{ - v1 *= scale; + while( !(s%10) && e>0 ) e--,s/=10; + } + + /* adjust the sign of significand */ + s = sign<0 ? -s : s; + + /* if exponent, scale significand as appropriate + ** and store in result. */ + if( e ){ + double scale = 1.0; + /* attempt to handle extremely small/large numbers better */ + if( e>307 && e<342 ){ + while( e%308 ) { scale *= 1.0e+1; e -= 1; } + if( esign<0 ){ + result = s / scale; + result /= 1.0e+308; + }else{ + result = s * scale; + result *= 1.0e+308; + } + }else{ + /* 1.0e+22 is the largest power of 10 than can be + ** represented exactly. */ + while( e%22 ) { scale *= 1.0e+1; e -= 1; } + while( e>0 ) { scale *= 1.0e+22; e -= 22; } + if( esign<0 ){ + result = s / scale; + }else{ + result = s * scale; + } + } + } else { + result = (double)s; } } - *pResult = (double)(sign<0 ? -v1 : v1); + + /* store the result */ + *pResult = result; + + /* return number of characters used */ return (int)(z - zBegin); #else return sqlite3Atoi64(z, pResult); #endif /* SQLITE_OMIT_FLOATING_POINT */ } @@ -902,11 +957,11 @@ #if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* ** Translate a single byte of Hex into an integer. -** This routinen only works if h really is a valid hexadecimal +** This routine only works if h really is a valid hexadecimal ** character: 0..9a..fA..F */ static u8 hexToInt(int h){ assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); #ifdef SQLITE_ASCII Index: src/vacuum.c ================================================================== --- src/vacuum.c +++ src/vacuum.c @@ -11,12 +11,10 @@ ************************************************************************* ** This file contains code used to implement the VACUUM command. ** ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. -** -** $Id: vacuum.c,v 1.90 2009/06/03 11:25:07 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) @@ -95,15 +93,18 @@ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } - /* Save the current value of the write-schema flag before setting it. */ + /* Save the current value of the database flags so that it can be + ** restored before returning. Then set the writable-schema flag, and + ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; + db->flags &= ~SQLITE_ForeignKeys; pMain = db->aDb[0].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); /* Attach the temporary database as 'vacuum_db'. The synchronous pragma @@ -124,10 +125,16 @@ rc = execSql(db, zSql); if( rc!=SQLITE_OK ) goto end_of_vacuum; pDb = &db->aDb[db->nDb-1]; assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); pTemp = db->aDb[db->nDb-1].pBt; + + /* The call to execSql() to attach the temp database has left the file + ** locked (as there was more than one active statement when the transaction + ** to read the schema was concluded. Unlock it here so that this doesn't + ** cause problems for the call to BtreeSetPageSize() below. */ + sqlite3BtreeCommit(pTemp); nRes = sqlite3BtreeGetReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC @@ -178,17 +185,17 @@ "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) " " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Loop through the tables in the main database. For each, do - ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy + ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " - "|| ' SELECT * FROM ' || quote(name) || ';'" - "FROM sqlite_master " + "|| ' SELECT * FROM main.' || quote(name) || ';'" + "FROM main.sqlite_master " "WHERE type = 'table' AND name!='sqlite_sequence' " " AND rootpage>0" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; @@ -200,11 +207,11 @@ "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' " ); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " - "|| ' SELECT * FROM ' || quote(name) || ';' " + "|| ' SELECT * FROM main.' || quote(name) || ';' " "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; @@ -214,11 +221,11 @@ ** from the SQLITE_MASTER table. */ rc = execSql(db, "INSERT INTO vacuum_db.sqlite_master " " SELECT type, name, tbl_name, rootpage, sql" - " FROM sqlite_master" + " FROM main.sqlite_master" " WHERE type='view' OR type='trigger'" " OR (type='table' AND rootpage=0)" ); if( rc ) goto end_of_vacuum; @@ -252,12 +259,11 @@ /* Copy Btree meta values */ for(i=0; in; } } #endif +/* +** The next global variable is incremented each type the OP_Found opcode +** is executed. This is used to test whether or not the foreign key +** operation implemented using OP_FkIsZero is working. This variable +** has no function other than to help verify the correct operation of the +** library. +*/ +#ifdef SQLITE_TEST +int sqlite3_found_count = 0; +#endif + /* ** Test a register to see if it exceeds the current maximum blob size. ** If it does, record the new maximum blob size. */ #if defined(SQLITE_TEST) && !defined(SQLITE_OMIT_BUILTIN_TEST) @@ -138,16 +147,14 @@ #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0) /* ** Argument pMem points at a register that will be passed to a ** user-defined function or returned to the user as the result of a query. -** The second argument, 'db_enc' is the text encoding used by the vdbe for -** register variables. This routine sets the pMem->enc and pMem->type -** variables used by the sqlite3_value_*() routines. +** This routine sets the pMem->type variable used by the sqlite3_value_*() +** routines. */ -#define storeTypeInfo(A,B) _storeTypeInfo(A) -static void _storeTypeInfo(Mem *pMem){ +void sqlite3VdbeMemStoreType(Mem *pMem){ int flags = pMem->flags; if( flags & MEM_Null ){ pMem->type = SQLITE_NULL; } else if( flags & MEM_Int ){ @@ -187,11 +194,11 @@ static VdbeCursor *allocateCursor( Vdbe *p, /* The virtual machine */ int iCur, /* Index of the new VdbeCursor */ int nField, /* Number of fields in the table or index */ int iDb, /* When database the cursor belongs to, or -1 */ - int isBtreeCursor /* True for B-Tree vs. pseudo-table or vtab */ + int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */ ){ /* Find the memory cell that will be used to store the blob of memory ** required for this VdbeCursor structure. It is convenient to use a ** vdbe memory cell to manage the memory allocation required for a ** VdbeCursor structure for the following reasons: @@ -314,11 +321,11 @@ ** This is an EXPERIMENTAL api and is subject to change or removal. */ int sqlite3_value_numeric_type(sqlite3_value *pVal){ Mem *pMem = (Mem*)pVal; applyNumericAffinity(pMem); - storeTypeInfo(pMem, 0); + sqlite3VdbeMemStoreType(pMem); return pMem->type; } /* ** Exported version of applyAffinity(). This one works on sqlite3_value*, @@ -698,15 +705,16 @@ if( (opProperty & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); pIn2 = &p->aMem[pOp->p2]; REGISTER_TRACE(pOp->p2, pIn2); - if( (opProperty & OPFLG_OUT3)!=0 ){ - assert( pOp->p3>0 ); - assert( pOp->p3<=p->nMem ); - pOut = &p->aMem[pOp->p3]; - } + /* As currently implemented, in2 implies out3. There is no reason + ** why this has to be, it just worked out that way. */ + assert( (opProperty & OPFLG_OUT3)!=0 ); + assert( pOp->p3>0 ); + assert( pOp->p3<=p->nMem ); + pOut = &p->aMem[pOp->p3]; }else if( (opProperty & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); pIn3 = &p->aMem[pOp->p3]; REGISTER_TRACE(pOp->p3, pIn3); @@ -844,21 +852,41 @@ ** There is an implied "Halt 0 0 0" instruction inserted at the very end of ** every program. So a jump past the last instruction of the program ** is the same as executing Halt. */ case OP_Halt: { + if( pOp->p1==SQLITE_OK && p->pFrame ){ + /* Halt the sub-program. Return control to the parent frame. */ + VdbeFrame *pFrame = p->pFrame; + p->pFrame = pFrame->pParent; + p->nFrame--; + sqlite3VdbeSetChanges(db, p->nChange); + pc = sqlite3VdbeFrameRestore(pFrame); + if( pOp->p2==OE_Ignore ){ + /* Instruction pc is the OP_Program that invoked the sub-program + ** currently being halted. If the p2 instruction of this OP_Halt + ** instruction is set to OE_Ignore, then the sub-program is throwing + ** an IGNORE exception. In this case jump to the address specified + ** as the p2 of the calling OP_Program. */ + pc = p->aOp[pc].p2-1; + } + break; + } + p->rc = pOp->p1; - p->pc = pc; p->errorAction = (u8)pOp->p2; + p->pc = pc; if( pOp->p4.z ){ sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); } rc = sqlite3VdbeHalt(p); - assert( rc==SQLITE_BUSY || rc==SQLITE_OK ); + assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); if( rc==SQLITE_BUSY ){ p->rc = rc = SQLITE_BUSY; }else{ + assert( rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ); + assert( rc==SQLITE_OK || db->nDeferredCons>0 ); rc = p->rc ? SQLITE_ERROR : SQLITE_DONE; } goto vdbe_return; } @@ -987,11 +1015,11 @@ p1 = pOp->p1 - 1; p2 = pOp->p2; n = pOp->p3; assert( p1>=0 && p1+n<=p->nVar ); assert( p2>=1 && p2+n-1<=p->nMem ); - assert( pOp->p4.z==0 || pOp->p3==1 ); + assert( pOp->p4.z==0 || pOp->p3==1 || pOp->p3==0 ); while( n-- > 0 ){ pVar = &p->aVar[p1++]; if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; @@ -1093,10 +1121,19 @@ Mem *pMem; int i; assert( p->nResColumn==pOp->p2 ); assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=p->nMem+1 ); + + /* If this statement has violated immediate foreign key constraints, do + ** not return the number of rows modified. And do not RELEASE the statement + ** transaction. It needs to be rolled back. */ + if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){ + assert( db->flags&SQLITE_CountRows ); + assert( p->usesStmtJournal ); + break; + } /* 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. @@ -1125,11 +1162,11 @@ ** as side effect. */ pMem = p->pResultSet = &p->aMem[pOp->p1]; for(i=0; ip2; i++){ sqlite3VdbeMemNulTerminate(&pMem[i]); - storeTypeInfo(&pMem[i], encoding); + sqlite3VdbeMemStoreType(&pMem[i]); REGISTER_TRACE(pOp->p1+i, &pMem[i]); } if( db->mallocFailed ) goto no_mem; /* Return SQLITE_ROW @@ -1203,13 +1240,13 @@ ** If either input is NULL, the result is NULL. */ /* Opcode: Divide P1 P2 P3 * * ** ** Divide the value in register P1 by the value in register P2 -** and store the result in register P3. If the value in register P2 -** is zero, then the result is NULL. -** If either input is NULL, the result is NULL. +** and store the result in register P3 (P3=P2/P1). If the value in +** register P1 is zero, then the result is NULL. If either input is +** NULL, the result is NULL. */ /* Opcode: Remainder P1 P2 P3 * * ** ** Compute the remainder after integer division of the value in ** register P1 by the value in register P2 and store the result in P3. @@ -1344,11 +1381,11 @@ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); pArg = &p->aMem[pOp->p2]; for(i=0; ip2, pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); if( pOp->p4type==P4_FUNCDEF ){ @@ -1657,16 +1694,28 @@ /* Opcode: Ne P1 P2 P3 P4 P5 ** ** This works just like the Lt opcode except that the jump is taken if ** the operands in registers P1 and P3 are not equal. See the Lt opcode for ** additional information. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is false. If either operand is NULL then the result is true. +** If neither operand is NULL the the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Eq P1 P2 P3 P4 P5 ** ** This works just like the Lt opcode except that the jump is taken if ** the operands in registers P1 and P3 are equal. ** See the Lt opcode for additional information. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is true. If either operand is NULL then the result is false. +** If neither operand is NULL the the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Le P1 P2 P3 P4 P5 ** ** This works just like the Lt opcode except that the jump is taken if ** the content of register P3 is less than or equal to the content of @@ -1688,41 +1737,50 @@ case OP_Ne: /* same as TK_NE, jump, in1, in3 */ case OP_Lt: /* same as TK_LT, jump, in1, in3 */ case OP_Le: /* same as TK_LE, jump, in1, in3 */ case OP_Gt: /* same as TK_GT, jump, in1, in3 */ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ - int flags; - int res; - char affinity; - - flags = pIn1->flags|pIn3->flags; - - if( flags&MEM_Null ){ - /* If either operand is NULL then the result is always NULL. - ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. - */ - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &p->aMem[pOp->p2]; - MemSetTypeFlag(pOut, MEM_Null); - REGISTER_TRACE(pOp->p2, pOut); - }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; - } - break; - } - - affinity = pOp->p5 & SQLITE_AFF_MASK; - if( affinity ){ - applyAffinity(pIn1, affinity, encoding); - applyAffinity(pIn3, affinity, encoding); - if( db->mallocFailed ) goto no_mem; - } - - assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); - ExpandBlob(pIn1); - ExpandBlob(pIn3); - res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + int res; /* Result of the comparison of pIn1 against pIn3 */ + char affinity; /* Affinity to use for comparison */ + + if( (pIn1->flags | pIn3->flags)&MEM_Null ){ + /* One or both operands are NULL */ + if( pOp->p5 & SQLITE_NULLEQ ){ + /* If SQLITE_NULLEQ is set (which will only happen if the operator is + ** OP_Eq or OP_Ne) then take the jump or not depending on whether + ** or not both operands are null. + */ + assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); + res = (pIn1->flags & pIn3->flags & MEM_Null)==0; + }else{ + /* SQLITE_NULLEQ is clear and at least one operand is NULL, + ** then the result is always NULL. + ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. + */ + if( pOp->p5 & SQLITE_STOREP2 ){ + pOut = &p->aMem[pOp->p2]; + MemSetTypeFlag(pOut, MEM_Null); + REGISTER_TRACE(pOp->p2, pOut); + }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ + pc = pOp->p2-1; + } + break; + } + }else{ + /* Neither operand is NULL. Do a comparison. */ + affinity = pOp->p5 & SQLITE_AFF_MASK; + if( affinity ){ + applyAffinity(pIn1, affinity, encoding); + applyAffinity(pIn3, affinity, encoding); + if( db->mallocFailed ) goto no_mem; + } + + assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); + ExpandBlob(pIn1); + ExpandBlob(pIn3); + res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); + } switch( pOp->opcode ){ case OP_Eq: res = res==0; break; case OP_Ne: res = res!=0; break; case OP_Lt: res = res<0; break; case OP_Le: res = res<=0; break; @@ -1784,13 +1842,22 @@ n = pOp->p3; pKeyInfo = pOp->p4.pKeyInfo; assert( n>0 ); assert( pKeyInfo!=0 ); p1 = pOp->p1; - assert( p1>0 && p1+n<=p->nMem+1 ); p2 = pOp->p2; - assert( p2>0 && p2+n<=p->nMem+1 ); +#if SQLITE_DEBUG + if( aPermute ){ + int k, mx = 0; + for(k=0; kmx ) mx = aPermute[k]; + assert( p1>0 && p1+mx<=p->nMem+1 ); + assert( p2>0 && p2+mx<=p->nMem+1 ); + }else{ + assert( p1>0 && p1+n<=p->nMem+1 ); + assert( p2>0 && p2+n<=p->nMem+1 ); + } +#endif /* SQLITE_DEBUG */ for(i=0; iaMem[p1+idx]); REGISTER_TRACE(p2+idx, &p->aMem[p2+idx]); assert( inField ); @@ -1955,33 +2022,11 @@ pc = pOp->p2 - 1; } break; } -/* Opcode: SetNumColumns * P2 * * * -** -** This opcode sets the number of columns for the cursor opened by the -** following instruction to P2. -** -** An OP_SetNumColumns is only useful if it occurs immediately before -** one of the following opcodes: -** -** OpenRead -** OpenWrite -** OpenPseudo -** -** If the OP_Column opcode is to be executed on a cursor, then -** 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 * +/* Opcode: Column P1 P2 P3 P4 P5 ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional ** information about the format of the data.) Extract the P2-th column ** from this record. If there are less that (P2+1) @@ -1990,10 +2035,15 @@ ** The value extracted is stored in register P3. ** ** If the column contains fewer than P2 fields, then extract a NULL. Or, ** if the P4 argument is a P4_MEM use the value of the P4 argument as ** the result. +** +** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor, +** then the cache of the cursor is reset prior to extracting the column. +** The first OP_Column against a pseudo-table after the value of the content +** register has changed should have this bit set. */ case OP_Column: { u32 payloadSize; /* Number of bytes in the record */ i64 payloadSize64; /* Number of bytes in the record */ int p1; /* P1 value of the opcode */ @@ -2013,10 +2063,11 @@ u8 *zEndHdr; /* Pointer to first byte after the header */ u32 offset; /* Offset into the data */ u64 offset64; /* 64-bit offset. 64 bits needed to catch overflow */ int szHdr; /* Size of the header size field at start of record */ int avail; /* Number of bytes of available data */ + Mem *pReg; /* PseudoTable input register */ p1 = pOp->p1; p2 = pOp->p2; pC = 0; @@ -2053,24 +2104,29 @@ payloadSize = 0; }else if( pC->cacheStatus==p->cacheCtr ){ payloadSize = pC->payloadSize; zRec = (char*)pC->aRow; }else if( pC->isIndex ){ - sqlite3BtreeKeySize(pCrsr, &payloadSize64); + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + rc = sqlite3BtreeKeySize(pCrsr, &payloadSize64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the ** payload size, so it is impossible for payloadSize64 to be ** larger than 32 bits. */ assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); payloadSize = (u32)payloadSize64; }else{ - sqlite3BtreeDataSize(pCrsr, &payloadSize); + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + rc = sqlite3BtreeDataSize(pCrsr, &payloadSize); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } - }else if( pC->pseudoTable ){ - /* The record is the sole entry of a pseudo-table */ - payloadSize = pC->nData; - zRec = pC->pData; - pC->cacheStatus = CACHE_STALE; + }else if( pC->pseudoTableReg>0 ){ + pReg = &p->aMem[pC->pseudoTableReg]; + assert( pReg->flags & MEM_Blob ); + payloadSize = pReg->n; + zRec = pReg->z; + pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; assert( payloadSize==0 || zRec!=0 ); }else{ /* Consider the row to be NULL */ payloadSize = 0; } @@ -2442,50 +2498,10 @@ pOut->u.i = nEntry; break; } #endif -/* Opcode: Statement P1 * * * * -** -** Begin an individual statement transaction which is part of a larger -** transaction. This is needed so that the statement -** can be rolled back after an error without having to roll back the -** entire transaction. The statement transaction will automatically -** commit when the VDBE halts. -** -** If the database connection is currently in autocommit mode (that -** is to say, if it is in between BEGIN and COMMIT) -** and if there are no other active statements on the same database -** connection, then this operation is a no-op. No statement transaction -** is needed since any error can use the normal ROLLBACK process to -** undo changes. -** -** If a statement transaction is started, then a statement journal file -** will be allocated and initialized. -** -** The statement is begun on the database file with index P1. The main -** database file has an index of 0 and the file used for temporary tables -** has an index of 1. -*/ -case OP_Statement: { - Btree *pBt; - if( db->autoCommit==0 || db->activeVdbeCnt>1 ){ - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( db->aDb[pOp->p1].pBt!=0 ); - pBt = db->aDb[pOp->p1].pBt; - assert( sqlite3BtreeIsInTrans(pBt) ); - assert( (p->btreeMask & (1<p1))!=0 ); - 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; -} - /* Opcode: Savepoint P1 * * P4 * ** ** Open, release or rollback the savepoint named by parameter P4, depending ** on the value of P1. To open a new savepoint, P1==0. To release (commit) an ** existing savepoint, P1==1, or to rollback an existing savepoint P1==2. @@ -2538,10 +2554,11 @@ } /* Link the new savepoint into the database handle's list. */ pNew->pNext = db->pSavepoint; db->pSavepoint = pNew; + pNew->nDeferredCons = db->nDeferredCons; } } }else{ iSavepoint = 0; @@ -2575,10 +2592,13 @@ ** and this is a RELEASE command, then the current transaction ** is committed. */ int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; if( isTransaction && p1==SAVEPOINT_RELEASE ){ + if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ + goto vdbe_return; + } db->autoCommit = 1; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = pc; db->autoCommit = 0; p->rc = rc = SQLITE_BUSY; @@ -2607,18 +2627,23 @@ db->pSavepoint = pTmp->pNext; sqlite3DbFree(db, pTmp); db->nSavepoint--; } - /* If it is a RELEASE, then destroy the savepoint being operated on too */ + /* If it is a RELEASE, then destroy the savepoint being operated on + ** too. If it is a ROLLBACK TO, then set the number of deferred + ** constraint violations present in the database to the value stored + ** when the savepoint was created. */ if( p1==SAVEPOINT_RELEASE ){ assert( pSavepoint==db->pSavepoint ); db->pSavepoint = pSavepoint->pNext; sqlite3DbFree(db, pSavepoint); if( !isTransaction ){ db->nSavepoint--; } + }else{ + db->nDeferredCons = pSavepoint->nDeferredCons; } } } break; @@ -2663,10 +2688,12 @@ }else if( desiredAutoCommit!=db->autoCommit ){ if( iRollback ){ assert( desiredAutoCommit==1 ); sqlite3RollbackAll(db); db->autoCommit = 1; + }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ + goto vdbe_return; }else{ db->autoCommit = (u8)desiredAutoCommit; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = pc; db->autoCommit = (u8)(1-desiredAutoCommit); @@ -2709,10 +2736,20 @@ ** other process can start another write transaction while this transaction is ** underway. Starting a write transaction also creates a rollback journal. A ** write transaction must be started before any changes can be made to the ** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained ** on the file. +** +** If a write-transaction is started and the Vdbe.usesStmtJournal flag is +** true (this flag is set if the Vdbe may modify more than one row and may +** throw an ABORT exception), a statement transaction may also be opened. +** More specifically, a statement transaction is opened iff the database +** connection is currently not in autocommit mode, or if there are other +** active statements. A statement transaction allows the affects of this +** VDBE to be rolled back after an error without having to roll back the +** entire transaction. If no error is encountered, the statement transaction +** will automatically commit when the VDBE halts. ** ** If P2 is zero, then a read-lock is obtained on the database file. */ case OP_Transaction: { Btree *pBt; @@ -2726,13 +2763,30 @@ if( rc==SQLITE_BUSY ){ p->pc = pc; p->rc = rc = SQLITE_BUSY; goto vdbe_return; } - if( rc!=SQLITE_OK && rc!=SQLITE_READONLY /* && rc!=SQLITE_BUSY */ ){ + if( rc!=SQLITE_OK ){ goto abort_due_to_error; } + + if( pOp->p2 && p->usesStmtJournal + && (db->autoCommit==0 || db->activeVdbeCnt>1) + ){ + assert( sqlite3BtreeIsInTrans(pBt) ); + if( p->iStatement==0 ){ + assert( db->nStatement>=0 && db->nSavepoint>=0 ); + db->nStatement++; + p->iStatement = db->nSavepoint + db->nStatement; + } + rc = sqlite3BtreeBeginStmt(pBt, p->iStatement); + + /* Store the current value of the database handles deferred constraint + ** counter. If the statement transaction needs to be rolled back, + ** the value of this counter needs to be restored too. */ + p->nStmtDefCons = db->nDeferredCons; + } } break; } /* Opcode: ReadCookie P1 P2 P3 * * @@ -2757,11 +2811,11 @@ assert( pOp->p3=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); assert( (p->btreeMask & (1<aDb[iDb].pBt, iCookie, (u32 *)&iMeta); + sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); pOut->u.i = iMeta; MemSetTypeFlag(pOut, MEM_Int); break; } @@ -2795,10 +2849,11 @@ } if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ sqlite3ExpirePreparedStatements(db); + p->expired = 0; } break; } /* Opcode: VerifyCookie P1 P2 * @@ -2822,16 +2877,15 @@ Btree *pBt; assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (1<p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ - rc = sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); }else{ - rc = SQLITE_OK; iMeta = 0; } - if( rc==SQLITE_OK && iMeta!=pOp->p2 ){ + if( iMeta!=pOp->p2 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie ** stored with the in-memory representation of the schema, do ** not reload the schema from the database file. @@ -2912,11 +2966,15 @@ int iDb; int wrFlag; Btree *pX; VdbeCursor *pCur; Db *pDb; - int flags; + + if( p->expired ){ + rc = SQLITE_ABORT; + break; + } nField = 0; pKeyInfo = 0; p2 = pOp->p2; iDb = pOp->p3; @@ -2960,49 +3018,26 @@ if( pCur==0 ) goto no_mem; pCur->nullRow = 1; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); pCur->pKeyInfo = pKeyInfo; - switch( rc ){ - case SQLITE_OK: { - flags = sqlite3BtreeFlags(pCur->pCursor); - - /* Sanity checking. Only the lower four bits of the flags byte should - ** be used. Bit 3 (mask 0x08) is unpredictable. The lower 3 bits - ** (mask 0x07) should be either 5 (intkey+leafdata for tables) or - ** 2 (zerodata for indices). If these conditions are not met it can - ** only mean that we are dealing with a corrupt database file. - ** Note: All of the above is checked already in sqlite3BtreeCursor(). - */ - assert( (flags & 0xf0)==0 ); - assert( (flags & 0x07)==5 || (flags & 0x07)==2 ); - - pCur->isTable = (flags & BTREE_INTKEY)!=0 ?1:0; - pCur->isIndex = (flags & BTREE_ZERODATA)!=0 ?1:0; - /* If P4==0 it means we are expected to open a table. If P4!=0 then - ** we expect to be opening an index. If this is not what happened, - ** then the database is corrupt - */ - if( (pCur->isTable && pOp->p4type==P4_KEYINFO) - || (pCur->isIndex && pOp->p4type!=P4_KEYINFO) ){ - rc = SQLITE_CORRUPT_BKPT; - goto abort_due_to_error; - } - break; - } - case SQLITE_EMPTY: { - pCur->isTable = pOp->p4type!=P4_KEYINFO; - pCur->isIndex = !pCur->isTable; - pCur->pCursor = 0; - rc = SQLITE_OK; - break; - } - default: { - assert( rc!=SQLITE_BUSY ); /* Busy conditions detected earlier */ - goto abort_due_to_error; - } - } + /* Since it performs no memory allocation or IO, the only values that + ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. + ** SQLITE_EMPTY is only returned when attempting to open the table + ** rooted at page 1 of a zero-byte database. */ + assert( rc==SQLITE_EMPTY || rc==SQLITE_OK ); + if( rc==SQLITE_EMPTY ){ + pCur->pCursor = 0; + rc = SQLITE_OK; + } + + /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of + ** SQLite used to check if the root-page flags were sane at this point + ** and report database corruption if they were not, but this check has + ** since moved into the btree layer. */ + pCur->isTable = pOp->p4type!=P4_KEYINFO; + pCur->isIndex = !pCur->isTable; break; } /* Opcode: OpenEphemeral P1 P2 * P4 * ** @@ -3068,26 +3103,18 @@ } /* 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 -** first row to be deleted. All data is deleted when the cursor is -** closed. +** row of data. The content of that one row in the content of memory +** register P2. In other words, cursor P1 becomes an alias for the +** MEM_Blob content contained in register P2. ** -** A pseudo-table created by this opcode is useful for holding the -** NEW or OLD tables in a trigger. Also used to hold the a single +** A pseudo-table created by this opcode is used to hold the a single ** row output from the sorter so that the row can be decomposed into -** individual columns using the OP_Column opcode. -** -** When OP_Insert is executed to insert a row in to the pseudo table, -** the pseudo-table cursor may or may not make it's own copy of the -** original row data. If P2 is 0, then the pseudo-table will copy the -** original row data. Otherwise, a pointer to the original memory cell -** 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). +** individual columns using the OP_Column opcode. The OP_Column opcode +** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by ** the pseudo-table. */ case OP_OpenPseudo: { @@ -3095,12 +3122,11 @@ assert( pOp->p1>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; - pCx->pseudoTable = 1; - pCx->ephemPseudoTable = (u8)pOp->p2; + pCx->pseudoTableReg = pOp->p2; pCx->isTable = 1; pCx->isIndex = 0; break; } @@ -3181,10 +3207,11 @@ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p2!=0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); + assert( pC->pseudoTableReg==0 ); if( pC->pCursor!=0 ){ oc = pOp->opcode; pC->nullRow = 0; if( pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, @@ -3294,11 +3321,10 @@ }else{ /* This happens when attempting to open the sqlite3_master table ** for read access returns SQLITE_EMPTY. In this case always ** take the jump (since there are no records in the table). */ - assert( pC->pseudoTable==0 ); pc = pOp->p2 - 1; } break; } @@ -3363,19 +3389,24 @@ int alreadyExists; VdbeCursor *pC; int res; UnpackedRecord *pIdxKey; char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; + +#ifdef SQLITE_TEST + sqlite3_found_count++; +#endif alreadyExists = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); if( ALWAYS(pC->pCursor!=0) ){ assert( pC->isTable==0 ); assert( pIn3->flags & MEM_Blob ); + ExpandBlob(pIn3); pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, aTempRec, sizeof(aTempRec)); if( pIdxKey==0 ){ goto no_mem; } @@ -3504,10 +3535,11 @@ assert( pIn3->flags & MEM_Int ); assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isTable ); + assert( pC->pseudoTableReg==0 ); pCrsr = pC->pCursor; if( pCrsr!=0 ){ res = 0; iKey = pIn3->u.i; rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); @@ -3523,12 +3555,10 @@ pC->seekResult = res; }else{ /* This happens when an attempt to open a read cursor on the ** sqlite_master table returns SQLITE_EMPTY. */ - assert( !pC->pseudoTable ); - assert( pC->isTable ); pc = pOp->p2 - 1; assert( pC->rowidIsValid==0 ); pC->seekResult = 0; } break; @@ -3555,23 +3585,24 @@ ** Get a new integer record number (a.k.a "rowid") used as the key to a table. ** The record number is not previously used as a key in the database ** table that cursor P1 points to. The new record number is written ** written to register P2. ** -** If P3>0 then P3 is a register that holds the largest previously -** generated record number. No new record numbers are allowed to be less -** than this value. When this value reaches its maximum, a SQLITE_FULL -** error is generated. The P3 register is updated with the generated -** record number. This P3 mechanism is used to help implement the +** If P3>0 then P3 is a register in the root frame of this VDBE that holds +** the largest previously generated record number. No new record numbers are +** allowed to be less than this value. When this value reaches its maximum, +** a SQLITE_FULL error is generated. The P3 register is updated with the ' +** generated record number. This P3 mechanism is used to help implement the ** AUTOINCREMENT feature. */ case OP_NewRowid: { /* out2-prerelease */ i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ int cnt; /* Counter to limit the number of searches */ Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ + VdbeFrame *pFrame; /* Root frame of VDBE */ v = 0; res = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; @@ -3613,11 +3644,13 @@ goto abort_due_to_error; } if( res ){ v = 1; }else{ - sqlite3BtreeKeySize(pC->pCursor, &v); + assert( sqlite3BtreeCursorIsValid(pC->pCursor) ); + rc = sqlite3BtreeKeySize(pC->pCursor, &v); + assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ if( v==MAX_ROWID ){ pC->useRandomRowid = 1; }else{ v++; } @@ -3624,13 +3657,24 @@ } } #ifndef SQLITE_OMIT_AUTOINCREMENT if( pOp->p3 ){ - assert( pOp->p3>0 && pOp->p3<=p->nMem ); /* P3 is a valid memory cell */ - pMem = &p->aMem[pOp->p3]; - REGISTER_TRACE(pOp->p3, pMem); + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3>0 ); + if( p->pFrame ){ + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3<=pFrame->nMem ); + pMem = &pFrame->aMem[pOp->p3]; + }else{ + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3<=p->nMem ); + pMem = &p->aMem[pOp->p3]; + } + + REGISTER_TRACE(pOp->p3, pMem); sqlite3VdbeMemIntegerify(pMem); assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ rc = SQLITE_FULL; goto abort_due_to_error; @@ -3675,18 +3719,31 @@ /* Opcode: Insert P1 P2 P3 P4 P5 ** ** Write an entry into the table of cursor P1. A new entry is ** created if it doesn't already exist or the data for an existing -** entry is overwritten. The data is the value stored register +** entry is overwritten. The data is the value MEM_Blob stored in register ** number P2. The key is stored in register P3. The key must -** be an integer. +** be a MEM_Int. ** ** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is ** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set, ** then rowid is stored for subsequent return by the ** sqlite3_last_insert_rowid() function (otherwise it is unmodified). +** +** If the OPFLAG_USESEEKRESULT flag of P5 is set and if the result of +** the last seek operation (OP_NotExists) was a success, then this +** operation will not attempt to find the appropriate row before doing +** the insert but will instead overwrite the row that the cursor is +** currently pointing to. Presumably, the prior OP_NotExists opcode +** has already positioned the cursor correctly. This is an optimization +** that boosts performance by avoiding redundant seeks. +** +** If the OPFLAG_ISUPDATE flag is set, then this opcode is part of an +** UPDATE operation. Otherwise (if the flag is clear) then this opcode +** is part of an INSERT operation. The difference is only important to +** the update hook. ** ** Parameter P4 may point to a string containing the table-name, or ** may be NULL. If it is not NULL, then the update-hook ** (sqlite3.xUpdateCallback) is invoked following a successful insert. ** @@ -3697,76 +3754,65 @@ ** cause any problems.) ** ** This instruction only works on tables. The equivalent instruction ** for indices is OP_IdxInsert. */ -case OP_Insert: { - Mem *pData; - Mem *pKey; - i64 iKey; /* The integer ROWID or key for the record to be inserted */ - VdbeCursor *pC; - int nZero; - int seekResult; - const char *zDb; - const char *zTbl; - int op; +/* Opcode: InsertInt P1 P2 P3 P4 P5 +** +** This works exactly like OP_Insert except that the key is the +** integer value P3, not the value of the integer stored in register P3. +*/ +case OP_Insert: +case OP_InsertInt: { + Mem *pData; /* MEM cell holding data for the record to be inserted */ + Mem *pKey; /* MEM cell holding key for the record */ + i64 iKey; /* The integer ROWID or key for the record to be inserted */ + VdbeCursor *pC; /* Cursor to table into which insert is written */ + int nZero; /* Number of zero-bytes to append */ + int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ + const char *zDb; /* database name - used by the update hook */ + const char *zTbl; /* Table name - used by the opdate hook */ + int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ pData = &p->aMem[pOp->p2]; - pKey = &p->aMem[pOp->p3]; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - assert( pC->pCursor!=0 || pC->pseudoTable ); - assert( pKey->flags & MEM_Int ); + assert( pC->pCursor!=0 ); + assert( pC->pseudoTableReg==0 ); assert( pC->isTable ); REGISTER_TRACE(pOp->p2, pData); - REGISTER_TRACE(pOp->p3, pKey); - iKey = pKey->u.i; + if( pOp->opcode==OP_Insert ){ + pKey = &p->aMem[pOp->p3]; + assert( pKey->flags & MEM_Int ); + REGISTER_TRACE(pOp->p3, pKey); + iKey = pKey->u.i; + }else{ + assert( pOp->opcode==OP_InsertInt ); + iKey = pOp->p3; + } + if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = pKey->u.i; + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = iKey; if( pData->flags & MEM_Null ){ pData->z = 0; pData->n = 0; }else{ assert( pData->flags & (MEM_Blob|MEM_Str) ); } - if( pC->pseudoTable ){ - if( !pC->ephemPseudoTable ){ - sqlite3DbFree(db, pC->pData); - } - pC->iKey = iKey; - pC->nData = pData->n; - if( pC->ephemPseudoTable || pData->z==pData->zMalloc ){ - pC->pData = pData->z; - if( !pC->ephemPseudoTable ){ - pData->flags &= ~MEM_Dyn; - pData->flags |= MEM_Ephem; - pData->zMalloc = 0; - } - }else{ - pC->pData = sqlite3Malloc( pC->nData+2 ); - if( !pC->pData ) goto no_mem; - memcpy(pC->pData, pData->z, pC->nData); - pC->pData[pC->nData] = 0; - pC->pData[pC->nData+1] = 0; - } - pC->nullRow = 0; - }else{ - seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); - if( pData->flags & MEM_Zero ){ - nZero = pData->u.nZero; - }else{ - nZero = 0; - } - sqlite3BtreeSetCachedRowid(pC->pCursor, 0); - rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, - pData->z, pData->n, nZero, - pOp->p5 & OPFLAG_APPEND, seekResult - ); - } - + seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); + if( pData->flags & MEM_Zero ){ + nZero = pData->u.nZero; + }else{ + nZero = 0; + } + sqlite3BtreeSetCachedRowid(pC->pCursor, 0); + rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, + pData->z, pData->n, nZero, + pOp->p5 & OPFLAG_APPEND, seekResult + ); pC->rowidIsValid = 0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ @@ -3843,22 +3889,19 @@ assert( pC->iDb>=0 ); } if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; break; } - -/* Opcode: ResetCount P1 * * +/* Opcode: ResetCount * * * * * ** -** This opcode resets the VMs internal change counter to 0. If P1 is true, -** then the value of the change counter is copied to the database handle -** change counter (returned by subsequent calls to sqlite3_changes()) -** before it is reset. This is used by trigger programs. +** The value of the change counter is copied to the database handle +** change counter (returned by subsequent calls to sqlite3_changes()). +** Then the VMs internal change counter resets to 0. +** This is used by trigger programs. */ case OP_ResetCount: { - if( pOp->p1 ){ - sqlite3VdbeSetChanges(db, p->nChange); - } + sqlite3VdbeSetChanges(db, p->nChange); p->nChange = 0; break; } /* Opcode: RowData P1 P2 * * * @@ -3895,13 +3938,14 @@ pC = p->apCsr[pOp->p1]; assert( pC->isTable || pOp->opcode==OP_RowKey ); assert( pC->isIndex || pOp->opcode==OP_RowData ); assert( pC!=0 ); assert( pC->nullRow==0 ); - assert( pC->pseudoTable==0 ); + assert( pC->pseudoTableReg==0 ); assert( pC->pCursor!=0 ); pCrsr = pC->pCursor; + assert( sqlite3BtreeCursorIsValid(pCrsr) ); /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or ** OP_Rewind/Op_Next with no intervening instructions that might invalidate ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always ** a no-op and can never fail. But we leave it in place as a safety. @@ -3910,17 +3954,19 @@ rc = sqlite3VdbeCursorMoveto(pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; if( pC->isIndex ){ assert( !pC->isTable ); - sqlite3BtreeKeySize(pCrsr, &n64); + rc = sqlite3BtreeKeySize(pCrsr, &n64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ if( n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } n = (u32)n64; }else{ - sqlite3BtreeDataSize(pCrsr, &n); + rc = sqlite3BtreeDataSize(pCrsr, &n); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } } if( sqlite3VdbeMemGrow(pOut, n, 0) ){ @@ -3954,17 +4000,16 @@ const sqlite3_module *pModule; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); + assert( pC->pseudoTableReg==0 ); if( pC->nullRow ){ /* Do nothing so that reg[P2] remains NULL */ break; }else if( pC->deferredMoveto ){ v = pC->movetoTarget; - }else if( pC->pseudoTable ){ - v = pC->iKey; #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( pC->pVtabCursor ){ pVtab = pC->pVtabCursor->pVtab; pModule = pVtab->pModule; assert( pModule->xRowid ); @@ -3974,17 +4019,18 @@ p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ + assert( pC->pCursor!=0 ); rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; if( pC->rowidIsValid ){ v = pC->lastRowid; }else{ - assert( pC->pCursor!=0 ); - sqlite3BtreeKeySize(pC->pCursor, &v); + rc = sqlite3BtreeKeySize(pC->pCursor, &v); + assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */ } } pOut->u.i = v; MemSetTypeFlag(pOut, MEM_Int); break; @@ -4743,61 +4789,205 @@ break; } #ifndef SQLITE_OMIT_TRIGGER -/* Opcode: ContextPush * * * -** -** Save the current Vdbe context such that it can be restored by a ContextPop -** opcode. The context stores the last insert row id, the last statement change -** count, and the current statement change count. -*/ -case OP_ContextPush: { - int i; - Context *pContext; - - i = p->contextStackTop++; - assert( i>=0 ); - /* FIX ME: This should be allocated as part of the vdbe at compile-time */ - if( i>=p->contextStackDepth ){ - p->contextStackDepth = i+1; - p->contextStack = sqlite3DbReallocOrFree(db, p->contextStack, - sizeof(Context)*(i+1)); - if( p->contextStack==0 ) goto no_mem; - } - pContext = &p->contextStack[i]; - pContext->lastRowid = db->lastRowid; - pContext->nChange = p->nChange; - break; -} - -/* Opcode: ContextPop * * * -** -** Restore the Vdbe context to the state it was in when contextPush was last -** executed. The context stores the last insert row id, the last statement -** change count, and the current statement change count. -*/ -case OP_ContextPop: { - Context *pContext; - pContext = &p->contextStack[--p->contextStackTop]; - assert( p->contextStackTop>=0 ); - db->lastRowid = pContext->lastRowid; - p->nChange = pContext->nChange; - break; -} + +/* Opcode: Program P1 P2 P3 P4 * +** +** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). +** +** P1 contains the address of the memory cell that contains the first memory +** cell in an array of values used as arguments to the sub-program. P2 +** contains the address to jump to if the sub-program throws an IGNORE +** exception using the RAISE() function. Register P3 contains the address +** of a memory cell in this (the parent) VM that is used to allocate the +** memory required by the sub-vdbe at runtime. +** +** P4 is a pointer to the VM containing the trigger program. +*/ +case OP_Program: { /* jump */ + int nMem; /* Number of memory registers for sub-program */ + int nByte; /* Bytes of runtime space required for sub-program */ + Mem *pRt; /* Register to allocate runtime space */ + Mem *pMem; /* Used to iterate through memory cells */ + Mem *pEnd; /* Last memory cell in new array */ + VdbeFrame *pFrame; /* New vdbe frame to execute in */ + SubProgram *pProgram; /* Sub-program to execute */ + void *t; /* Token identifying trigger */ + + pProgram = pOp->p4.pProgram; + pRt = &p->aMem[pOp->p3]; + assert( pProgram->nOp>0 ); + + /* If the p5 flag is clear, then recursive invocation of triggers is + ** disabled for backwards compatibility (p5 is set if this sub-program + ** is really a trigger, not a foreign key action, and the flag set + ** and cleared by the "PRAGMA recursive_triggers" command is clear). + ** + ** It is recursive invocation of triggers, at the SQL level, that is + ** disabled. In some cases a single trigger may generate more than one + ** SubProgram (if the trigger may be executed with more than one different + ** ON CONFLICT algorithm). SubProgram structures associated with a + ** single trigger all have the same value for the SubProgram.token + ** variable. */ + if( pOp->p5 ){ + t = pProgram->token; + for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent); + if( pFrame ) break; + } + + if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ + rc = SQLITE_ERROR; + sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion"); + break; + } + + /* Register pRt is used to store the memory required to save the state + ** of the current program, and the memory required at runtime to execute + ** the trigger program. If this trigger has been fired before, then pRt + ** is already allocated. Otherwise, it must be initialized. */ + if( (pRt->flags&MEM_Frame)==0 ){ + /* SubProgram.nMem is set to the number of memory cells used by the + ** program stored in SubProgram.aOp. As well as these, one memory + ** cell is required for each cursor used by the program. Set local + ** variable nMem (and later, VdbeFrame.nChildMem) to this value. + */ + nMem = pProgram->nMem + pProgram->nCsr; + nByte = ROUND8(sizeof(VdbeFrame)) + + nMem * sizeof(Mem) + + pProgram->nCsr * sizeof(VdbeCursor *); + pFrame = sqlite3DbMallocZero(db, nByte); + if( !pFrame ){ + goto no_mem; + } + sqlite3VdbeMemRelease(pRt); + pRt->flags = MEM_Frame; + pRt->u.pFrame = pFrame; + + pFrame->v = p; + pFrame->nChildMem = nMem; + pFrame->nChildCsr = pProgram->nCsr; + pFrame->pc = pc; + pFrame->aMem = p->aMem; + pFrame->nMem = p->nMem; + pFrame->apCsr = p->apCsr; + pFrame->nCursor = p->nCursor; + pFrame->aOp = p->aOp; + pFrame->nOp = p->nOp; + pFrame->token = pProgram->token; + + pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem]; + for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){ + pMem->flags = MEM_Null; + pMem->db = db; + } + }else{ + pFrame = pRt->u.pFrame; + assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem ); + assert( pProgram->nCsr==pFrame->nChildCsr ); + assert( pc==pFrame->pc ); + } + + p->nFrame++; + pFrame->pParent = p->pFrame; + pFrame->lastRowid = db->lastRowid; + pFrame->nChange = p->nChange; + p->nChange = 0; + p->pFrame = pFrame; + p->aMem = &VdbeFrameMem(pFrame)[-1]; + p->nMem = pFrame->nChildMem; + p->nCursor = (u16)pFrame->nChildCsr; + p->apCsr = (VdbeCursor **)&p->aMem[p->nMem+1]; + p->aOp = pProgram->aOp; + p->nOp = pProgram->nOp; + pc = -1; + + break; +} + +/* Opcode: Param P1 P2 * * * +** +** This opcode is only ever present in sub-programs called via the +** OP_Program instruction. Copy a value currently stored in a memory +** cell of the calling (parent) frame to cell P2 in the current frames +** address space. This is used by trigger programs to access the new.* +** and old.* values. +** +** The address of the cell in the parent frame is determined by adding +** the value of the P1 argument to the value of the P1 argument to the +** calling OP_Program instruction. +*/ +case OP_Param: { /* out2-prerelease */ + VdbeFrame *pFrame; + Mem *pIn; + pFrame = p->pFrame; + pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; + sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); + break; +} + #endif /* #ifndef SQLITE_OMIT_TRIGGER */ +#ifndef SQLITE_OMIT_FOREIGN_KEY +/* Opcode: FkCounter P1 P2 * * * +** +** Increment a "constraint counter" by P2 (P2 may be negative or positive). +** If P1 is non-zero, the database constraint counter is incremented +** (deferred foreign key constraints). Otherwise, if P1 is zero, the +** statement counter is incremented (immediate foreign key constraints). +*/ +case OP_FkCounter: { + if( pOp->p1 ){ + db->nDeferredCons += pOp->p2; + }else{ + p->nFkConstraint += pOp->p2; + } + break; +} + +/* Opcode: FkIfZero P1 P2 * * * +** +** This opcode tests if a foreign key constraint-counter is currently zero. +** If so, jump to instruction P2. Otherwise, fall through to the next +** instruction. +** +** If P1 is non-zero, then the jump is taken if the database constraint-counter +** is zero (the one that counts deferred constraint violations). If P1 is +** zero, the jump is taken if the statement constraint-counter is zero +** (immediate foreign key constraint violations). +*/ +case OP_FkIfZero: { /* jump */ + if( pOp->p1 ){ + if( db->nDeferredCons==0 ) pc = pOp->p2-1; + }else{ + if( p->nFkConstraint==0 ) pc = pOp->p2-1; + } + break; +} +#endif /* #ifndef SQLITE_OMIT_FOREIGN_KEY */ + #ifndef SQLITE_OMIT_AUTOINCREMENT /* Opcode: MemMax P1 P2 * * * ** -** Set the value of register P1 to the maximum of its current value -** and the value in register P2. +** P1 is a register in the root frame of this VM (the root frame is +** different from the current frame if this instruction is being executed +** within a sub-program). Set the value of register P1 to the maximum of +** its current value and the value in register P2. ** ** This instruction throws an error if the memory cell is not initially ** an integer. */ -case OP_MemMax: { /* in1, in2 */ +case OP_MemMax: { /* in2 */ + Mem *pIn1; + VdbeFrame *pFrame; + if( p->pFrame ){ + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + pIn1 = &pFrame->aMem[pOp->p1]; + }else{ + pIn1 = &p->aMem[pOp->p1]; + } sqlite3VdbeMemIntegerify(pIn1); sqlite3VdbeMemIntegerify(pIn2); if( pIn1->u.iu.i){ pIn1->u.i = pIn2->u.i; } @@ -4873,11 +5063,11 @@ pRec = &p->aMem[pOp->p2]; apVal = p->apArg; assert( apVal || n==0 ); for(i=0; ip4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); ctx.pMem = pMem = &p->aMem[pOp->p3]; pMem->n++; @@ -4992,32 +5182,31 @@ /* Opcode: TableLock P1 P2 P3 P4 * ** ** Obtain a lock on a particular table. This instruction is only used when ** the shared-cache feature is enabled. ** -** If P1 is the index of the database in sqlite3.aDb[] of the database +** P1 is the index of the database in sqlite3.aDb[] of the database ** on which the lock is acquired. A readlock is obtained if P3==0 or ** a write lock if P3==1. ** ** P2 contains the root-page of the table to lock. ** ** P4 contains a pointer to the name of the table being locked. This is only ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { - int p1; - u8 isWriteLock; - - p1 = pOp->p1; - isWriteLock = (u8)pOp->p3; - assert( p1>=0 && p1nDb ); - assert( (p->btreeMask & (1<aDb[p1].pBt, pOp->p2, isWriteLock); - if( (rc&0xFF)==SQLITE_LOCKED ){ - const char *z = pOp->p4.z; - sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); + u8 isWriteLock = (u8)pOp->p3; + if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ + int p1 = pOp->p1; + assert( p1>=0 && p1nDb ); + assert( (p->btreeMask & (1<aDb[p1].pBt, pOp->p2, isWriteLock); + if( (rc&0xFF)==SQLITE_LOCKED ){ + const char *z = pOp->p4.z; + sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); + } } break; } #endif /* SQLITE_OMIT_SHARED_CACHE */ @@ -5030,17 +5219,17 @@ ** 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, the error ** code will be set to SQLITE_LOCKED. */ case OP_VBegin: { - sqlite3_vtab *pVtab; - pVtab = pOp->p4.pVtab; - rc = sqlite3VtabBegin(db, pVtab); - if( pVtab ){ + VTable *pVTab; + pVTab = pOp->p4.pVtab; + rc = sqlite3VtabBegin(db, pVTab); + if( pVTab ){ sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = pVtab->zErrMsg; - pVtab->zErrMsg = 0; + p->zErrMsg = pVTab->pVtab->zErrMsg; + pVTab->pVtab->zErrMsg = 0; } break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -5083,11 +5272,11 @@ sqlite3_vtab *pVtab; sqlite3_module *pModule; pCur = 0; pVtabCursor = 0; - pVtab = pOp->p4.pVtab; + pVtab = pOp->p4.pVtab->pVtab; pModule = (sqlite3_module *)pVtab->pModule; assert(pVtab && pModule); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xOpen(pVtab, &pVtabCursor); sqlite3DbFree(db, p->zErrMsg); @@ -5162,22 +5351,20 @@ { res = 0; apArg = p->apArg; for(i = 0; iinVtabMethod = 1; rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); p->inVtabMethod = 0; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( rc==SQLITE_OK ){ res = pModule->xEof(pVtabCursor); } if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; @@ -5281,18 +5468,16 @@ ** xNext(). Instead, if an error occurs, true is returned (indicating that ** data is available) and the error code returned when xColumn or ** some other method is next invoked on the save virtual table cursor. */ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(pVtab); p->inVtabMethod = 1; rc = pModule->xNext(pCur->pVtabCursor); p->inVtabMethod = 0; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( rc==SQLITE_OK ){ res = pModule->xEof(pCur->pVtabCursor); } if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; @@ -5313,22 +5498,20 @@ */ case OP_VRename: { sqlite3_vtab *pVtab; Mem *pName; - pVtab = pOp->p4.pVtab; + pVtab = pOp->p4.pVtab->pVtab; pName = &p->aMem[pOp->p1]; assert( pVtab->pModule->xRename ); REGISTER_TRACE(pOp->p1, pName); assert( pName->flags & MEM_Str ); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(pVtab); rc = pVtab->pModule->xRename(pVtab, pName->z); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; break; } #endif @@ -5364,29 +5547,27 @@ int i; sqlite_int64 rowid; Mem **apArg; Mem *pX; - pVtab = pOp->p4.pVtab; + pVtab = pOp->p4.pVtab->pVtab; pModule = (sqlite3_module *)pVtab->pModule; nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); if( ALWAYS(pModule->xUpdate) ){ apArg = p->apArg; pX = &p->aMem[pOp->p3]; for(i=0; ixUpdate(pVtab, nArg, apArg, &rowid); sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, pVtab); if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( rc==SQLITE_OK && pOp->p1 ){ assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); db->lastRowid = rowid; } Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -12,12 +12,10 @@ ** Header file for the Virtual DataBase Engine (VDBE) ** ** This header defines the interface to the virtual database engine ** 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.141 2009/04/10 00:56:29 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ #include @@ -32,10 +30,11 @@ ** The names of the following types declared in vdbeInt.h are required ** for the VdbeOp definition. */ typedef struct VdbeFunc VdbeFunc; typedef struct Mem Mem; +typedef struct SubProgram SubProgram; /* ** A single instruction of the virtual machine has an opcode ** and as many as three operands. The instruction is recorded ** as an instance of the following structure: @@ -46,23 +45,24 @@ u8 opflags; /* Not currently used */ u8 p5; /* Fifth parameter is an unsigned character */ int p1; /* First operand */ int p2; /* Second parameter (often the jump destination) */ int p3; /* The third parameter */ - union { /* forth parameter */ + union { /* fourth parameter */ int i; /* Integer value if p4type==P4_INT32 */ void *p; /* Generic pointer */ char *z; /* Pointer to data for string (char array) types */ i64 *pI64; /* Used when p4type is P4_INT64 */ double *pReal; /* Used when p4type is P4_REAL */ FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ Mem *pMem; /* Used when p4type is P4_MEM */ - sqlite3_vtab *pVtab; /* Used when p4type is P4_VTAB */ + VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ + SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ } p4; #ifdef SQLITE_DEBUG char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE @@ -70,10 +70,23 @@ u64 cycles; /* Total time spent executing this instruction */ #endif }; typedef struct VdbeOp VdbeOp; + +/* +** A sub-routine used to implement a trigger program. +*/ +struct SubProgram { + VdbeOp *aOp; /* Array of opcodes for sub-program */ + int nOp; /* Elements in aOp[] */ + int nMem; /* Number of memory cells required */ + int nCsr; /* Number of cursors required */ + int nRef; /* Number of pointers to this structure */ + void *token; /* id that may be used to recursive triggers */ +}; + /* ** A smaller version of VdbeOp used for the VdbeAddOpList() function because ** it takes up less space. */ struct VdbeOpList { @@ -83,11 +96,11 @@ signed char p3; /* Third parameter */ }; typedef struct VdbeOpList VdbeOpList; /* -** Allowed values of VdbeOp.p3type +** Allowed values of VdbeOp.p4type */ #define P4_NOTUSED 0 /* The P4 parameter is not used */ #define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ #define P4_STATIC (-2) /* Pointer to a static string */ #define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */ @@ -100,10 +113,11 @@ #define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ +#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure ** is made. That copy is freed when the Vdbe is finalized. But if the ** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still ** gets freed when the Vdbe is finalized so it still should be obtained @@ -166,15 +180,16 @@ void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); void sqlite3VdbeDelete(Vdbe*); -void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int); +void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int); int sqlite3VdbeFinalize(Vdbe*); void sqlite3VdbeResolveLabel(Vdbe*, int); int sqlite3VdbeCurrentAddr(Vdbe*); #ifdef SQLITE_DEBUG + int sqlite3VdbeAssertMayAbort(Vdbe *, int); void sqlite3VdbeTrace(Vdbe*,FILE*); #endif void sqlite3VdbeResetStepResult(Vdbe*); int sqlite3VdbeReset(Vdbe*); void sqlite3VdbeSetNumCols(Vdbe*,int); @@ -181,14 +196,15 @@ int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); void sqlite3VdbeCountChanges(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*); void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); void sqlite3VdbeSwap(Vdbe*,Vdbe*); +VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); +void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int); +sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); +void sqlite3VdbeSetVarmask(Vdbe*, int); -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -int sqlite3VdbeReleaseMemory(int); -#endif UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int); void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*); int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -12,12 +12,10 @@ ** This is the header file for information that is private to the ** VDBE. This information used to all be at the top of the single ** source code file "vdbe.c". When that file became too big (over ** 6000 lines long) it was split up into several smaller files and ** this header information was factored out. -** -** $Id: vdbeInt.h,v 1.174 2009/06/23 14:15:04 drh Exp $ */ #ifndef _VDBEINT_H_ #define _VDBEINT_H_ /* @@ -54,20 +52,16 @@ 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 pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */ - Bool ephemPseudoTable; Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ Btree *pBt; /* Separate file holding temporary table */ - int nData; /* Number of bytes in pData */ - char *pData; /* Data for a NEW or OLD pseudo-table */ - i64 iKey; /* Key for the NEW or OLD pseudo-table row */ + int pseudoTableReg; /* Register holding pseudotable content. */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int nField; /* Number of fields in the header */ i64 seqCount; /* Sequence counter */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite3_module *pModule; /* Module for cursor pVtabCursor */ @@ -75,22 +69,59 @@ /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or ** OP_IsUnique opcode on this cursor. */ int seekResult; /* Cached information about the header for the data record that the - ** cursor is currently pointing to. Only valid if cacheValid is true. + ** cursor is currently pointing to. Only valid if cacheStatus matches + ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of + ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that + ** the cache is out of date. + ** ** aRow might point to (ephemeral) data for the current row, or it might ** be NULL. */ - int cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ + u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ int payloadSize; /* Total number of bytes in the record */ u32 *aType; /* Type values for all entries in the record */ u32 *aOffset; /* Cached offsets to the start of each columns data */ u8 *aRow; /* Data for the current row, if all on one page */ }; typedef struct VdbeCursor VdbeCursor; +/* +** When a sub-program is executed (OP_Program), a structure of this type +** is allocated to store the current value of the program counter, as +** well as the current memory cell array and various other frame specific +** values stored in the Vdbe struct. When the sub-program is finished, +** these values are copied back to the Vdbe from the VdbeFrame structure, +** restoring the state of the VM to as it was before the sub-program +** began executing. +** +** Frames are stored in a linked list headed at Vdbe.pParent. Vdbe.pParent +** is the parent of the current frame, or zero if the current frame +** is the main Vdbe program. +*/ +typedef struct VdbeFrame VdbeFrame; +struct VdbeFrame { + Vdbe *v; /* VM this frame belongs to */ + int pc; /* Program Counter */ + Op *aOp; /* Program instructions */ + int nOp; /* Size of aOp array */ + Mem *aMem; /* Array of memory cells */ + int nMem; /* Number of entries in aMem */ + VdbeCursor **apCsr; /* Element of Vdbe cursors */ + u16 nCursor; /* Number of entries in apCsr */ + void *token; /* Copy of SubProgram.token */ + int nChildMem; /* Number of memory cells for child frame */ + int nChildCsr; /* Number of cursors for child frame */ + i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ + int nChange; /* Statement changes (Vdbe.nChanges) */ + VdbeFrame *pParent; /* Parent of this frame */ +}; + +#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) + /* ** A value for VdbeCursor.cacheValid that means the cache is always invalid. */ #define CACHE_STALE 0 @@ -109,10 +140,11 @@ union { i64 i; /* Integer value. */ int nZero; /* Used when bit MEM_Zero is set in flags */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ + VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ } u; double r; /* Real value */ sqlite3 *db; /* The associated database connection */ char *z; /* String or BLOB value */ int n; /* Number of characters in string value, excluding '\0' */ @@ -142,10 +174,11 @@ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ +#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ #define MEM_TypeMask 0x00ff /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the @@ -221,25 +254,10 @@ struct Set { Hash hash; /* A set is just a hash table */ HashElem *prev; /* Previously accessed hash elemen */ }; -/* -** A Context stores the last insert rowid, the last statement change count, -** and the current statement change count (i.e. changes since last statement). -** The current keylist is also stored in the context. -** Elements of Context structure type make up the ContextStack, which is -** updated by the ContextPush and ContextPop opcodes (used by triggers). -** The context is pushed before executing a trigger a popped when the -** trigger finishes. -*/ -typedef struct Context Context; -struct Context { - i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ - int nChange; /* Statement changes (Vdbe.nChanges) */ -}; - /* ** An instance of the virtual machine. This structure contains the complete ** state of the virtual machine. ** ** The "sqlite3_stmt" structure pointer that is returned by sqlite3_compile() @@ -268,20 +286,17 @@ u16 nResColumn; /* Number of columns in one row of the result set */ u16 nCursor; /* Number of slots in apCsr[] */ VdbeCursor **apCsr; /* One element of this array for each open cursor */ u8 errorAction; /* Recovery action to do in case of an error */ u8 okVar; /* True if azVar[] has been initialized */ - u16 nVar; /* Number of entries in aVar[] */ + ynVar nVar; /* Number of entries in aVar[] */ Mem *aVar; /* Values for the OP_Variable opcode. */ char **azVar; /* Name of variables */ u32 magic; /* Magic number for sanity checking */ int nMem; /* Number of memory locations currently allocated */ Mem *aMem; /* The memory locations */ - 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*/ + u32 cacheCtr; /* VdbeCursor row cache generation counter */ int pc; /* The program counter */ int rc; /* Value to return */ char *zErrMsg; /* Error message written here */ u8 explain; /* True if EXPLAIN present on SQL command */ u8 changeCntOn; /* True to update the change-counter */ @@ -296,14 +311,19 @@ i64 startTime; /* Time when query started - used for profiling */ BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ int aCounter[2]; /* Counters used by sqlite3_stmt_status() */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ + i64 nFkConstraint; /* Number of imm. FK constraints this VM */ + i64 nStmtDefCons; /* Number of def. constraints when stmt started */ int iStatement; /* Statement number (or 0 if has not opened stmt) */ #ifdef SQLITE_DEBUG FILE *trace; /* Write an execution trace here, if not NULL */ #endif + VdbeFrame *pFrame; /* Parent frame */ + int nFrame; /* Number of frames in pFrame list */ + u32 expmask; /* Binding to these vars invalidates VM */ }; /* ** The following are allowed values for Vdbe.magic */ @@ -360,12 +380,18 @@ 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); +void sqlite3VdbeFrameDelete(VdbeFrame*); +int sqlite3VdbeFrameRestore(VdbeFrame *); +void sqlite3VdbeMemStoreType(Mem *pMem); + +#ifndef SQLITE_OMIT_FOREIGN_KEY +int sqlite3VdbeCheckFk(Vdbe *, int); +#else +# define sqlite3VdbeCheckFk(p,i) 0 #endif #ifndef SQLITE_OMIT_SHARED_CACHE void sqlite3VdbeMutexArrayEnter(Vdbe *p); #else Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -10,12 +10,10 @@ ** ************************************************************************* ** ** This file contains code use to implement APIs that are part of the ** VDBE. -** -** $Id: vdbeapi.c,v 1.167 2009/06/25 01:47:12 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" #ifndef SQLITE_OMIT_DEPRECATED @@ -74,11 +72,11 @@ rc = SQLITE_OK; }else{ Vdbe *v = (Vdbe*)pStmt; sqlite3_mutex_enter(v->db->mutex); rc = sqlite3VdbeReset(v); - sqlite3VdbeMakeReady(v, -1, 0, 0, 0); + sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0, 0); assert( (rc & (v->db->errMask))==rc ); rc = sqlite3ApiExit(v->db, rc); sqlite3_mutex_leave(v->db->mutex); } return rc; @@ -96,10 +94,13 @@ #endif sqlite3_mutex_enter(mutex); for(i=0; inVar; i++){ sqlite3VdbeMemRelease(&p->aVar[i]); p->aVar[i].flags = MEM_Null; + } + if( p->isPrepareV2 && p->expmask ){ + p->expired = 1; } sqlite3_mutex_leave(mutex); return rc; } @@ -302,11 +303,11 @@ if( db->mallocFailed ){ return SQLITE_NOMEM; } if( p->pc<=0 && p->expired ){ - if( ALWAYS(p->rc==SQLITE_OK) ){ + if( ALWAYS(p->rc==SQLITE_OK || p->rc==SQLITE_SCHEMA) ){ p->rc = SQLITE_SCHEMA; } rc = SQLITE_ERROR; goto end_of_step; } @@ -320,10 +321,12 @@ ** from interrupting a statement that has not yet started. */ if( db->activeVdbeCnt==0 ){ db->u1.isInterrupted = 0; } + + assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 ); #ifndef SQLITE_OMIT_TRACE if( db->xProfile && !db->init.busy ){ double rNow; sqlite3OsCurrentTime(db->pVfs, &rNow); @@ -910,10 +913,19 @@ i--; pVar = &p->aVar[i]; sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; sqlite3Error(p->db, SQLITE_OK, 0); + + /* If the bit corresponding to this variable in Vdbe.expmask is set, then + ** binding a new value to this variable invalidates the current query plan. + */ + if( p->isPrepareV2 && + ((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff) + ){ + p->expired = 1; + } return SQLITE_OK; } /* ** Bind a text or BLOB value. @@ -1159,10 +1171,16 @@ 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; + } + if( pTo->isPrepareV2 && pTo->expmask ){ + pTo->expired = 1; + } + if( pFrom->isPrepareV2 && pFrom->expmask ){ + pFrom->expired = 1; } return sqlite3TransferBindings(pFromStmt, pToStmt); } #endif Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -11,12 +11,10 @@ ************************************************************************* ** This file contains code used for creating, destroying, and populating ** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior ** 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.467 2009/06/26 16:32:13 shane Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -51,17 +49,18 @@ /* ** Remember the SQL string for a prepared statement. */ void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){ + assert( isPrepareV2==1 || isPrepareV2==0 ); 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; + p->isPrepareV2 = (u8)isPrepareV2; } /* ** Return the SQL associated with a prepared statement */ @@ -238,10 +237,131 @@ if( p->aLabel ){ p->aLabel[j] = p->nOp; } } +#ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */ + +/* +** The following type and function are used to iterate through all opcodes +** in a Vdbe main program and each of the sub-programs (triggers) it may +** invoke directly or indirectly. It should be used as follows: +** +** Op *pOp; +** VdbeOpIter sIter; +** +** memset(&sIter, 0, sizeof(sIter)); +** sIter.v = v; // v is of type Vdbe* +** while( (pOp = opIterNext(&sIter)) ){ +** // Do something with pOp +** } +** sqlite3DbFree(v->db, sIter.apSub); +** +*/ +typedef struct VdbeOpIter VdbeOpIter; +struct VdbeOpIter { + Vdbe *v; /* Vdbe to iterate through the opcodes of */ + SubProgram **apSub; /* Array of subprograms */ + int nSub; /* Number of entries in apSub */ + int iAddr; /* Address of next instruction to return */ + int iSub; /* 0 = main program, 1 = first sub-program etc. */ +}; +static Op *opIterNext(VdbeOpIter *p){ + Vdbe *v = p->v; + Op *pRet = 0; + Op *aOp; + int nOp; + + if( p->iSub<=p->nSub ){ + + if( p->iSub==0 ){ + aOp = v->aOp; + nOp = v->nOp; + }else{ + aOp = p->apSub[p->iSub-1]->aOp; + nOp = p->apSub[p->iSub-1]->nOp; + } + assert( p->iAddriAddr]; + p->iAddr++; + if( p->iAddr==nOp ){ + p->iSub++; + p->iAddr = 0; + } + + if( pRet->p4type==P4_SUBPROGRAM ){ + int nByte = (p->nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; jnSub; j++){ + if( p->apSub[j]==pRet->p4.pProgram ) break; + } + if( j==p->nSub ){ + p->apSub = sqlite3DbReallocOrFree(v->db, p->apSub, nByte); + if( !p->apSub ){ + pRet = 0; + }else{ + p->apSub[p->nSub++] = pRet->p4.pProgram; + } + } + } + } + + return pRet; +} + +/* +** Check if the program stored in the VM associated with pParse may +** throw an ABORT exception (causing the statement, but not entire transaction +** to be rolled back). This condition is true if the main program or any +** sub-programs contains any of the following: +** +** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. +** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. +** * OP_Destroy +** * OP_VUpdate +** * OP_VRename +** * OP_FkCounter with P2==0 (immediate foreign key constraint) +** +** Then check that the value of Parse.mayAbort is true if an +** ABORT may be thrown, or false otherwise. Return true if it does +** match, or false otherwise. This function is intended to be used as +** part of an assert statement in the compiler. Similar to: +** +** assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); +*/ +int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ + int hasAbort = 0; + Op *pOp; + VdbeOpIter sIter; + memset(&sIter, 0, sizeof(sIter)); + sIter.v = v; + + while( (pOp = opIterNext(&sIter))!=0 ){ + int opcode = pOp->opcode; + if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename +#ifndef SQLITE_OMIT_FOREIGN_KEY + || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1) +#endif + || ((opcode==OP_Halt || opcode==OP_HaltIfNull) + && (pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) + ){ + hasAbort = 1; + break; + } + } + sqlite3DbFree(v->db, sIter.apSub); + + /* Return true if hasAbort==mayAbort. Or if a malloc failure occured. + ** If malloc failed, then the while() loop above may not have iterated + ** through all opcodes and hasAbort may be set incorrectly. Return + ** true for this case to prevent the assert() in the callers frame + ** from failing. */ + return ( v->db->mallocFailed || hasAbort==mayAbort ); +} +#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ + /* ** Loop through the program looking for P2 values that are negative ** on jump instructions. Each such value is a label. Resolve the ** label by setting the P2 value to its correct non-zero value. ** @@ -248,57 +368,29 @@ ** This routine is called once after all opcodes have been inserted. ** ** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument ** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by ** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array. -** -** This routine also does the following optimization: It scans for -** instructions that might cause a statement rollback. Such instructions -** are: -** -** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. -** * OP_Destroy -** * OP_VUpdate -** * OP_VRename -** -** If no such instruction is found, then every Statement instruction -** is changed to a Noop. In this way, we avoid creating the statement -** journal file unnecessarily. */ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ int i; - int nMaxArgs = 0; + int nMaxArgs = *pMaxFuncArgs; Op *pOp; int *aLabel = p->aLabel; - int doesStatementRollback = 0; - int hasStatementBegin = 0; p->readOnly = 1; - p->usesStmtJournal = 0; for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ u8 opcode = pOp->opcode; if( opcode==OP_Function || opcode==OP_AggStep ){ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5; #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( opcode==OP_VUpdate ){ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; #endif - } - if( opcode==OP_Halt ){ - if( pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort ){ - doesStatementRollback = 1; - } - }else if( opcode==OP_Statement ){ - hasStatementBegin = 1; - p->usesStmtJournal = 1; - }else if( opcode==OP_Destroy ){ - doesStatementRollback = 1; }else if( opcode==OP_Transaction && pOp->p2!=0 ){ p->readOnly = 0; #ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( opcode==OP_VUpdate || opcode==OP_VRename ){ - doesStatementRollback = 1; }else if( opcode==OP_VFilter ){ int n; assert( p->nOp - i >= 3 ); assert( pOp[-1].opcode==OP_Integer ); n = pOp[-1].p1; @@ -313,33 +405,43 @@ } sqlite3DbFree(p->db, p->aLabel); p->aLabel = 0; *pMaxFuncArgs = nMaxArgs; - - /* If we never rollback a statement transaction, then statement - ** transactions are not needed. So change every OP_Statement - ** opcode into an OP_Noop. This avoid a call to sqlite3OsOpenExclusive() - ** which can be expensive on some platforms. - */ - if( hasStatementBegin && !doesStatementRollback ){ - p->usesStmtJournal = 0; - for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ - if( pOp->opcode==OP_Statement ){ - pOp->opcode = OP_Noop; - } - } - } } /* ** Return the address of the next instruction to be inserted. */ int sqlite3VdbeCurrentAddr(Vdbe *p){ assert( p->magic==VDBE_MAGIC_INIT ); return p->nOp; } + +/* +** This function returns a pointer to the array of opcodes associated with +** the Vdbe passed as the first argument. It is the callers responsibility +** to arrange for the returned array to be eventually freed using the +** vdbeFreeOpArray() function. +** +** Before returning, *pnOp is set to the number of entries in the returned +** array. Also, *pnMaxArg is set to the larger of its current value and +** the number of entries in the Vdbe.apArg[] array required to execute the +** returned program. +*/ +VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ + VdbeOp *aOp = p->aOp; + assert( aOp && !p->db->mallocFailed ); + + /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ + assert( p->aMutex.nMutex==0 ); + + resolveP2Values(p, pnMaxArg); + *pnOp = p->nOp; + p->aOp = 0; + return aOp; +} /* ** Add a whole list of operations to the operation stack. Return the ** address of the first operation added. */ @@ -476,10 +578,65 @@ } case P4_MEM: { sqlite3ValueFree((sqlite3_value*)p4); break; } + case P4_VTAB : { + sqlite3VtabUnlock((VTable *)p4); + break; + } + case P4_SUBPROGRAM : { + sqlite3VdbeProgramDelete(db, (SubProgram *)p4, 1); + break; + } + } + } +} + +/* +** Free the space allocated for aOp and any p4 values allocated for the +** opcodes contained within. If aOp is not NULL it is assumed to contain +** nOp entries. +*/ +static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ + if( aOp ){ + Op *pOp; + for(pOp=aOp; pOp<&aOp[nOp]; pOp++){ + freeP4(db, pOp->p4type, pOp->p4.p); +#ifdef SQLITE_DEBUG + sqlite3DbFree(db, pOp->zComment); +#endif + } + } + sqlite3DbFree(db, aOp); +} + +/* +** Decrement the ref-count on the SubProgram structure passed as the +** second argument. If the ref-count reaches zero, free the structure. +** +** The array of VDBE opcodes stored as SubProgram.aOp is freed if +** either the ref-count reaches zero or parameter freeop is non-zero. +** +** Since the array of opcodes pointed to by SubProgram.aOp may directly +** or indirectly contain a reference to the SubProgram structure itself. +** By passing a non-zero freeop parameter, the caller may ensure that all +** SubProgram structures and their aOp arrays are freed, even when there +** are such circular references. +*/ +void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){ + if( p ){ + assert( p->nRef>0 ); + if( freeop || p->nRef==1 ){ + Op *aOp = p->aOp; + p->aOp = 0; + vdbeFreeOpArray(db, aOp, p->nOp); + p->nOp = 0; + } + p->nRef--; + if( p->nRef==0 ){ + sqlite3DbFree(db, p); } } } @@ -529,11 +686,11 @@ sqlite3 *db; assert( p!=0 ); db = p->db; assert( p->magic==VDBE_MAGIC_INIT ); if( p->aOp==0 || db->mallocFailed ){ - if (n != P4_KEYINFO) { + if ( n!=P4_KEYINFO && n!=P4_VTAB ) { freeP4(db, n, (void*)*(char**)&zP4); } return; } assert( p->nOp>0 ); @@ -574,10 +731,15 @@ pOp->p4type = P4_NOTUSED; } }else if( n==P4_KEYINFO_HANDOFF ){ pOp->p4.p = (void*)zP4; pOp->p4type = P4_KEYINFO; + }else if( n==P4_VTAB ){ + pOp->p4.p = (void*)zP4; + pOp->p4type = P4_VTAB; + sqlite3VtabLock((VTable *)zP4); + assert( ((VTable *)zP4)->db==p->db ); }else if( n<0 ){ pOp->p4.p = (void*)zP4; pOp->p4type = (signed char)n; }else{ if( n==0 ) n = sqlite3Strlen30(zP4); @@ -593,10 +755,11 @@ ** makes the code easier to read during debugging. None of this happens ** in a production build. */ void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ va_list ap; + if( !p ) return; assert( p->nOp>0 || p->aOp==0 ); assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); if( p->nOp ){ char **pz = &p->aOp[p->nOp-1].zComment; va_start(ap, zFormat); @@ -605,10 +768,11 @@ va_end(ap); } } void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ va_list ap; + if( !p ) return; sqlite3VdbeAddOp0(p, OP_Noop); assert( p->nOp>0 || p->aOp==0 ); assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); if( p->nOp ){ char **pz = &p->aOp[p->nOp-1].zComment; @@ -724,23 +888,30 @@ zP4 = pMem->z; }else if( pMem->flags & MEM_Int ){ sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i); }else if( pMem->flags & MEM_Real ){ sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r); + }else{ + assert( pMem->flags & MEM_Blob ); + zP4 = "(blob)"; } break; } #ifndef SQLITE_OMIT_VIRTUALTABLE case P4_VTAB: { - sqlite3_vtab *pVtab = pOp->p4.pVtab; + sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule); break; } #endif case P4_INTARRAY: { sqlite3_snprintf(nTemp, zTemp, "intarray"); break; + } + case P4_SUBPROGRAM: { + sqlite3_snprintf(nTemp, zTemp, "program"); + break; } default: { zP4 = pOp->p4.z; if( zP4==0 ){ zP4 = zTemp; @@ -753,11 +924,10 @@ } #endif /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. -** */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ int mask; assert( i>=0 && idb->nDb && ibtreeMask)*8 ); @@ -812,11 +982,11 @@ ** sqlite3MemRelease() were called from here. With -O2, this jumps ** to 6.6 percent. The test case is inserting 1000 rows into a table ** with no indexes using a single prepared INSERT statement, bind() ** and reset(). Inserts are grouped into a transaction. */ - if( p->flags&(MEM_Agg|MEM_Dyn) ){ + if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){ sqlite3VdbeMemRelease(p); }else if( p->zMalloc ){ sqlite3DbFree(db, p->zMalloc); p->zMalloc = 0; } @@ -825,29 +995,24 @@ } db->mallocFailed = malloc_failed; } } -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -int sqlite3VdbeReleaseBuffers(Vdbe *p){ - int ii; - int nFree = 0; - assert( sqlite3_mutex_held(p->db->mutex) ); - for(ii=1; ii<=p->nMem; ii++){ - Mem *pMem = &p->aMem[ii]; - if( pMem->flags & MEM_RowSet ){ - sqlite3RowSetClear(pMem->u.pRowSet); - } - if( pMem->z && pMem->flags&MEM_Dyn ){ - assert( !pMem->xDel ); - nFree += sqlite3DbMallocSize(pMem->db, pMem->z); - sqlite3VdbeMemRelease(pMem); - } - } - return nFree; -} -#endif +/* +** Delete a VdbeFrame object and its contents. VdbeFrame objects are +** allocated by the OP_Program opcode in sqlite3VdbeExec(). +*/ +void sqlite3VdbeFrameDelete(VdbeFrame *p){ + int i; + Mem *aMem = VdbeFrameMem(p); + VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem]; + for(i=0; inChildCsr; i++){ + sqlite3VdbeFreeCursor(p->v, apCsr[i]); + } + releaseMemArray(aMem, p->nChildMem); + sqlite3DbFree(p->v->db, p); +} #ifndef SQLITE_OMIT_EXPLAIN /* ** Give a listing of the program in the virtual machine. ** @@ -861,46 +1026,74 @@ ** EXPLAIN QUERY PLAN. */ int sqlite3VdbeList( Vdbe *p /* The VDBE */ ){ + int nRow; /* Total number of rows to return */ + int nSub = 0; /* Number of sub-vdbes seen so far */ + SubProgram **apSub = 0; /* Array of sub-vdbes */ + Mem *pSub = 0; sqlite3 *db = p->db; int i; int rc = SQLITE_OK; Mem *pMem = p->pResultSet = &p->aMem[1]; assert( p->explain ); - if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE; + assert( p->magic==VDBE_MAGIC_RUN ); assert( db->magic==SQLITE_MAGIC_BUSY ); assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); /* Even though this opcode does not use dynamic strings for ** the result, result columns may become dynamic if the user calls ** sqlite3_column_text16(), causing a translation to UTF-16 encoding. */ - releaseMemArray(pMem, p->nMem); + releaseMemArray(pMem, 8); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ db->mallocFailed = 1; return SQLITE_ERROR; } + + /* Figure out total number of rows that will be returned by this + ** EXPLAIN program. */ + nRow = p->nOp; + if( p->explain==1 ){ + pSub = &p->aMem[9]; + if( pSub->flags&MEM_Blob ){ + nSub = pSub->n/sizeof(Vdbe*); + apSub = (SubProgram **)pSub->z; + } + for(i=0; inOp; + } + } do{ i = p->pc++; - }while( inOp && p->explain==2 && p->aOp[i].opcode!=OP_Explain ); - if( i>=p->nOp ){ + }while( iexplain==2 && p->aOp[i].opcode!=OP_Explain ); + if( i>=nRow ){ p->rc = SQLITE_OK; rc = SQLITE_DONE; }else if( db->u1.isInterrupted ){ p->rc = SQLITE_INTERRUPT; rc = SQLITE_ERROR; sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc)); }else{ char *z; - Op *pOp = &p->aOp[i]; + Op *pOp; + if( inOp ){ + pOp = &p->aOp[i]; + }else{ + int j; + i -= p->nOp; + for(j=0; i>=apSub[j]->nOp; j++){ + i -= apSub[j]->nOp; + } + pOp = &apSub[j]->aOp[i]; + } if( p->explain==1 ){ pMem->flags = MEM_Int; pMem->type = SQLITE_INTEGER; pMem->u.i = i; /* Program counter */ pMem++; @@ -910,10 +1103,24 @@ assert( pMem->z!=0 ); pMem->n = sqlite3Strlen30(pMem->z); pMem->type = SQLITE_TEXT; pMem->enc = SQLITE_UTF8; pMem++; + + if( pOp->p4type==P4_SUBPROGRAM ){ + int nByte = (nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; jp4.pProgram ) break; + } + if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, 1) ){ + apSub = (SubProgram **)pSub->z; + apSub[nSub++] = pOp->p4.pProgram; + pSub->flags |= MEM_Blob; + pSub->n = nSub*sizeof(SubProgram*); + } + } } pMem->flags = MEM_Int; pMem->u.i = pOp->p1; /* P1 */ pMem->type = SQLITE_INTEGER; @@ -1053,11 +1260,11 @@ 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 ){ + if( &(*ppFrom)[nByte] <= pEnd ){ *(void**)pp = (void *)*ppFrom; *ppFrom += nByte; }else{ *pnByte += nByte; } @@ -1084,11 +1291,13 @@ void sqlite3VdbeMakeReady( Vdbe *p, /* The VDBE */ int nVar, /* Number of '?' see in the SQL statement */ int nMem, /* Number of memory cells to allocate */ int nCursor, /* Number of cursors to allocate */ - int isExplain /* True if the EXPLAIN keywords is present */ + int nArg, /* Maximum number of args in SubPrograms */ + int isExplain, /* True if the EXPLAIN keywords is present */ + int usesStmtJournal /* True to set Vdbe.usesStmtJournal */ ){ int n; sqlite3 *db = p->db; assert( p!=0 ); @@ -1115,43 +1324,42 @@ /* 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( nVar>=0 && !db->mallocFailed ){ + if( nVar>=0 && ALWAYS(db->mallocFailed==0) ){ 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); + p->usesStmtJournal = (u8)usesStmtJournal; if( isExplain && nMem<10 ){ nMem = 10; } + memset(zCsr, 0, zEnd-zCsr); zCsr += (zCsr - (u8*)0)&7; assert( EIGHT_BYTE_ALIGNMENT(zCsr) ); - if( zEndaMem, 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); + p->pFree = sqlite3DbMallocZero(db, nByte); } zCsr = p->pFree; zEnd = &zCsr[nByte]; }while( nByte && !db->mallocFailed ); p->nCursor = (u16)nCursor; if( p->aVar ){ - p->nVar = (u16)nVar; + p->nVar = (ynVar)nVar; for(n=0; naVar[n].flags = MEM_Null; p->aVar[n].db = db; } } @@ -1214,28 +1422,59 @@ pModule->xClose(pVtabCursor); (void)sqlite3SafetyOn(p->db); p->inVtabMethod = 0; } #endif - if( !pCx->ephemPseudoTable ){ - sqlite3DbFree(p->db, pCx->pData); - } +} + +/* +** Copy the values stored in the VdbeFrame structure to its Vdbe. This +** is used, for example, when a trigger sub-program is halted to restore +** control to the main program. +*/ +int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ + Vdbe *v = pFrame->v; + v->aOp = pFrame->aOp; + v->nOp = pFrame->nOp; + v->aMem = pFrame->aMem; + v->nMem = pFrame->nMem; + v->apCsr = pFrame->apCsr; + v->nCursor = pFrame->nCursor; + v->db->lastRowid = pFrame->lastRowid; + v->nChange = pFrame->nChange; + return pFrame->pc; } /* -** Close all cursors except for VTab cursors that are currently -** in use. +** Close all cursors. +** +** Also release any dynamic memory held by the VM in the Vdbe.aMem memory +** cell array. This is necessary as the memory cell array may contain +** pointers to VdbeFrame objects, which may in turn contain pointers to +** open cursors. */ -static void closeAllCursorsExceptActiveVtabs(Vdbe *p){ - int i; - if( p->apCsr==0 ) return; - for(i=0; inCursor; i++){ - VdbeCursor *pC = p->apCsr[i]; - if( pC && (!p->inVtabMethod || !pC->pVtabCursor) ){ - sqlite3VdbeFreeCursor(p, pC); - p->apCsr[i] = 0; - } +static void closeAllCursors(Vdbe *p){ + if( p->pFrame ){ + VdbeFrame *pFrame = p->pFrame; + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + sqlite3VdbeFrameRestore(pFrame); + } + p->pFrame = 0; + p->nFrame = 0; + + if( p->apCsr ){ + int i; + for(i=0; inCursor; i++){ + VdbeCursor *pC = p->apCsr[i]; + if( pC ){ + sqlite3VdbeFreeCursor(p, pC); + p->apCsr[i] = 0; + } + } + } + if( p->aMem ){ + releaseMemArray(&p->aMem[1], p->nMem); } } /* ** Clean up the VM after execution. @@ -1243,27 +1482,20 @@ ** This routine will automatically close any cursors, lists, and/or ** sorters that were left open. It also deletes the values of ** variables in the aVar[] array. */ static void Cleanup(Vdbe *p){ - int i; sqlite3 *db = p->db; - Mem *pMem; - closeAllCursorsExceptActiveVtabs(p); - for(pMem=&p->aMem[1], i=1; i<=p->nMem; i++, pMem++){ - if( pMem->flags & MEM_RowSet ){ - sqlite3RowSetClear(pMem->u.pRowSet); - } - MemSetTypeFlag(pMem, MEM_Null); - } - releaseMemArray(&p->aMem[1], p->nMem); - if( p->contextStack ){ - sqlite3DbFree(db, p->contextStack); - } - p->contextStack = 0; - p->contextStackDepth = 0; - p->contextStackTop = 0; + +#ifdef SQLITE_DEBUG + /* Execute assert() statements to ensure that the Vdbe.apCsr[] and + ** Vdbe.aMem[] arrays have already been cleaned up. */ + int i; + for(i=0; inCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 ); + for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null ); +#endif + sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; p->pResultSet = 0; } @@ -1367,16 +1599,13 @@ } } /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ - assert( (db->flags & SQLITE_CommitBusy)==0 ); - db->flags |= SQLITE_CommitBusy; (void)sqlite3SafetyOff(db); rc = db->xCommitCallback(db->pCommitArg); (void)sqlite3SafetyOn(db); - db->flags &= ~SQLITE_CommitBusy; if( rc ){ return SQLITE_CONSTRAINT; } } @@ -1615,11 +1844,17 @@ ** Otherwise SQLITE_OK. */ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ sqlite3 *const db = p->db; int rc = SQLITE_OK; - if( p->iStatement && db->nStatement ){ + + /* If p->iStatement is greater than zero, then this Vdbe opened a + ** statement transaction that should be closed here. The only exception + ** is that an IO error may have occured, causing an emergency rollback. + ** In this case (db->nStatement==0), and there is nothing to do. + */ + if( db->nStatement && p->iStatement ){ int i; const int iSavepoint = p->iStatement-1; assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE); assert( db->nStatement>0 ); @@ -1640,10 +1875,17 @@ } } } db->nStatement--; p->iStatement = 0; + + /* If the statement transaction is being rolled back, also restore the + ** database handles deferred constraint counter to the value it had when + ** the statement transaction was opened. */ + if( eOp==SAVEPOINT_ROLLBACK ){ + db->nDeferredCons = p->nStmtDefCons; + } } return rc; } /* @@ -1671,10 +1913,33 @@ sqlite3BtreeEnterAll(p->db); #endif } #endif +/* +** This function is called when a transaction opened by the database +** handle associated with the VM passed as an argument is about to be +** committed. If there are outstanding deferred foreign key constraint +** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. +** +** If there are outstanding FK violations and this function returns +** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT and write +** an error message to it. Then return SQLITE_ERROR. +*/ +#ifndef SQLITE_OMIT_FOREIGN_KEY +int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ + sqlite3 *db = p->db; + if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){ + p->rc = SQLITE_CONSTRAINT; + p->errorAction = OE_Abort; + sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed"); + return SQLITE_ERROR; + } + return SQLITE_OK; +} +#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 ** changes. If a rollback is needed, then do the rollback. ** @@ -1707,11 +1972,11 @@ */ if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } - closeAllCursorsExceptActiveVtabs(p); + closeAllCursors(p); if( p->magic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } checkActiveVdbeCnt(db); @@ -1724,22 +1989,19 @@ /* Lock all btrees used by the statement */ sqlite3VdbeMutexArrayEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; + assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; if( isSpecialError ){ /* If the query was read-only, we need do no rollback at all. Otherwise, ** proceed with the special handling. */ if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ - if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){ - eStatementOp = SAVEPOINT_ROLLBACK; - p->rc = SQLITE_BUSY; - }else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) - && p->usesStmtJournal ){ + if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ 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. */ @@ -1748,10 +2010,15 @@ sqlite3CloseSavepoints(db); db->autoCommit = 1; } } } + + /* Check for immediate foreign key violations. */ + if( p->rc==SQLITE_OK ){ + sqlite3VdbeCheckFk(p, 0); + } /* 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 @@ -1758,25 +2025,29 @@ ** above has occurred. */ if( !sqlite3VtabInSync(db) && db->autoCommit && db->writeVdbeCnt==(p->readOnly==0) - && (db->flags & SQLITE_CommitBusy)==0 ){ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - /* The auto-commit flag is true, and the vdbe program was - ** successful or hit an 'OR FAIL' constraint. This means a commit - ** is required. - */ + if( sqlite3VdbeCheckFk(p, 1) ){ + sqlite3BtreeMutexArrayLeave(&p->aMutex); + return SQLITE_ERROR; + } + /* The auto-commit flag is true, the vdbe program was successful + ** or hit an 'OR FAIL' constraint and there are no deferred foreign + ** key constraints to hold up the transaction. This means a commit + ** is required. */ rc = vdbeCommit(db, p); if( rc==SQLITE_BUSY ){ sqlite3BtreeMutexArrayLeave(&p->aMutex); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; sqlite3RollbackAll(db); }else{ + db->nDeferredCons = 0; sqlite3CommitInternalChanges(db); } }else{ sqlite3RollbackAll(db); } @@ -1810,11 +2081,11 @@ } /* 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( p->changeCntOn ){ if( eStatementOp!=SAVEPOINT_ROLLBACK ){ sqlite3VdbeSetChanges(db, p->nChange); }else{ sqlite3VdbeSetChanges(db, 0); } @@ -1957,12 +2228,10 @@ int sqlite3VdbeFinalize(Vdbe *p){ int rc = SQLITE_OK; if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ rc = sqlite3VdbeReset(p); assert( (rc & p->db->errMask)==rc ); - }else if( p->magic!=VDBE_MAGIC_INIT ){ - return SQLITE_MISUSE; } sqlite3VdbeDelete(p); return rc; } @@ -1987,14 +2256,13 @@ /* ** Delete an entire VDBE. */ void sqlite3VdbeDelete(Vdbe *p){ - int i; sqlite3 *db; - if( p==0 ) return; + if( NEVER(p==0) ) return; db = p->db; if( p->pPrev ){ p->pPrev->pNext = p->pNext; }else{ assert( db->pVdbe==p ); @@ -2001,26 +2269,17 @@ db->pVdbe = p->pNext; } if( p->pNext ){ p->pNext->pPrev = p->pPrev; } - if( p->aOp ){ - Op *pOp = p->aOp; - for(i=0; inOp; i++, pOp++){ - freeP4(db, pOp->p4type, pOp->p4.p); -#ifdef SQLITE_DEBUG - sqlite3DbFree(db, pOp->zComment); -#endif - } - } releaseMemArray(p->aVar, p->nVar); - sqlite3DbFree(db, p->aLabel); releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + vdbeFreeOpArray(db, p->aOp, p->nOp); + sqlite3DbFree(db, p->aLabel); sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); p->magic = VDBE_MAGIC_DEAD; - sqlite3DbFree(db, p->aOp); sqlite3DbFree(db, p->pFree); sqlite3DbFree(db, p); } /* @@ -2054,11 +2313,11 @@ #ifdef SQLITE_TEST sqlite3_search_count++; #endif p->deferredMoveto = 0; p->cacheStatus = CACHE_STALE; - }else if( p->pCursor ){ + }else if( ALWAYS(p->pCursor) ){ int hasMoved; int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved); if( rc ) return rc; if( hasMoved ){ p->cacheStatus = CACHE_STALE; @@ -2427,15 +2686,14 @@ p->aMem = pMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))]; assert( EIGHT_BYTE_ALIGNMENT(pMem) ); idx = getVarint32(aKey, szHdr); d = szHdr; u = 0; - while( idxnField ){ + while( idxnField && d<=nKey ){ u32 serial_type; idx += getVarint32(&aKey[idx], serial_type); - if( d>=nKey && sqlite3VdbeSerialTypeLen(serial_type)>0 ) break; pMem->enc = pKeyInfo->enc; pMem->db = pKeyInfo->db; pMem->flags = 0; pMem->zMalloc = 0; d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); @@ -2455,13 +2713,16 @@ Mem *pMem; assert( p!=0 ); assert( p->flags & UNPACKED_NEED_DESTROY ); for(i=0, pMem=p->aMem; inField; i++, pMem++){ - if( pMem->zMalloc ){ - sqlite3VdbeMemRelease(pMem); - } + /* The unpacked record is always constructed by the + ** sqlite3VdbeUnpackRecord() function above, which makes all + ** strings and blobs static. And none of the elements are + ** ever transformed, so there is never anything to delete. + */ + if( NEVER(pMem->zMalloc) ) sqlite3VdbeMemRelease(pMem); } if( p->flags & UNPACKED_NEED_FREE ){ sqlite3DbFree(p->pKeyInfo->db, p); } } @@ -2537,11 +2798,13 @@ if( rc!=0 ){ break; } i++; } - if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1); + + /* No memory allocation is ever used on mem1. */ + if( NEVER(mem1.zMalloc) ) sqlite3VdbeMemRelease(&mem1); /* If the PREFIX_SEARCH flag is set and all fields except the final ** rowid field were equal, then clear the PREFIX_SEARCH flag and set ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1). ** This is used by the OP_IsUnique opcode. @@ -2591,22 +2854,25 @@ int rc; u32 szHdr; /* Size of the header */ u32 typeRowid; /* Serial type of the rowid */ u32 lenRowid; /* Size of the rowid */ Mem m, v; + + UNUSED_PARAMETER(db); /* Get the size of the index entry. Only indices entries of less ** than 2GiB are support - anything large must be database corruption. ** Any corruption is detected in sqlite3BtreeParseCellPtr(), though, so - ** this code can safely assume that nCellKey is 32-bits */ - sqlite3BtreeKeySize(pCur, &nCellKey); + ** this code can safely assume that nCellKey is 32-bits + */ + assert( sqlite3BtreeCursorIsValid(pCur) ); + rc = sqlite3BtreeKeySize(pCur, &nCellKey); + assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); /* Read in the complete content of the index entry */ - m.flags = 0; - m.db = db; - m.zMalloc = 0; + memset(&m, 0, sizeof(m)); rc = sqlite3VdbeMemFromBtree(pCur, 0, (int)nCellKey, 1, &m); if( rc ){ return rc; } @@ -2631,12 +2897,12 @@ testcase( typeRowid==9 ); if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){ goto idx_rowid_corruption; } lenRowid = sqlite3VdbeSerialTypeLen(typeRowid); - testcase( m.n-lenRowid==szHdr ); - if( unlikely(m.n-lenRowidpCursor; Mem m; - sqlite3BtreeKeySize(pCur, &nCellKey); + assert( sqlite3BtreeCursorIsValid(pCur) ); + rc = sqlite3BtreeKeySize(pCur, &nCellKey); + assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ + /* nCellKey will always be between 0 and 0xffffffff because of the say + ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ *res = 0; - return SQLITE_OK; + return SQLITE_CORRUPT; } - m.db = 0; - m.flags = 0; - m.zMalloc = 0; + memset(&m, 0, sizeof(m)); rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); if( rc ){ return rc; } assert( pUnpacked->flags & UNPACKED_IGNORE_ROWID ); @@ -2733,5 +2998,44 @@ ** Return the database associated with the Vdbe. */ sqlite3 *sqlite3VdbeDb(Vdbe *v){ return v->db; } + +/* +** Return a pointer to an sqlite3_value structure containing the value bound +** parameter iVar of VM v. Except, if the value is an SQL NULL, return +** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* +** constants) to the value before returning it. +** +** The returned value must be freed by the caller using sqlite3ValueFree(). +*/ +sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){ + assert( iVar>0 ); + if( v ){ + Mem *pMem = &v->aVar[iVar-1]; + if( 0==(pMem->flags & MEM_Null) ){ + sqlite3_value *pRet = sqlite3ValueNew(v->db); + if( pRet ){ + sqlite3VdbeMemCopy((Mem *)pRet, pMem); + sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); + sqlite3VdbeMemStoreType((Mem *)pRet); + } + return pRet; + } + } + return 0; +} + +/* +** Configure SQL variable iVar so that binding a new value to it signals +** to sqlite3_reoptimize() that re-preparing the statement may result +** in a better query plan. +*/ +void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ + assert( iVar>0 ); + if( iVar>32 ){ + v->expmask = 0xffffffff; + }else{ + v->expmask |= ((u32)1 << (iVar-1)); + } +} Index: src/vdbeblob.c ================================================================== --- src/vdbeblob.c +++ src/vdbeblob.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains code used to implement incremental BLOB I/O. -** -** $Id: vdbeblob.c,v 1.33 2009/06/01 19:53:31 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -64,23 +62,22 @@ ** transaction. */ static const VdbeOpList openBlob[] = { {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */ {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */ - - /* One of the following two instructions is replaced by an - ** OP_Noop before exection. - */ - {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, 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 */ + {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ + + /* One of the following two instructions is replaced by an OP_Noop. */ + {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ + {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ + + {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ + {OP_NotExists, 0, 9, 1}, /* 6: Seek the cursor */ + {OP_Column, 0, 0, 1}, /* 7 */ + {OP_ResultRow, 1, 0, 0}, /* 8 */ + {OP_Close, 0, 0, 0}, /* 9 */ + {OP_Halt, 0, 0, 0}, /* 10 */ }; Vdbe *v = 0; int rc = SQLITE_OK; char *zErr = 0; @@ -143,66 +140,91 @@ sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* If the value is being opened for writing, check that the - ** column is not indexed. It is against the rules to open an - ** indexed column for writing. - */ + ** column is not indexed, and that it is not part of a foreign key. + ** It is against the rules to open a column to which either of these + ** descriptions applies for writing. */ if( flags ){ + const char *zFault = 0; Index *pIdx; +#ifndef SQLITE_OMIT_FOREIGN_KEY + if( db->flags&SQLITE_ForeignKeys ){ + /* Check that the column is not part of an FK child key definition. It + ** is not necessary to check if it is part of a parent key, as parent + ** key columns must be indexed. The check below will pick up this + ** case. */ + FKey *pFKey; + for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + int j; + for(j=0; jnCol; j++){ + if( pFKey->aCol[j].iFrom==iCol ){ + zFault = "foreign key"; + } + } + } + } +#endif for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int j; for(j=0; jnColumn; j++){ if( pIdx->aiColumn[j]==iCol ){ - sqlite3DbFree(db, zErr); - zErr = sqlite3MPrintf(db, - "cannot open indexed column for writing"); - rc = SQLITE_ERROR; - (void)sqlite3SafetyOff(db); - sqlite3BtreeLeaveAll(db); - goto blob_open_out; + zFault = "indexed"; } } } + if( zFault ){ + sqlite3DbFree(db, zErr); + zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); + rc = SQLITE_ERROR; + (void)sqlite3SafetyOff(db); + sqlite3BtreeLeaveAll(db); + goto blob_open_out; + } } v = sqlite3VdbeCreate(db); if( v ){ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); + flags = !!flags; /* flags = (flags ? 1 : 0); */ /* Configure the OP_Transaction */ sqlite3VdbeChangeP1(v, 0, iDb); - sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0)); + sqlite3VdbeChangeP2(v, 0, flags); /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); + /* Configure the OP_TableLock instruction */ + sqlite3VdbeChangeP1(v, 2, iDb); + sqlite3VdbeChangeP2(v, 2, pTab->tnum); + sqlite3VdbeChangeP3(v, 2, flags); + sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); + /* Remove either the OP_OpenWrite or OpenRead. Set the P2 - ** parameter of the other to pTab->tnum. - */ - flags = !!flags; - sqlite3VdbeChangeToNoop(v, 3 - flags, 1); - sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum); - sqlite3VdbeChangeP3(v, 2 + flags, iDb); + ** parameter of the other to pTab->tnum. */ + sqlite3VdbeChangeToNoop(v, 4 - flags, 1); + sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); + sqlite3VdbeChangeP3(v, 3 + flags, iDb); /* 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. */ - sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); - sqlite3VdbeChangeP2(v, 6, pTab->nCol); + sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); + sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ - sqlite3VdbeMakeReady(v, 1, 1, 1, 0); + sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); } } sqlite3BtreeLeaveAll(db); rc = sqlite3SafetyOff(db); Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -12,12 +12,10 @@ ** ** This file contains code use to manipulate "Mem" structure. A "Mem" ** stores a single value in the VDBE. Mem is an opaque structure visible ** only within the VDBE. Interface routines refer to a Mem using the ** name sqlite_value -** -** $Id: vdbemem.c,v 1.150 2009/06/25 01:47:12 drh Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" /* @@ -268,11 +266,15 @@ ** invoking an external callback, free it now. Calling this function ** does not free any Mem.zMalloc buffer. */ void sqlite3VdbeMemReleaseExternal(Mem *p){ assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) ); - if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet) ){ + testcase( p->flags & MEM_Agg ); + testcase( p->flags & MEM_Dyn ); + testcase( p->flags & MEM_RowSet ); + testcase( p->flags & MEM_Frame ); + if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){ if( p->flags&MEM_Agg ){ sqlite3VdbeMemFinalize(p, p->u.pDef); assert( (p->flags & MEM_Agg)==0 ); sqlite3VdbeMemRelease(p); }else if( p->flags&MEM_Dyn && p->xDel ){ @@ -279,10 +281,12 @@ assert( (p->flags&MEM_RowSet)==0 ); p->xDel((void *)p->z); p->xDel = 0; }else if( p->flags&MEM_RowSet ){ sqlite3RowSetClear(p->u.pRowSet); + }else if( p->flags&MEM_Frame ){ + sqlite3VdbeMemSetNull(p); } } } /* @@ -416,15 +420,18 @@ ** ** (1) the round-trip conversion real->int->real is a no-op, and ** (2) The integer is neither the largest nor the smallest ** possible integer (ticket #3922) ** - ** The second term in the following conditional enforces the second - ** condition under the assumption that additional overflow causes - ** values to wrap around. + ** The second and third terms in the following conditional enforces + ** the second condition under the assumption that addition overflow causes + ** values to wrap around. On x86 hardware, the third term is always + ** true and could be omitted. But we leave it in because other + ** architectures might behave differently. */ - if( pMem->r==(double)pMem->u.i && (pMem->u.i-1) < (pMem->u.i+1) ){ + if( pMem->r==(double)pMem->u.i && pMem->u.i>SMALLEST_INT64 + && ALWAYS(pMem->u.iflags |= MEM_Int; } } /* @@ -477,10 +484,13 @@ /* ** Delete any previous value and set the value stored in *pMem to NULL. */ void sqlite3VdbeMemSetNull(Mem *pMem){ + if( pMem->flags & MEM_Frame ){ + sqlite3VdbeFrameDelete(pMem->u.pFrame); + } if( pMem->flags & MEM_RowSet ){ sqlite3RowSetClear(pMem->u.pRowSet); } MemSetTypeFlag(pMem, MEM_Null); pMem->type = SQLITE_NULL; @@ -496,10 +506,18 @@ pMem->type = SQLITE_BLOB; pMem->n = 0; if( n<0 ) n = 0; pMem->u.nZero = n; pMem->enc = SQLITE_UTF8; + +#ifdef SQLITE_OMIT_INCRBLOB + sqlite3VdbeMemGrow(pMem, n, 0); + if( pMem->z ){ + pMem->n = n; + memset(pMem->z, 0, n); + } +#endif } /* ** Delete any previous value and set the value stored in *pMem to val, ** manifest type INTEGER. @@ -864,10 +882,12 @@ Mem *pMem /* OUT: Return data in this Mem structure. */ ){ char *zData; /* Data from the btree layer */ int available = 0; /* Number of bytes available on the local btree page */ int rc = SQLITE_OK; /* Return code */ + + assert( sqlite3BtreeCursorIsValid(pCur) ); /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() ** that both the BtShared and database handle mutexes are held. */ assert( (pMem->flags & MEM_RowSet)==0 ); if( key ){ @@ -984,10 +1004,13 @@ if( !pExpr ){ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; + if( op==TK_REGISTER ){ + op = pExpr->op2; /* This only happens with SQLITE_ENABLE_STAT2 */ + } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = sqlite3ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ @@ -994,10 +1017,11 @@ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue); }else{ zVal = sqlite3DbStrDup(db, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); + if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; } if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); }else{ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); @@ -1025,10 +1049,13 @@ sqlite3VdbeMemSetStr(pVal, sqlite3HexToBlob(db, zVal, nVal), nVal/2, 0, SQLITE_DYNAMIC); } #endif + if( pVal ){ + sqlite3VdbeMemStoreType(pVal); + } *ppVal = pVal; return SQLITE_OK; no_mem: db->mallocFailed = 1; Index: src/vtab.c ================================================================== --- src/vtab.c +++ src/vtab.c @@ -8,12 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to help implement virtual tables. -** -** $Id: vtab.c,v 1.91 2009/06/15 16:27:08 shane Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" /* @@ -25,11 +23,11 @@ sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ void *pAux, /* Context pointer for xCreate/xConnect */ void (*xDestroy)(void *) /* Module destructor function */ -) { +){ int rc, nName; Module *pMod; sqlite3_mutex_enter(db->mutex); nName = sqlite3Strlen30(zName); @@ -91,60 +89,157 @@ ** If an unlock is omitted, resources leaks will occur. ** ** If a disconnect is attempted while a virtual table is locked, ** the disconnect is deferred until all locks have been removed. */ -void sqlite3VtabLock(sqlite3_vtab *pVtab){ - pVtab->nRef++; +void sqlite3VtabLock(VTable *pVTab){ + pVTab->nRef++; +} + + +/* +** pTab is a pointer to a Table structure representing a virtual-table. +** Return a pointer to the VTable object used by connection db to access +** this virtual-table, if one has been created, or NULL otherwise. +*/ +VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){ + VTable *pVtab; + assert( IsVirtual(pTab) ); + for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); + return pVtab; } /* -** Unlock a virtual table. When the last lock is removed, -** disconnect the virtual table. +** Decrement the ref-count on a virtual table object. When the ref-count +** reaches zero, call the xDisconnect() method to delete the object. */ -void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){ -#ifndef SQLITE_DEBUG - UNUSED_PARAMETER(db); -#endif - assert( pVtab->nRef>0 ); - pVtab->nRef--; - assert(db); +void sqlite3VtabUnlock(VTable *pVTab){ + sqlite3 *db = pVTab->db; + + assert( db ); + assert( pVTab->nRef>0 ); assert( sqlite3SafetyCheckOk(db) ); - if( pVtab->nRef==0 ){ + + pVTab->nRef--; + if( pVTab->nRef==0 ){ + sqlite3_vtab *p = pVTab->pVtab; + if( p ){ #ifdef SQLITE_DEBUG - if( db->magic==SQLITE_MAGIC_BUSY ){ - (void)sqlite3SafetyOff(db); - pVtab->pModule->xDisconnect(pVtab); - (void)sqlite3SafetyOn(db); - } else + if( pVTab->db->magic==SQLITE_MAGIC_BUSY ){ + (void)sqlite3SafetyOff(db); + p->pModule->xDisconnect(p); + (void)sqlite3SafetyOn(db); + } else #endif - { - pVtab->pModule->xDisconnect(pVtab); + { + p->pModule->xDisconnect(p); + } + } + sqlite3DbFree(db, pVTab); + } +} + +/* +** Table p is a virtual table. This function moves all elements in the +** p->pVTable list to the sqlite3.pDisconnect lists of their associated +** database connections to be disconnected at the next opportunity. +** Except, if argument db is not NULL, then the entry associated with +** connection db is left in the p->pVTable list. +*/ +static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ + VTable *pRet = 0; + VTable *pVTable = p->pVTable; + p->pVTable = 0; + + /* Assert that the mutex (if any) associated with the BtShared database + ** that contains table p is held by the caller. See header comments + ** above function sqlite3VtabUnlockList() for an explanation of why + ** this makes it safe to access the sqlite3.pDisconnect list of any + ** database connection that may have an entry in the p->pVTable list. */ + assert( db==0 || + sqlite3BtreeHoldsMutex(db->aDb[sqlite3SchemaToIndex(db, p->pSchema)].pBt) + ); + + while( pVTable ){ + sqlite3 *db2 = pVTable->db; + VTable *pNext = pVTable->pNext; + assert( db2 ); + if( db2==db ){ + pRet = pVTable; + p->pVTable = pRet; + pRet->pNext = 0; + }else{ + pVTable->pNext = db2->pDisconnect; + db2->pDisconnect = pVTable; } + pVTable = pNext; + } + + assert( !db || pRet ); + return pRet; +} + + +/* +** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. +** +** This function may only be called when the mutexes associated with all +** shared b-tree databases opened using connection db are held by the +** caller. This is done to protect the sqlite3.pDisconnect list. The +** sqlite3.pDisconnect list is accessed only as follows: +** +** 1) By this function. In this case, all BtShared mutexes and the mutex +** associated with the database handle itself must be held. +** +** 2) By function vtabDisconnectAll(), when it adds a VTable entry to +** the sqlite3.pDisconnect list. In this case either the BtShared mutex +** associated with the database the virtual table is stored in is held +** or, if the virtual table is stored in a non-sharable database, then +** the database handle mutex is held. +** +** As a result, a sqlite3.pDisconnect cannot be accessed simultaneously +** by multiple threads. It is thread-safe. +*/ +void sqlite3VtabUnlockList(sqlite3 *db){ + VTable *p = db->pDisconnect; + db->pDisconnect = 0; + + assert( sqlite3BtreeHoldsAllMutexes(db) ); + assert( sqlite3_mutex_held(db->mutex) ); + + if( p ){ + sqlite3ExpirePreparedStatements(db); + do { + VTable *pNext = p->pNext; + sqlite3VtabUnlock(p); + p = pNext; + }while( p ); } } /* ** Clear any and all virtual-table information from the Table record. ** This routine is called, for example, just before deleting the Table ** record. +** +** Since it is a virtual-table, the Table structure contains a pointer +** to the head of a linked list of VTable structures. Each VTable +** structure is associated with a single sqlite3* user of the schema. +** The reference count of the VTable structure associated with database +** connection db is decremented immediately (which may lead to the +** structure being xDisconnected and free). Any other VTable structures +** in the list are moved to the sqlite3.pDisconnect list of the associated +** database connection. */ void sqlite3VtabClear(Table *p){ - sqlite3_vtab *pVtab = p->pVtab; - Schema *pSchema = p->pSchema; - sqlite3 *db = pSchema ? pSchema->db : 0; - if( pVtab ){ - assert( p->pMod && p->pMod->pModule ); - sqlite3VtabUnlock(db, pVtab); - p->pVtab = 0; - } + vtabDisconnectAll(0, p); if( p->azModuleArg ){ int i; for(i=0; inModuleArg; i++){ - sqlite3DbFree(db, p->azModuleArg[i]); + sqlite3DbFree(p->dbMem, p->azModuleArg[i]); } - sqlite3DbFree(db, p->azModuleArg); + sqlite3DbFree(p->dbMem, p->azModuleArg); } } /* ** Add a new module argument to pTable->azModuleArg[]. @@ -185,15 +280,10 @@ ){ int iDb; /* The database the table is being created in */ Table *pTable; /* The new virtual table */ sqlite3 *db; /* Database connection */ - if( pParse->db->flags & SQLITE_SharedCache ){ - sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode"); - return; - } - sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0); pTable = pParse->pNewTable; if( pTable==0 ) return; assert( 0==pTable->pIndex ); @@ -238,27 +328,17 @@ /* ** The parser calls this routine after the CREATE VIRTUAL TABLE statement ** has been completely parsed. */ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ - Table *pTab; /* The table being constructed */ - sqlite3 *db; /* The database connection */ - char *zModule; /* The module name of the table: USING modulename */ - Module *pMod = 0; + Table *pTab = pParse->pNewTable; /* The table being constructed */ + sqlite3 *db = pParse->db; /* The database connection */ + if( pTab==0 ) return; addArgumentToVtab(pParse); pParse->sArg.z = 0; - - /* Lookup the module name. */ - pTab = pParse->pNewTable; - if( pTab==0 ) return; - db = pParse->db; if( pTab->nModuleArg<1 ) return; - zModule = pTab->azModuleArg[0]; - pMod = (Module*)sqlite3HashFind(&db->aModule, zModule, - sqlite3Strlen30(zModule)); - pTab->pMod = pMod; /* If the CREATE VIRTUAL TABLE statement is being entered for the ** first time (in other words if the virtual table is actually being ** created now instead of just being read out of sqlite_master) then ** do additional initialization work and store the statement text @@ -305,13 +385,14 @@ sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, pTab->zName, sqlite3Strlen30(pTab->zName) + 1); } /* If we are rereading the sqlite_master table create the in-memory - ** record of the table. If the module has already been registered, - ** also call the xConnect method here. - */ + ** record of the table. The xConnect() method is not called until + ** the first time the virtual table is used in an SQL statement. This + ** allows a schema that contains virtual tables to be loaded before + ** the required virtual table implementations are registered. */ else { Table *pOld; Schema *pSchema = pTab->pSchema; const char *zName = pTab->zName; int nName = sqlite3Strlen30(zName); @@ -361,94 +442,101 @@ Table *pTab, Module *pMod, int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), char **pzErr ){ + VTable *pVTable; int rc; - int rc2; - sqlite3_vtab *pVtab = 0; const char *const*azArg = (const char *const*)pTab->azModuleArg; int nArg = pTab->nModuleArg; char *zErr = 0; char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); if( !zModuleName ){ return SQLITE_NOMEM; } + pVTable = sqlite3DbMallocZero(db, sizeof(VTable)); + if( !pVTable ){ + sqlite3DbFree(db, zModuleName); + return SQLITE_NOMEM; + } + pVTable->db = db; + pVTable->pMod = pMod; + assert( !db->pVTab ); assert( xConstruct ); - db->pVTab = pTab; - rc = sqlite3SafetyOff(db); - assert( rc==SQLITE_OK ); - rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVtab, &zErr); - rc2 = sqlite3SafetyOn(db); + + /* Invoke the virtual table constructor */ + (void)sqlite3SafetyOff(db); + rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + (void)sqlite3SafetyOn(db); if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - /* Justification of ALWAYS(): A correct vtab constructor must allocate - ** the sqlite3_vtab object if successful. */ - if( rc==SQLITE_OK && ALWAYS(pVtab) ){ - pVtab->pModule = pMod->pModule; - pVtab->nRef = 1; - pTab->pVtab = pVtab; - } if( SQLITE_OK!=rc ){ if( zErr==0 ){ *pzErr = sqlite3MPrintf(db, "vtable constructor failed: %s", zModuleName); }else { *pzErr = sqlite3MPrintf(db, "%s", zErr); sqlite3DbFree(db, zErr); } - }else if( db->pVTab ){ - const char *zFormat = "vtable constructor did not declare schema: %s"; - *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); - rc = SQLITE_ERROR; - } - if( rc==SQLITE_OK ){ - rc = rc2; - } - db->pVTab = 0; - sqlite3DbFree(db, zModuleName); - - /* If everything went according to plan, loop through the columns - ** of the table to see if any of them contain the token "hidden". - ** If so, set the Column.isHidden flag and remove the token from - ** the type string. - */ - if( rc==SQLITE_OK ){ - int iCol; - for(iCol=0; iColnCol; iCol++){ - char *zType = pTab->aCol[iCol].zType; - int nType; - int i = 0; - if( !zType ) continue; - nType = sqlite3Strlen30(zType); - if( sqlite3StrNICmp("hidden", zType, 6) || (zType[6] && zType[6]!=' ') ){ - for(i=0; i0 ){ - assert(zType[i-1]==' '); - zType[i-1] = '\0'; - } - pTab->aCol[iCol].isHidden = 1; - } - } - } + sqlite3DbFree(db, pVTable); + }else if( ALWAYS(pVTable->pVtab) ){ + /* Justification of ALWAYS(): A correct vtab constructor must allocate + ** the sqlite3_vtab object if successful. */ + pVTable->pVtab->pModule = pMod->pModule; + pVTable->nRef = 1; + if( db->pVTab ){ + const char *zFormat = "vtable constructor did not declare schema: %s"; + *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); + sqlite3VtabUnlock(pVTable); + rc = SQLITE_ERROR; + }else{ + int iCol; + /* If everything went according to plan, link the new VTable structure + ** into the linked list headed by pTab->pVTable. Then loop through the + ** columns of the table to see if any of them contain the token "hidden". + ** If so, set the Column.isHidden flag and remove the token from + ** the type string. */ + pVTable->pNext = pTab->pVTable; + pTab->pVTable = pVTable; + + for(iCol=0; iColnCol; iCol++){ + char *zType = pTab->aCol[iCol].zType; + int nType; + int i = 0; + if( !zType ) continue; + nType = sqlite3Strlen30(zType); + if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){ + for(i=0; i0 ){ + assert(zType[i-1]==' '); + zType[i-1] = '\0'; + } + pTab->aCol[iCol].isHidden = 1; + } + } + } + } + + sqlite3DbFree(db, zModuleName); + db->pVTab = 0; return rc; } /* ** This function is invoked by the parser to call the xConnect() method @@ -456,26 +544,30 @@ ** and an error left in pParse. ** ** This call is a no-op if table pTab is not a virtual table. */ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ + sqlite3 *db = pParse->db; + const char *zMod; Module *pMod; - int rc = SQLITE_OK; + int rc; assert( pTab ); - if( (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){ + if( (pTab->tabFlags & TF_Virtual)==0 || sqlite3GetVTable(db, pTab) ){ return SQLITE_OK; } - pMod = pTab->pMod; + /* Locate the required virtual table module */ + zMod = pTab->azModuleArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); + if( !pMod ){ const char *zModule = pTab->azModuleArg[0]; sqlite3ErrorMsg(pParse, "no such module: %s", zModule); rc = SQLITE_ERROR; - } else { + }else{ char *zErr = 0; - sqlite3 *db = pParse->db; rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr); if( rc!=SQLITE_OK ){ sqlite3ErrorMsg(pParse, "%s", zErr); } sqlite3DbFree(db, zErr); @@ -483,18 +575,18 @@ return rc; } /* -** Add the virtual table pVtab to the array sqlite3.aVTrans[]. +** Add the virtual table pVTab to the array sqlite3.aVTrans[]. */ -static int addToVTrans(sqlite3 *db, sqlite3_vtab *pVtab){ +static int addToVTrans(sqlite3 *db, VTable *pVTab){ const int ARRAY_INCR = 5; /* Grow the sqlite3.aVTrans array if required */ if( (db->nVTrans%ARRAY_INCR)==0 ){ - sqlite3_vtab **aVTrans; + VTable **aVTrans; int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); if( !aVTrans ){ return SQLITE_NOMEM; } @@ -501,12 +593,12 @@ memset(&aVTrans[db->nVTrans], 0, sizeof(sqlite3_vtab *)*ARRAY_INCR); db->aVTrans = aVTrans; } /* Add pVtab to the end of sqlite3.aVTrans */ - db->aVTrans[db->nVTrans++] = pVtab; - sqlite3VtabLock(pVtab); + db->aVTrans[db->nVTrans++] = pVTab; + sqlite3VtabLock(pVTab); return SQLITE_OK; } /* ** This function is invoked by the vdbe to call the xCreate method @@ -518,32 +610,34 @@ */ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ int rc = SQLITE_OK; Table *pTab; Module *pMod; - const char *zModule; + const char *zMod; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - assert(pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVtab); - pMod = pTab->pMod; - zModule = pTab->azModuleArg[0]; + assert( pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVTable ); + + /* Locate the required virtual table module */ + zMod = pTab->azModuleArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); /* If the module has been registered and includes a Create method, ** invoke it now. If the module has not been registered, return an ** error. Otherwise, do nothing. */ if( !pMod ){ - *pzErr = sqlite3MPrintf(db, "no such module: %s", zModule); + *pzErr = sqlite3MPrintf(db, "no such module: %s", zMod); rc = SQLITE_ERROR; }else{ rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr); } /* Justification of ALWAYS(): The xConstructor method is required to ** create a valid sqlite3_vtab if it returns SQLITE_OK. */ - if( rc==SQLITE_OK && ALWAYS(pTab->pVtab) ){ - rc = addToVTrans(db, pTab->pVtab); + if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){ + rc = addToVTrans(db, sqlite3GetVTable(db, pTab)); } return rc; } @@ -564,11 +658,11 @@ if( !pTab ){ sqlite3Error(db, SQLITE_MISUSE, 0); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE; } - assert((pTab->tabFlags & TF_Virtual)!=0 && pTab->nCol==0 && pTab->aCol==0); + assert( (pTab->tabFlags & TF_Virtual)!=0 ); pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ rc = SQLITE_NOMEM; }else{ @@ -579,14 +673,16 @@ SQLITE_OK == sqlite3RunParser(pParse, zCreateTable, &zErr) && pParse->pNewTable && !pParse->pNewTable->pSelect && (pParse->pNewTable->tabFlags & TF_Virtual)==0 ){ - pTab->aCol = pParse->pNewTable->aCol; - pTab->nCol = pParse->pNewTable->nCol; - pParse->pNewTable->nCol = 0; - pParse->pNewTable->aCol = 0; + if( !pTab->aCol ){ + pTab->aCol = pParse->pNewTable->aCol; + pTab->nCol = pParse->pNewTable->nCol; + pParse->pNewTable->nCol = 0; + pParse->pNewTable->aCol = 0; + } db->pVTab = 0; } else { sqlite3Error(db, SQLITE_ERROR, zErr); sqlite3DbFree(db, zErr); rc = SQLITE_ERROR; @@ -616,25 +712,24 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ int rc = SQLITE_OK; Table *pTab; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - if( ALWAYS(pTab!=0 && pTab->pVtab!=0) ){ - int (*xDestroy)(sqlite3_vtab *pVTab) = pTab->pMod->pModule->xDestroy; + if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ + VTable *p = vtabDisconnectAll(db, pTab); + rc = sqlite3SafetyOff(db); assert( rc==SQLITE_OK ); - rc = xDestroy(pTab->pVtab); + rc = p->pMod->pModule->xDestroy(p->pVtab); (void)sqlite3SafetyOn(db); + + /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ - int i; - for(i=0; inVTrans; i++){ - if( db->aVTrans[i]==pTab->pVtab ){ - db->aVTrans[i] = db->aVTrans[--db->nVTrans]; - break; - } - } - pTab->pVtab = 0; + assert( pTab->pVTable==p && p->pNext==0 ); + p->pVtab = 0; + pTab->pVTable = 0; + sqlite3VtabUnlock(p); } } return rc; } @@ -649,17 +744,18 @@ */ static void callFinaliser(sqlite3 *db, int offset){ int i; if( db->aVTrans ){ for(i=0; inVTrans; i++){ - sqlite3_vtab *pVtab = db->aVTrans[i]; - int (*x)(sqlite3_vtab *); - - assert( pVtab!=0 ); - x = *(int (**)(sqlite3_vtab *))((char *)pVtab->pModule + offset); - if( x ) x(pVtab); - sqlite3VtabUnlock(db, pVtab); + VTable *pVTab = db->aVTrans[i]; + sqlite3_vtab *p = pVTab->pVtab; + if( p ){ + int (*x)(sqlite3_vtab *); + x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset); + if( x ) x(p); + } + sqlite3VtabUnlock(pVTab); } sqlite3DbFree(db, db->aVTrans); db->nVTrans = 0; db->aVTrans = 0; } @@ -675,20 +771,18 @@ */ int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ int i; int rc = SQLITE_OK; int rcsafety; - sqlite3_vtab **aVTrans = db->aVTrans; + VTable **aVTrans = db->aVTrans; rc = sqlite3SafetyOff(db); db->aVTrans = 0; for(i=0; rc==SQLITE_OK && inVTrans; i++){ - sqlite3_vtab *pVtab = aVTrans[i]; int (*x)(sqlite3_vtab *); - assert( pVtab!=0 ); - x = pVtab->pModule->xSync; - if( x ){ + sqlite3_vtab *pVtab = aVTrans[i]->pVtab; + if( pVtab && (x = pVtab->pModule->xSync)!=0 ){ rc = x(pVtab); sqlite3DbFree(db, *pzErrmsg); *pzErrmsg = pVtab->zErrMsg; pVtab->zErrMsg = 0; } @@ -726,11 +820,11 @@ ** not currently open, invoke the xBegin method now. ** ** If the xBegin call is successful, place the sqlite3_vtab pointer ** in the sqlite3.aVTrans array. */ -int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ +int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ int rc = SQLITE_OK; const sqlite3_module *pModule; /* Special case: If db->aVTrans is NULL and db->nVTrans is greater ** than zero, then this function is being called from within a @@ -738,30 +832,30 @@ ** virtual module tables in this case, so return SQLITE_LOCKED. */ if( sqlite3VtabInSync(db) ){ return SQLITE_LOCKED; } - if( !pVtab ){ + if( !pVTab ){ return SQLITE_OK; } - pModule = pVtab->pModule; + pModule = pVTab->pVtab->pModule; if( pModule->xBegin ){ int i; /* If pVtab is already in the aVTrans array, return early */ for(i=0; inVTrans; i++){ - if( db->aVTrans[i]==pVtab ){ + if( db->aVTrans[i]==pVTab ){ return SQLITE_OK; } } /* Invoke the xBegin method */ - rc = pModule->xBegin(pVtab); + rc = pModule->xBegin(pVTab->pVtab); if( rc==SQLITE_OK ){ - rc = addToVTrans(db, pVtab); + rc = addToVTrans(db, pVTab); } } return rc; } @@ -799,11 +893,11 @@ if( NEVER(pExpr==0) ) return pDef; if( pExpr->op!=TK_COLUMN ) return pDef; pTab = pExpr->pTab; if( NEVER(pTab==0) ) return pDef; if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef; - pVtab = pTab->pVtab; + pVtab = sqlite3GetVTable(db, pTab)->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); pMod = (sqlite3_module *)pVtab->pModule; if( pMod->xFindFunction==0 ) return pDef; @@ -823,11 +917,11 @@ } /* Create a new ephemeral function definition for the overloaded ** function */ pNew = sqlite3DbMallocZero(db, sizeof(*pNew) - + sqlite3Strlen30(pDef->zName) ); + + sqlite3Strlen30(pDef->zName) + 1); if( pNew==0 ){ return pDef; } *pNew = *pDef; pNew->zName = (char *)&pNew[1]; @@ -843,23 +937,24 @@ ** array so that an OP_VBegin will get generated for it. Add pTab to the ** array if it is missing. If pTab is already in the array, this routine ** is a no-op. */ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); int i, n; Table **apVtabLock; assert( IsVirtual(pTab) ); - for(i=0; inVtabLock; i++){ - if( pTab==pParse->apVtabLock[i] ) return; + for(i=0; inVtabLock; i++){ + if( pTab==pToplevel->apVtabLock[i] ) return; } - n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]); - apVtabLock = sqlite3_realloc(pParse->apVtabLock, n); + n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); + apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n); if( apVtabLock ){ - pParse->apVtabLock = apVtabLock; - pParse->apVtabLock[pParse->nVtabLock++] = pTab; + pToplevel->apVtabLock = apVtabLock; + pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; }else{ - pParse->db->mallocFailed = 1; + pToplevel->db->mallocFailed = 1; } } #endif /* SQLITE_OMIT_VIRTUALTABLE */ Index: src/walker.c ================================================================== --- src/walker.c +++ src/walker.c @@ -9,12 +9,10 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains routines used for walking the parser tree for ** an SQL statement. -** -** $Id: walker.c,v 1.7 2009/06/15 23:15:59 drh Exp $ */ #include "sqliteInt.h" #include #include Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -13,12 +13,10 @@ ** the WHERE clause of SQL statements. This module is responsible for ** generating the code that loops through a table looking for applicable ** rows. Indices are selected and used to speed the search when doing ** 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.408 2009/06/16 14:15:22 shane Exp $ */ #include "sqliteInt.h" /* ** Trace output macros @@ -193,10 +191,11 @@ */ struct WhereCost { WherePlan plan; /* The lookup strategy */ double rCost; /* Overall cost of pursuing this search strategy */ double nRow; /* Estimated number of output rows */ + Bitmask used; /* Bitmask of cursors used by this plan */ }; /* ** Bitmasks for the operators that indices are able to exploit. An ** OR-ed combination of these values can be used when searching for @@ -622,59 +621,103 @@ ** literal that does not begin with a wildcard. */ static int isLikeOrGlob( Parse *pParse, /* Parsing and code generating context */ Expr *pExpr, /* Test this expression */ - int *pnPattern, /* Number of non-wildcard prefix characters */ + Expr **ppPrefix, /* Pointer to TK_STRING expression with pattern prefix */ int *pisComplete, /* True if the only wildcard is % in the last character */ int *pnoCase /* True if uppercase is equivalent to lowercase */ ){ - const char *z; /* String on RHS of LIKE operator */ + const char *z = 0; /* String on RHS of LIKE operator */ Expr *pRight, *pLeft; /* Right and left size of LIKE operator */ ExprList *pList; /* List of operands to the LIKE operator */ int c; /* One character in z[] */ int cnt; /* Number of non-wildcard prefix characters */ char wc[3]; /* Wildcard characters */ CollSeq *pColl; /* Collating sequence for LHS */ sqlite3 *db = pParse->db; /* Database connection */ + sqlite3_value *pVal = 0; + int op; /* Opcode of pRight */ if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){ return 0; } #ifdef SQLITE_EBCDIC if( *pnoCase ) return 0; #endif pList = pExpr->x.pList; - pRight = pList->a[0].pExpr; - if( pRight->op!=TK_STRING ){ - return 0; - } pLeft = pList->a[1].pExpr; - if( pLeft->op!=TK_COLUMN ){ + if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT ){ + /* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must + ** be the name of an indexed column with TEXT affinity. */ return 0; } + assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */ pColl = sqlite3ExprCollSeq(pParse, pLeft); - assert( pColl!=0 || pLeft->iColumn==-1 ); - if( pColl==0 ) return 0; + assert( pColl!=0 ); /* Every non-IPK column has a collating sequence */ if( (pColl->type!=SQLITE_COLL_BINARY || *pnoCase) && (pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){ + /* IMP: R-09003-32046 For the GLOB operator, the column must use the + ** default BINARY collating sequence. + ** IMP: R-41408-28306 For the LIKE operator, if case_sensitive_like mode + ** is enabled then the column must use the default BINARY collating + ** sequence, or if case_sensitive_like mode is disabled then the column + ** must use the built-in NOCASE collating sequence. + */ return 0; } - if( sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT ) return 0; - z = pRight->u.zToken; - if( ALWAYS(z) ){ + + pRight = pList->a[0].pExpr; + op = pRight->op; + if( op==TK_REGISTER ){ + op = pRight->op2; + } + if( op==TK_VARIABLE ){ + Vdbe *pReprepare = pParse->pReprepare; + pVal = sqlite3VdbeGetValue(pReprepare, pRight->iColumn, SQLITE_AFF_NONE); + if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ + z = (char *)sqlite3_value_text(pVal); + } + sqlite3VdbeSetVarmask(pParse->pVdbe, pRight->iColumn); + assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); + }else if( op==TK_STRING ){ + z = pRight->u.zToken; + } + if( z ){ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; } if( cnt!=0 && c!=0 && 255!=(u8)z[cnt-1] ){ + Expr *pPrefix; *pisComplete = z[cnt]==wc[0] && z[cnt+1]==0; - *pnPattern = cnt; - return 1; + pPrefix = sqlite3Expr(db, TK_STRING, z); + if( pPrefix ) pPrefix->u.zToken[cnt] = 0; + *ppPrefix = pPrefix; + if( op==TK_VARIABLE ){ + Vdbe *v = pParse->pVdbe; + sqlite3VdbeSetVarmask(v, pRight->iColumn); + if( *pisComplete && pRight->u.zToken[1] ){ + /* If the rhs of the LIKE expression is a variable, and the current + ** value of the variable means there is no need to invoke the LIKE + ** function, then no OP_Variable will be added to the program. + ** This causes problems for the sqlite3_bind_parameter_name() + ** API. To workaround them, add a dummy OP_Variable here. + */ + int r1 = sqlite3GetTempReg(pParse); + sqlite3ExprCodeTarget(pParse, pRight, r1); + sqlite3VdbeChangeP3(v, sqlite3VdbeCurrentAddr(v)-1, 0); + sqlite3ReleaseTempReg(pParse, r1); + } + } + }else{ + z = 0; } } - return 0; + + sqlite3ValueFree(pVal); + return (z!=0); } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -1051,14 +1094,14 @@ WhereTerm *pTerm; /* The term to be analyzed */ WhereMaskSet *pMaskSet; /* Set of table index masks */ Expr *pExpr; /* The expression to be analyzed */ Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ Bitmask prereqAll; /* Prerequesites of pExpr */ - Bitmask extraRight = 0; - int nPattern; - int isComplete; - int noCase; + Bitmask extraRight = 0; /* */ + Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ + int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ + int noCase = 0; /* LIKE/GLOB distinguishes case */ int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWC->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ if( db->mallocFailed ){ @@ -1174,10 +1217,11 @@ ** an OR operator. */ else if( pExpr->op==TK_OR ){ assert( pWC->op==TK_AND ); exprAnalyzeOrTerm(pSrc, pWC, idxTerm); + pTerm = &pWC->a[idxTerm]; } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ #ifndef SQLITE_OMIT_LIKE_OPTIMIZATION /* Add constraints to reduce the search space on a LIKE or GLOB @@ -1188,25 +1232,25 @@ ** x>='abc' AND x<'abd' AND x LIKE 'abc%' ** ** The last character of the prefix "abc" is incremented to form the ** termination condition "abd". */ - if( isLikeOrGlob(pParse, pExpr, &nPattern, &isComplete, &noCase) - && pWC->op==TK_AND ){ - Expr *pLeft, *pRight; - Expr *pStr1, *pStr2; - Expr *pNewExpr1, *pNewExpr2; - int idxNew1, idxNew2; + if( pWC->op==TK_AND + && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) + ){ + Expr *pLeft; /* LHS of LIKE/GLOB operator */ + Expr *pStr2; /* Copy of pStr1 - RHS of LIKE/GLOB operator */ + Expr *pNewExpr1; + Expr *pNewExpr2; + int idxNew1; + int idxNew2; pLeft = pExpr->x.pList->a[1].pExpr; - pRight = pExpr->x.pList->a[0].pExpr; - pStr1 = sqlite3Expr(db, TK_STRING, pRight->u.zToken); - if( pStr1 ) pStr1->u.zToken[nPattern] = 0; pStr2 = sqlite3ExprDup(db, pStr1, 0); if( !db->mallocFailed ){ u8 c, *pC; /* Last character before the first wildcard */ - pC = (u8*)&pStr2->u.zToken[nPattern-1]; + pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; c = *pC; if( noCase ){ /* The point is to increment the last character before the first ** wildcard. But if we increment '@', that will push it into the ** alphabetic range where case conversions will mess up the @@ -1335,10 +1379,15 @@ assert( pOrderBy!=0 ); nTerm = pOrderBy->nExpr; assert( nTerm>0 ); + /* Argument pIdx must either point to a 'real' named index structure, + ** or an index structure allocated on the stack by bestBtreeIndex() to + ** represent the rowid index that is part of every table. */ + assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); + /* Match terms of the ORDER BY clause against columns of ** the index. ** ** Note that indices have pIdx->nColumn regular columns plus ** one additional column containing the rowid. The rowid column @@ -1361,11 +1410,11 @@ } pColl = sqlite3ExprCollSeq(pParse, pExpr); if( !pColl ){ pColl = db->pDfltColl; } - if( inColumn ){ + if( pIdx->zName && inColumn ){ iColumn = pIdx->aiColumn[i]; if( iColumn==pIdx->pTable->iPKey ){ iColumn = -1; } iSortOrder = pIdx->aSortOrder[i]; @@ -1390,11 +1439,11 @@ ** then the index cannot satisfy the ORDER BY constraint. */ return 0; } } - assert( pIdx->aSortOrder!=0 ); + assert( pIdx->aSortOrder!=0 || iColumn==-1 ); assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 ); assert( iSortOrder==0 || iSortOrder==1 ); termSortOrder = iSortOrder ^ pTerm->sortOrder; if( i>nEqCol ){ if( termSortOrder!=sortOrder ){ @@ -1430,34 +1479,10 @@ ** and the index is UNIQUE and no terms on the tail of the ORDER BY ** clause reference other tables in a join. If this is all true then ** the order by clause is superfluous. */ return 1; } - return 0; -} - -/* -** Check table to see if the ORDER BY clause in pOrderBy can be satisfied -** by sorting in order of ROWID. Return true if so and set *pbRev to be -** true for reverse ROWID and false for forward ROWID order. -*/ -static int sortableByRowid( - int base, /* Cursor number for table to be sorted */ - ExprList *pOrderBy, /* The ORDER BY clause */ - WhereMaskSet *pMaskSet, /* Mapping from table cursors to bitmaps */ - int *pbRev /* Set to 1 if ORDER BY is DESC */ -){ - Expr *p; - - assert( pOrderBy!=0 ); - assert( pOrderBy->nExpr>0 ); - p = pOrderBy->a[0].pExpr; - if( p->op==TK_COLUMN && p->iTable==base && p->iColumn==-1 - && !referencesOtherTables(pOrderBy, pMaskSet, 1, base) ){ - *pbRev = pOrderBy->a[0].sortOrder; - return 1; - } return 0; } /* ** Prepare a crude estimate of the logarithm of the input value. @@ -1557,10 +1582,11 @@ WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; WhereTerm *pOrTerm; int flags = WHERE_MULTI_OR; double rTotal = 0; double nRow = 0; + Bitmask used = 0; for(pOrTerm=pOrWC->a; pOrTerma), (pTerm - pWC->a) @@ -1579,10 +1605,11 @@ }else{ continue; } rTotal += sTermCost.rCost; nRow += sTermCost.nRow; + used |= sTermCost.used; if( rTotal>=pCost->rCost ) break; } /* If there is an ORDER BY clause, increase the scan cost to account ** for the cost of the sort. */ @@ -1596,10 +1623,11 @@ ** of pCost. */ WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); if( rTotalrCost ){ pCost->rCost = rTotal; pCost->nRow = nRow; + pCost->used = used; pCost->plan.wsFlags = flags; pCost->plan.u.pTerm = pTerm; } } } @@ -1724,11 +1752,11 @@ ** Whether or not an error is returned, it is the responsibility of the ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates ** that this is required. */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ - sqlite3_vtab *pVtab = pTab->pVtab; + sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int i; int rc; (void)sqlite3SafetyOff(pParse->db); WHERETRACE(("xBestIndex for %s\n", pTab->zName)); @@ -1821,11 +1849,11 @@ /* The module name must be defined. Also, by this point there must ** be a pointer to an sqlite3_vtab structure. Otherwise ** sqlite3ViewGetColumnNames() would have picked up the error. */ assert( pTab->azModuleArg && pTab->azModuleArg[0] ); - assert( pTab->pVtab ); + assert( sqlite3GetVTable(pParse->db, pTab) ); /* Set the aConstraint[].usable fields and initialize all ** output variables to zero. ** ** aConstraint[].usable is true for constraints where the right-hand @@ -1848,11 +1876,11 @@ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; pUsage = pIdxInfo->aConstraintUsage; for(i=0; inConstraint; i++, pIdxCons++){ j = pIdxCons->iTermOffset; pTerm = &pWC->a[j]; - pIdxCons->usable = (pTerm->prereqRight & notReady)==0 ?1:0; + pIdxCons->usable = (pTerm->prereqRight¬Ready) ? 0 : 1; } memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); if( pIdxInfo->needToFreeIdxStr ){ sqlite3_free(pIdxInfo->idxStr); } @@ -1868,10 +1896,17 @@ } if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ return; } + + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; inConstraint; i++){ + if( pUsage[i].argvIndex>0 ){ + pCost->used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight; + } + } /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the ** inital value of lowestCost in this loop. If it is, then the ** (costaSample; + int i = 0; + int eType = sqlite3_value_type(pVal); + + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + double r = sqlite3_value_double(pVal); + for(i=0; i=SQLITE_TEXT || aSample[i].u.r>r ) break; + } + }else{ + sqlite3 *db = pParse->db; + CollSeq *pColl; + const u8 *z; + int n; + + /* pVal comes from sqlite3ValueFromExpr() so the type cannot be NULL */ + assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); + + if( eType==SQLITE_BLOB ){ + z = (const u8 *)sqlite3_value_blob(pVal); + pColl = db->pDfltColl; + assert( pColl->enc==SQLITE_UTF8 ); + }else{ + pColl = sqlite3GetCollSeq(db, SQLITE_UTF8, 0, *pIdx->azColl); + if( pColl==0 ){ + sqlite3ErrorMsg(pParse, "no such collation sequence: %s", + *pIdx->azColl); + return SQLITE_ERROR; + } + z = (const u8 *)sqlite3ValueText(pVal, pColl->enc); + if( !z ){ + return SQLITE_NOMEM; + } + assert( z && pColl && pColl->xCmp ); + } + n = sqlite3ValueBytes(pVal, pColl->enc); + + for(i=0; ienc!=SQLITE_UTF8 ){ + int nSample; + char *zSample = sqlite3Utf8to16( + db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample + ); + if( !zSample ){ + assert( db->mallocFailed ); + return SQLITE_NOMEM; + } + r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); + sqlite3DbFree(db, zSample); + }else +#endif + { + r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); + } + if( r>0 ) break; + } + } + + assert( i>=0 && i<=SQLITE_INDEX_SAMPLES ); + *piRegion = i; + } + return SQLITE_OK; +} +#endif /* #ifdef SQLITE_ENABLE_STAT2 */ + +/* +** If expression pExpr represents a literal value, set *pp to point to +** an sqlite3_value structure containing the same value, with affinity +** aff applied to it, before returning. It is the responsibility of the +** caller to eventually release this structure by passing it to +** sqlite3ValueFree(). +** +** If the current parse is a recompile (sqlite3Reprepare()) and pExpr +** is an SQL variable that currently has a non-NULL value bound to it, +** create an sqlite3_value structure containing this value, again with +** affinity aff applied to it, instead. +** +** If neither of the above apply, set *pp to NULL. +** +** If an error occurs, return an error code. Otherwise, SQLITE_OK. +*/ +#ifdef SQLITE_ENABLE_STAT2 +static int valueFromExpr( + Parse *pParse, + Expr *pExpr, + u8 aff, + sqlite3_value **pp +){ + /* The evalConstExpr() function will have already converted any TK_VARIABLE + ** expression involved in an comparison into a TK_REGISTER. */ + assert( pExpr->op!=TK_VARIABLE ); + if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ + int iVar = pExpr->iColumn; + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); + *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); + return SQLITE_OK; + } + return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); +} +#endif + +/* +** This function is used to estimate the number of rows that will be visited +** by scanning an index for a range of values. The range may have an upper +** bound, a lower bound, or both. The WHERE clause terms that set the upper +** and lower bounds are represented by pLower and pUpper respectively. For +** example, assuming that index p is on t1(a): +** +** ... FROM t1 WHERE a > ? AND a < ? ... +** |_____| |_____| +** | | +** pLower pUpper +** +** If either of the upper or lower bound is not present, then NULL is passed in +** place of the corresponding WhereTerm. +** +** The nEq parameter is passed the index of the index column subject to the +** range constraint. Or, equivalently, the number of equality constraints +** optimized by the proposed index scan. For example, assuming index p is +** on t1(a, b), and the SQL query is: +** +** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ... +** +** then nEq should be passed the value 1 (as the range restricted column, +** b, is the second left-most column of the index). Or, if the query is: +** +** ... FROM t1 WHERE a > ? AND a < ? ... +** +** then nEq should be passed 0. +** +** The returned value is an integer between 1 and 100, inclusive. A return +** value of 1 indicates that the proposed range scan is expected to visit +** approximately 1/100th (1%) of the rows selected by the nEq equality +** constraints (if any). A return value of 100 indicates that it is expected +** that the range scan will visit every row (100%) selected by the equality +** constraints. +** +** In the absence of sqlite_stat2 ANALYZE data, each range inequality +** reduces the search space by 2/3rds. Hence a single constraint (x>?) +** results in a return of 33 and a range constraint (x>? AND xaCol[] of the range-compared column */ + WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ + WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ + int *piEst /* OUT: Return value */ +){ + int rc = SQLITE_OK; + +#ifdef SQLITE_ENABLE_STAT2 + + if( nEq==0 && p->aSample ){ + sqlite3_value *pLowerVal = 0; + sqlite3_value *pUpperVal = 0; + int iEst; + int iLower = 0; + int iUpper = SQLITE_INDEX_SAMPLES; + u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; + + if( pLower ){ + Expr *pExpr = pLower->pExpr->pRight; + rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); + } + if( rc==SQLITE_OK && pUpper ){ + Expr *pExpr = pUpper->pExpr->pRight; + rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); + } + + if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ + sqlite3ValueFree(pLowerVal); + sqlite3ValueFree(pUpperVal); + goto range_est_fallback; + }else if( pLowerVal==0 ){ + rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + if( pLower ) iLower = iUpper/2; + }else if( pUpperVal==0 ){ + rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; + }else{ + rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + if( rc==SQLITE_OK ){ + rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + } + } + + iEst = iUpper - iLower; + testcase( iEst==SQLITE_INDEX_SAMPLES ); + assert( iEst<=SQLITE_INDEX_SAMPLES ); + if( iEst<1 ){ + iEst = 1; + } + + sqlite3ValueFree(pLowerVal); + sqlite3ValueFree(pUpperVal); + *piEst = (iEst * 100)/SQLITE_INDEX_SAMPLES; + return rc; + } +range_est_fallback: +#else + UNUSED_PARAMETER(pParse); + UNUSED_PARAMETER(p); + UNUSED_PARAMETER(nEq); +#endif + assert( pLower || pUpper ); + if( pLower && pUpper ){ + *piEst = 11; + }else{ + *piEst = 33; + } + return rc; +} + /* ** Find the query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. @@ -1931,294 +2211,318 @@ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors that are not available */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ - WhereTerm *pTerm; /* A single term of the WHERE clause */ int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ Index *pProbe; /* An index we are evaluating */ - int rev; /* True to scan in reverse order */ - int wsFlags; /* Flags associated with pProbe */ - int nEq; /* Number of == or IN constraints */ - int eqTermMask; /* Mask of valid equality operators */ - double cost; /* Cost of using pProbe */ - double nRow; /* Estimated number of rows in result set */ - int i; /* Loop counter */ - - WHERETRACE(("bestIndex: tbl=%s notReady=%llx\n", pSrc->pTab->zName,notReady)); - pProbe = pSrc->pTab->pIndex; - if( pSrc->notIndexed ){ - pProbe = 0; - } - - /* If the table has no indices and there are no terms in the where - ** clause that refer to the ROWID, then we will never be able to do - ** anything other than a full table scan on this table. We might as - ** well put it first in the join order. That way, perhaps it can be - ** referenced by other tables in the join. - */ + Index *pIdx; /* Copy of pProbe, or zero for IPK index */ + int eqTermMask; /* Current mask of valid equality operators */ + int idxEqTermMask; /* Index mask of valid equality operators */ + Index sPk; /* A fake index object for the primary key */ + unsigned int aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ + int aiColumnPk = -1; /* The aColumn[] value for the sPk index */ + int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */ + + /* Initialize the cost to a worst-case value */ memset(pCost, 0, sizeof(*pCost)); - 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; - /* Check for a rowid=EXPR or rowid IN (...) constraints. If there was - ** an INDEXED BY clause attached to this table, skip this step. - */ - if( !pSrc->pIndex ){ - pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); - if( pTerm ){ - Expr *pExpr; - pCost->plan.wsFlags = WHERE_ROWID_EQ; - if( pTerm->eOperator & WO_EQ ){ - /* Rowid== is always the best pick. Look no further. Because only - ** a single row is generated, output is always in sorted order */ - pCost->plan.wsFlags = WHERE_ROWID_EQ | WHERE_UNIQUE; - pCost->plan.nEq = 1; - WHERETRACE(("... best is rowid\n")); - pCost->rCost = 0; - pCost->nRow = 1; - return; - }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->x.pList->nExpr; - pCost->rCost *= estLog(pCost->rCost); - }else{ - /* Rowid IN (SELECT): cost is NlogN where N is the number of rows - ** in the result of the inner select. We have no way to estimate - ** that value so make a wild guess. */ - pCost->nRow = 100; - pCost->rCost = 200; - } - WHERETRACE(("... rowid IN cost: %.9g\n", pCost->rCost)); - } - - /* Estimate the cost of a table scan. If we do not know how many - ** entries are in the table, use 1 million as a guess. - */ - cost = pProbe ? pProbe->aiRowEst[0] : 1000000; - WHERETRACE(("... table scan base cost: %.9g\n", cost)); - wsFlags = WHERE_ROWID_RANGE; - - /* Check for constraints on a range of rowids in a table scan. - */ - pTerm = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE|WO_GT|WO_GE, 0); - if( pTerm ){ - if( findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0) ){ - wsFlags |= WHERE_TOP_LIMIT; - cost /= 3; /* Guess that rowidEXPR eliminates two-thirds of rows */ - } - WHERETRACE(("... rowid range reduces cost to %.9g\n", cost)); - }else{ - wsFlags = 0; - } - nRow = cost; - - /* If the table scan does not satisfy the ORDER BY clause, increase - ** the cost by NlogN to cover the expense of sorting. */ - if( pOrderBy ){ - if( sortableByRowid(iCur, pOrderBy, pWC->pMaskSet, &rev) ){ - wsFlags |= WHERE_ORDERBY|WHERE_ROWID_RANGE; - if( rev ){ - wsFlags |= WHERE_REVERSE; - } - }else{ - 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( costrCost ){ - pCost->rCost = cost; - pCost->nRow = nRow; - pCost->plan.wsFlags = wsFlags; - } - } - - bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); - /* If the pSrc table is the right table of a LEFT JOIN then we may not ** use an index to satisfy IS NULL constraints on that table. This is ** because columns might end up being NULL if the table does not match - ** a circumstance which the index cannot help us discover. Ticket #2177. */ - if( (pSrc->jointype & JT_LEFT)!=0 ){ - eqTermMask = WO_EQ|WO_IN; + if( pSrc->jointype & JT_LEFT ){ + idxEqTermMask = WO_EQ|WO_IN; }else{ - eqTermMask = WO_EQ|WO_IN|WO_ISNULL; + idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL; } - /* Look at each index. - */ if( pSrc->pIndex ){ - pProbe = pSrc->pIndex; - } - for(; pProbe; pProbe=(pSrc->pIndex ? 0 : pProbe->pNext)){ - 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 or x IS NULL constraints or x IN (...) constraints. - ** For a term of the form x=EXPR or x IS NULL 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; inColumn; i++){ - int j = pProbe->aiColumn[i]; - pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pProbe); + /* An INDEXED BY clause specifies a particular index to use */ + pIdx = pProbe = pSrc->pIndex; + wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); + eqTermMask = idxEqTermMask; + }else{ + /* There is no INDEXED BY clause. Create a fake Index object to + ** represent the primary key */ + Index *pFirst; /* Any other index on the table */ + memset(&sPk, 0, sizeof(Index)); + sPk.nColumn = 1; + sPk.aiColumn = &aiColumnPk; + sPk.aiRowEst = aiRowEstPk; + aiRowEstPk[1] = 1; + sPk.onError = OE_Replace; + sPk.pTable = pSrc->pTab; + pFirst = pSrc->pTab->pIndex; + if( pSrc->notIndexed==0 ){ + sPk.pNext = pFirst; + } + /* The aiRowEstPk[0] is an estimate of the total number of rows in the + ** table. Get this information from the ANALYZE information if it is + ** available. If not available, assume the table 1 million rows in size. + */ + if( pFirst ){ + assert( pFirst->aiRowEst!=0 ); /* Allocated together with pFirst */ + aiRowEstPk[0] = pFirst->aiRowEst[0]; + }else{ + aiRowEstPk[0] = 1000000; + } + pProbe = &sPk; + wsFlagMask = ~( + WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE + ); + eqTermMask = WO_EQ|WO_IN; + pIdx = 0; + } + + /* Loop over all indices looking for the best one to use + */ + for(; pProbe; pIdx=pProbe=pProbe->pNext){ + const unsigned int * const aiRowEst = pProbe->aiRowEst; + double cost; /* Cost of using pProbe */ + double nRow; /* Estimated number of rows in result set */ + int rev; /* True to scan in reverse order */ + int wsFlags = 0; + Bitmask used = 0; + + /* The following variables are populated based on the properties of + ** scan being evaluated. They are then used to determine the expected + ** cost and number of rows returned. + ** + ** nEq: + ** Number of equality terms that can be implemented using the index. + ** + ** nInMul: + ** The "in-multiplier". This is an estimate of how many seek operations + ** SQLite must perform on the index in question. For example, if the + ** WHERE clause is: + ** + ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6) + ** + ** SQLite must perform 9 lookups on an index on (a, b), so nInMul is + ** set to 9. Given the same schema and either of the following WHERE + ** clauses: + ** + ** WHERE a = 1 + ** WHERE a >= 2 + ** + ** nInMul is set to 1. + ** + ** If there exists a WHERE term of the form "x IN (SELECT ...)", then + ** the sub-select is assumed to return 25 rows for the purposes of + ** determining nInMul. + ** + ** bInEst: + ** Set to true if there was at least one "x IN (SELECT ...)" term used + ** in determining the value of nInMul. + ** + ** nBound: + ** An estimate on the amount of the table that must be searched. A + ** value of 100 means the entire table is searched. Range constraints + ** might reduce this to a value less than 100 to indicate that only + ** a fraction of the table needs searching. In the absence of + ** sqlite_stat2 ANALYZE data, a single inequality reduces the search + ** space to 1/3rd its original size. So an x>? constraint reduces + ** nBound to 33. Two constraints (x>? AND xnColumn; nEq++){ + WhereTerm *pTerm; /* A single term of the WHERE clause */ + int j = pProbe->aiColumn[nEq]; + pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); if( pTerm==0 ) break; - wsFlags |= WHERE_COLUMN_EQ; + wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - inMultiplier *= 25; - inMultIsEst = 1; + nInMul *= 25; + bInEst = 1; }else if( pExpr->x.pList ){ - inMultiplier *= pExpr->x.pList->nExpr + 1; + nInMul *= pExpr->x.pList->nExpr + 1; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } - } - nRow = pProbe->aiRowEst[i] * 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 && nEq==pProbe->nColumn ){ + used |= pTerm->prereqRight; + } + + /* Determine the value of nBound. */ + if( nEqnColumn ){ + int j = pProbe->aiColumn[nEq]; + if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ + WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); + WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx); + whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &nBound); + if( pTop ){ + wsFlags |= WHERE_TOP_LIMIT; + used |= pTop->prereqRight; + } + if( pBtm ){ + wsFlags |= WHERE_BTM_LIMIT; + used |= pBtm->prereqRight; + } + wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); + } + }else if( pProbe->onError!=OE_None ){ testcase( wsFlags & WHERE_COLUMN_IN ); testcase( wsFlags & WHERE_COLUMN_NULL ); if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ wsFlags |= WHERE_UNIQUE; } } - WHERETRACE(("...... nEq=%d inMult=%.9g nRow=%.9g cost=%.9g\n", - nEq, inMultiplier, nRow, cost)); - - /* Look for range constraints. Assume that each range constraint - ** makes the search space 1/3rd smaller. - */ - if( nEqnColumn ){ - int j = pProbe->aiColumn[nEq]; - pTerm = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pProbe); - if( pTerm ){ - wsFlags |= WHERE_COLUMN_RANGE; - if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pProbe) ){ - wsFlags |= WHERE_TOP_LIMIT; - cost /= 3; - nRow /= 3; - } - if( findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pProbe) ){ - wsFlags |= WHERE_BTM_LIMIT; - cost /= 3; - nRow /= 3; - } - WHERETRACE(("...... range reduces nRow to %.9g and cost to %.9g\n", - nRow, cost)); - } - } - - /* Add the additional cost of sorting if that is a factor. - */ + + /* If there is an ORDER BY clause and the index being considered will + ** naturally scan rows in the required order, set the appropriate flags + ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index + ** will scan rows in a different order, set the bSort variable. */ if( pOrderBy ){ if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 - && isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) + && isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) ){ - if( wsFlags==0 ){ - wsFlags = WHERE_COLUMN_RANGE; - } - wsFlags |= WHERE_ORDERBY; - if( rev ){ - wsFlags |= WHERE_REVERSE; - } + wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; + wsFlags |= (rev ? WHERE_REVERSE : 0); }else{ - cost += cost*estLog(cost); - WHERETRACE(("...... orderby increases cost to %.9g\n", cost)); - } - }else if( wsFlags!=0 && (pParse->db->flags & SQLITE_ReverseOrder)!=0 ){ - /* 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 - ** ever reading the table. If that is the case, then halve the - ** cost of this index. - */ - if( wsFlags && pSrc->colUsed < (((Bitmask)1)<<(BMS-1)) ){ + bSort = 1; + } + } + + /* If currently calculating the cost of using an index (not the IPK + ** index), determine if all required column data may be obtained without + ** seeking to entries in the main table (i.e. if the index is a covering + ** index for this query). If it is, set the WHERE_IDX_ONLY flag in + ** wsFlags. Otherwise, set the bLookup variable to true. */ + if( pIdx && wsFlags ){ Bitmask m = pSrc->colUsed; int j; - for(j=0; jnColumn; j++){ - int x = pProbe->aiColumn[j]; + for(j=0; jnColumn; j++){ + int x = pIdx->aiColumn[j]; if( xaiRowEst[0] ){ + nRow = aiRowEst[0]/2; + nInMul = (int)(nRow / aiRowEst[nEq]); + } + + /* Assume constant cost to access a row and logarithmic cost to + ** do a binary search. Hence, the initial cost is the number of output + ** rows plus log2(table-size) times the number of binary searches. + */ + cost = nRow + nInMul*estLog(aiRowEst[0]); + + /* Adjust the number of rows and the cost downward to reflect rows + ** that are excluded by range constraints. + */ + nRow = (nRow * (double)nBound) / (double)100; + cost = (cost * (double)nBound) / (double)100; + + /* Add in the estimated cost of sorting the result + */ + if( bSort ){ + cost += cost*estLog(cost); + } + + /* If all information can be taken directly from the index, we avoid + ** doing table lookups. This reduces the cost by half. (Not really - + ** this needs to be fixed.) + */ + if( pIdx && bLookup==0 ){ + cost /= (double)2; + } + /**** Cost of using this index has now been computed ****/ + + WHERETRACE(( + "tbl=%s idx=%s nEq=%d nInMul=%d nBound=%d bSort=%d bLookup=%d" + " wsFlags=%d (nRow=%.2f cost=%.2f)\n", + pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), + nEq, nInMul, nBound, bSort, bLookup, wsFlags, nRow, cost + )); + + /* If this index is the best we have seen so far, then record this + ** index and its cost in the pCost structure. */ - if( wsFlags!=0 && cost < pCost->rCost ){ + if( (!pIdx || wsFlags) && costrCost ){ pCost->rCost = cost; pCost->nRow = nRow; - pCost->plan.wsFlags = wsFlags; + pCost->used = used; + pCost->plan.wsFlags = (wsFlags&wsFlagMask); pCost->plan.nEq = nEq; - assert( pCost->plan.wsFlags & WHERE_INDEXED ); - pCost->plan.u.pIdx = pProbe; + pCost->plan.u.pIdx = pIdx; } + + /* If there was an INDEXED BY clause, then only that one index is + ** considered. */ + if( pSrc->pIndex ) break; + + /* Reset masks for the next index in the loop */ + wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); + eqTermMask = idxEqTermMask; + } + + /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag + ** is set, then reverse the order that the index will be scanned + ** in. This is used for application testing, to help find cases + ** where application behaviour depends on the (undefined) order that + ** SQLite outputs rows in in the absence of an ORDER BY clause. */ + if( !pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){ + pCost->plan.wsFlags |= WHERE_REVERSE; } - /* Report the best result - */ + assert( pOrderBy || (pCost->plan.wsFlags&WHERE_ORDERBY)==0 ); + assert( pCost->plan.u.pIdx==0 || (pCost->plan.wsFlags&WHERE_ROWID_EQ)==0 ); + assert( pSrc->pIndex==0 + || pCost->plan.u.pIdx==0 + || pCost->plan.u.pIdx==pSrc->pIndex + ); + + WHERETRACE(("best index is: %s\n", + (pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") + )); + + bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); pCost->plan.wsFlags |= eqTermMask; - WHERETRACE(("best index is %s, cost=%.9g, nrow=%.9g, wsFlags=%x, nEq=%d\n", - (pCost->plan.wsFlags & WHERE_INDEXED)!=0 ? - pCost->plan.u.pIdx->zName : "(none)", pCost->nRow, - pCost->rCost, pCost->plan.wsFlags, pCost->plan.nEq)); } /* ** Find the query plan for accessing table pSrc->pTab. Write the ** best query plan and its cost into the WhereCost object supplied @@ -2285,21 +2589,23 @@ } } } /* -** Apply the affinities associated with the first n columns of index -** pIdx to the values in the n registers starting at base. +** Code an OP_Affinity opcode to apply the column affinity string zAff +** to the n registers starting at base. +** +** Buffer zAff was allocated using sqlite3DbMalloc(). It is the +** responsibility of this function to arrange for it to be eventually +** freed using sqlite3DbFree(). */ -static void codeApplyAffinity(Parse *pParse, int base, int n, Index *pIdx){ - if( n>0 ){ - Vdbe *v = pParse->pVdbe; - assert( v!=0 ); - sqlite3VdbeAddOp2(v, OP_Affinity, base, n); - sqlite3IndexAffinityStr(v, pIdx); - sqlite3ExprCacheAffinityChange(pParse, base, n); - } +static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ + Vdbe *v = pParse->pVdbe; + assert( v!=0 ); + sqlite3VdbeAddOp2(v, OP_Affinity, base, n); + sqlite3VdbeChangeP4(v, -1, zAff, P4_DYNAMIC); + sqlite3ExprCacheAffinityChange(pParse, base, n); } /* ** Generate code for a single equality term of the WHERE clause. An equality @@ -2386,26 +2692,43 @@ ** the index of that memory cell. The code that ** calls this routine will use that memory cell to store the termination ** key value of the loop. If one or more IN operators appear, then ** this routine allocates an additional nEq memory cells for internal ** use. +** +** Before returning, *pzAff is set to point to a buffer containing a +** copy of the column affinity string of the index allocated using +** sqlite3DbMalloc(). Except, entries in the copy of the string associated +** with equality constraints that use NONE affinity are set to +** SQLITE_AFF_NONE. This is to deal with SQL such as the following: +** +** CREATE TABLE t1(a TEXT PRIMARY KEY, b); +** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b; +** +** In the example above, the index on t1(a) has TEXT affinity. But since +** the right hand side of the equality constraint (t2.b) has NONE affinity, +** no conversion should be attempted before using a t2.b value as part of +** a key to search the index. Hence the first byte in the returned affinity +** string in this example would be set to SQLITE_AFF_NONE. */ static int codeAllEqualityTerms( Parse *pParse, /* Parsing context */ WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */ WhereClause *pWC, /* The WHERE clause */ Bitmask notReady, /* Which parts of FROM have not yet been coded */ - int nExtraReg /* Number of extra registers to allocate */ + int nExtraReg, /* Number of extra registers to allocate */ + char **pzAff /* OUT: Set to point to affinity string */ ){ int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */ Vdbe *v = pParse->pVdbe; /* The vm under construction */ Index *pIdx; /* The index being used for this loop */ int iCur = pLevel->iTabCur; /* The cursor of the table */ WhereTerm *pTerm; /* A single constraint term */ int j; /* Loop counter */ int regBase; /* Base register */ int nReg; /* Number of registers to allocate */ + char *zAff; /* Affinity string to return */ /* This module is only called on query plans that use an index. */ assert( pLevel->plan.wsFlags & WHERE_INDEXED ); pIdx = pLevel->plan.u.pIdx; @@ -2412,10 +2735,15 @@ /* Figure out how many memory cells we will need then allocate them. */ regBase = pParse->nMem + 1; nReg = pLevel->plan.nEq + nExtraReg; pParse->nMem += nReg; + + zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx)); + if( !zAff ){ + pParse->db->mallocFailed = 1; + } /* Evaluate the equality constraints */ assert( pIdx->nColumn>=nEq ); for(j=0; jeOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); + if( zAff + && sqlite3CompareAffinity(pTerm->pExpr->pRight, zAff[j])==SQLITE_AFF_NONE + ){ + zAff[j] = SQLITE_AFF_NONE; + } } } + *pzAff = zAff; return regBase; } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause @@ -2692,10 +3026,11 @@ int nConstraint; /* Number of constraint terms */ Index *pIdx; /* The index we will be using */ int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ + char *zAff; pIdx = pLevel->plan.u.pIdx; iIdxCur = pLevel->iIdxCur; k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */ @@ -2731,13 +3066,14 @@ /* Generate code to evaluate all constraint terms using == or IN ** and store the values of those terms in an array of registers ** starting at regBase. */ - regBase = codeAllEqualityTerms(pParse, pLevel, pWC, notReady, nExtraReg); + regBase = codeAllEqualityTerms( + pParse, pLevel, pWC, notReady, nExtraReg, &zAff + ); addrNxt = pLevel->addrNxt; - /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). */ @@ -2754,20 +3090,29 @@ start_constraints = pRangeStart || nEq>0; /* Seek the index cursor to the start of the range. */ nConstraint = nEq; if( pRangeStart ){ - sqlite3ExprCode(pParse, pRangeStart->pExpr->pRight, regBase+nEq); + Expr *pRight = pRangeStart->pExpr->pRight; + sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); + if( zAff + && sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE + ){ + /* Since the comparison is to be performed with no conversions applied + ** to the operands, set the affinity to apply to pRight to + ** SQLITE_AFF_NONE. */ + zAff[nConstraint] = SQLITE_AFF_NONE; + } nConstraint++; }else if( isMinQuery ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); nConstraint++; startEq = 0; start_constraints = 1; } - codeApplyAffinity(pParse, regBase, nConstraint, pIdx); + codeApplyAffinity(pParse, regBase, nConstraint, zAff); op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; assert( op!=0 ); testcase( op==OP_Rewind ); testcase( op==OP_Last ); testcase( op==OP_SeekGt ); @@ -2780,14 +3125,24 @@ /* Load the value for the inequality constraint at the end of the ** range (if any). */ nConstraint = nEq; if( pRangeEnd ){ + Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq); - sqlite3ExprCode(pParse, pRangeEnd->pExpr->pRight, regBase+nEq); + sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); - codeApplyAffinity(pParse, regBase, nEq+1, pIdx); + zAff = sqlite3DbStrDup(pParse->db, zAff); + if( zAff + && sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE + ){ + /* Since the comparison is to be performed with no conversions applied + ** to the operands, set the affinity to apply to pRight to + ** SQLITE_AFF_NONE. */ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + codeApplyAffinity(pParse, regBase, nEq+1, zAff); nConstraint++; } /* Top of the loop body */ pLevel->p2 = sqlite3VdbeCurrentAddr(v); @@ -3268,48 +3623,88 @@ WHERETRACE(("*** Optimizer Start ***\n")); for(i=iFrom=0, pLevel=pWInfo->a; inSrc; i++, pLevel++){ WhereCost bestPlan; /* Most efficient plan seen so far */ Index *pIdx; /* Index for FROM table at pTabItem */ int j; /* For looping over FROM tables */ - int bestJ = 0; /* The value of j */ + int bestJ = -1; /* The value of j */ Bitmask m; /* Bitmask value for j or bestJ */ - int once = 0; /* True when first table is seen */ + int isOptimal; /* Iterator for optimal/non-optimal search */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; - for(j=iFrom, pTabItem=&pTabList->a[j]; jnSrc; j++, pTabItem++){ - int doNotReorder; /* True if this table should not be reordered */ - WhereCost sCost; /* Cost information from best[Virtual]Index() */ - ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ - - doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0; - if( once && doNotReorder ) break; - m = getMask(pMaskSet, pTabItem->iCursor); - if( (m & notReady)==0 ){ - if( j==iFrom ) iFrom++; - continue; - } - pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); - - assert( pTabItem->pTab ); + + /* Loop through the remaining entries in the FROM clause to find the + ** next nested loop. The FROM clause entries may be iterated through + ** either once or twice. + ** + ** The first iteration, which is always performed, searches for the + ** FROM clause entry that permits the lowest-cost, "optimal" scan. In + ** this context an optimal scan is one that uses the same strategy + ** for the given FROM clause entry as would be selected if the entry + ** were used as the innermost nested loop. In other words, a table + ** is chosen such that the cost of running that table cannot be reduced + ** by waiting for other tables to run first. + ** + ** The second iteration is only performed if no optimal scan strategies + ** were found by the first. This iteration is used to search for the + ** lowest cost scan overall. + ** + ** Previous versions of SQLite performed only the second iteration - + ** the next outermost loop was always that with the lowest overall + ** cost. However, this meant that SQLite could select the wrong plan + ** for scripts such as the following: + ** + ** CREATE TABLE t1(a, b); + ** CREATE TABLE t2(c, d); + ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; + ** + ** The best strategy is to iterate through table t1 first. However it + ** is not possible to determine this with a simple greedy algorithm. + ** However, since the cost of a linear scan through table t2 is the same + ** as the cost of a linear scan through table t1, a simple greedy + ** algorithm may choose to use t2 for the outer loop, which is a much + ** costlier approach. + */ + for(isOptimal=1; isOptimal>=0 && bestJ<0; isOptimal--){ + Bitmask mask = (isOptimal ? 0 : notReady); + assert( (pTabList->nSrc-iFrom)>1 || isOptimal ); + for(j=iFrom, pTabItem=&pTabList->a[j]; jnSrc; j++, pTabItem++){ + int doNotReorder; /* True if this table should not be reordered */ + WhereCost sCost; /* Cost information from best[Virtual]Index() */ + ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ + + doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0; + if( j!=iFrom && doNotReorder ) break; + m = getMask(pMaskSet, pTabItem->iCursor); + if( (m & notReady)==0 ){ + if( j==iFrom ) iFrom++; + continue; + } + pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); + + assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTabItem->pTab) ){ - sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; - bestVirtualIndex(pParse, pWC, pTabItem, notReady, pOrderBy, &sCost, pp); - }else + if( IsVirtual(pTabItem->pTab) ){ + sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; + bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp); + }else #endif - { - bestBtreeIndex(pParse, pWC, pTabItem, notReady, pOrderBy, &sCost); - } - if( once==0 || sCost.rCost=0 ); assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ, pLevel-pWInfo->a)); if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ *ppOrderBy = 0; @@ -3402,17 +3797,17 @@ sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC); } #endif /* SQLITE_OMIT_EXPLAIN */ pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue; #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); int iCur = pTabItem->iCursor; - sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, - (const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); }else #endif if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 && (wctrlFlags & WHERE_OMIT_OPEN)==0 ){ int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; ADDED test/analyze2.test Index: test/analyze2.test ================================================================== --- /dev/null +++ test/analyze2.test @@ -0,0 +1,505 @@ +# 2009 August 06 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements regression tests for SQLite library. This file +# implements tests for the extra functionality provided by the ANALYZE +# command when the library is compiled with SQLITE_ENABLE_STAT2 defined. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat2 { + finish_test + return +} + +#-------------------------------------------------------------------- +# Test organization: +# +# analyze2-1.*: Tests to verify that ANALYZE creates and populates the +# sqlite_stat2 table as expected. +# +# analyze2-2.*: Test that when a table has two indexes on it and either +# index may be used for the scan, the index suggested by +# the contents of sqlite_stat2 table is prefered. +# +# analyze2-3.*: Similar to the previous block of tests, but using tables +# that contain a mixture of NULL, numeric, text and blob +# values. +# +# analyze2-4.*: Check that when an indexed column uses a collation other +# than BINARY, the collation is taken into account when +# using the contents of sqlite_stat2 to estimate the cost +# of a range scan. +# +# analyze2-5.*: Check that collation sequences are used as described above +# even when the only available version of the collation +# function require UTF-16 encoded arguments. +# +# analyze2-6.*: Check that the library behaves correctly when one of the +# sqlite_stat2 or sqlite_stat1 tables are missing. +# +# analyze2-7.*: Check that in a shared-schema situation, nothing goes +# wrong if sqlite_stat2 data is read by one connection, +# and freed by another. +# + +proc eqp {sql {db db}} { + uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db +} + +do_test analyze2-1.1 { + execsql { CREATE TABLE t1(x PRIMARY KEY) } + for {set i 0} {$i < 1000} {incr i} { + execsql { INSERT INTO t1 VALUES($i) } + } + execsql { + ANALYZE; + SELECT * FROM sqlite_stat2; + } +} [list t1 sqlite_autoindex_t1_1 0 50 \ + t1 sqlite_autoindex_t1_1 1 149 \ + t1 sqlite_autoindex_t1_1 2 249 \ + t1 sqlite_autoindex_t1_1 3 349 \ + t1 sqlite_autoindex_t1_1 4 449 \ + t1 sqlite_autoindex_t1_1 5 549 \ + t1 sqlite_autoindex_t1_1 6 649 \ + t1 sqlite_autoindex_t1_1 7 749 \ + t1 sqlite_autoindex_t1_1 8 849 \ + t1 sqlite_autoindex_t1_1 9 949 \ +] + +do_test analyze2-1.2 { + execsql { + DELETE FROM t1 WHERe x>9; + ANALYZE; + SELECT tbl, idx, group_concat(sample, ' ') FROM sqlite_stat2; + } +} {t1 sqlite_autoindex_t1_1 {0 1 2 3 4 5 6 7 8 9}} +do_test analyze2-1.3 { + execsql { + DELETE FROM t1 WHERE x>8; + ANALYZE; + SELECT * FROM sqlite_stat2; + } +} {} +do_test analyze2-1.4 { + execsql { + DELETE FROM t1; + ANALYZE; + SELECT * FROM sqlite_stat2; + } +} {} + +do_test analyze2-2.1 { + execsql { + BEGIN; + DROP TABLE t1; + CREATE TABLE t1(x, y); + CREATE INDEX t1_x ON t1(x); + CREATE INDEX t1_y ON t1(y); + } + for {set i 0} {$i < 1000} {incr i} { + execsql { INSERT INTO t1 VALUES($i, $i) } + } + execsql COMMIT + execsql ANALYZE +} {} +do_test analyze2-2.2 { + eqp "SELECT * FROM t1 WHERE x>500 AND y>700" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-2.3 { + eqp "SELECT * FROM t1 WHERE x>700 AND y>500" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.3 { + eqp "SELECT * FROM t1 WHERE y>700 AND x>500" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-2.4 { + eqp "SELECT * FROM t1 WHERE y>500 AND x>700" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.5 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.6 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-2.7 { + eqp "SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.8 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN -400 AND -300" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-2.9 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 500 AND 100 AND y BETWEEN 100 AND 300" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-2.10 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN 500 AND 100" +} {0 0 {TABLE t1 WITH INDEX t1_y}} + +do_test analyze2-3.1 { + set alphabet [list a b c d e f g h i j] + execsql BEGIN + for {set i 0} {$i < 1000} {incr i} { + set str [lindex $alphabet [expr ($i/100)%10]] + append str [lindex $alphabet [expr ($i/ 10)%10]] + append str [lindex $alphabet [expr ($i/ 1)%10]] + execsql { INSERT INTO t1 VALUES($str, $str) } + } + execsql COMMIT + execsql ANALYZE + execsql { + SELECT tbl,idx,group_concat(sample,' ') + FROM sqlite_stat2 + WHERE idx = 't1_x' + GROUP BY tbl,idx + } +} {t1 t1_x {100 299 499 699 899 ajj cjj ejj gjj ijj}} +do_test analyze2-3.2 { + execsql { + SELECT tbl,idx,group_concat(sample,' ') + FROM sqlite_stat2 + WHERE idx = 't1_y' + GROUP BY tbl,idx + } +} {t1 t1_y {100 299 499 699 899 ajj cjj ejj gjj ijj}} + +do_test analyze2-3.3 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b'" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-3.4 { + eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 400 AND y BETWEEN 'a' AND 'h'" +} {0 0 {TABLE t1 WITH INDEX t1_x}} +do_test analyze2-3.5 { + eqp "SELECT * FROM t1 WHERE x<'a' AND y>'h'" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-3.6 { + eqp "SELECT * FROM t1 WHERE x<444 AND y>'h'" +} {0 0 {TABLE t1 WITH INDEX t1_y}} +do_test analyze2-3.7 { + eqp "SELECT * FROM t1 WHERE x<221 AND y>'g'" +} {0 0 {TABLE t1 WITH INDEX t1_x}} + +do_test analyze2-4.1 { + execsql { CREATE TABLE t3(a COLLATE nocase, b) } + execsql { CREATE INDEX t3a ON t3(a) } + execsql { CREATE INDEX t3b ON t3(b) } + set alphabet [list A b C d E f G h I j] + execsql BEGIN + for {set i 0} {$i < 1000} {incr i} { + set str [lindex $alphabet [expr ($i/100)%10]] + append str [lindex $alphabet [expr ($i/ 10)%10]] + append str [lindex $alphabet [expr ($i/ 1)%10]] + execsql { INSERT INTO t3 VALUES($str, $str) } + } + execsql COMMIT + execsql ANALYZE +} {} +do_test analyze2-4.2 { + execsql { + SELECT tbl,idx,group_concat(sample,' ') + FROM sqlite_stat2 + WHERE idx = 't3a' + GROUP BY tbl,idx + } +} {t3 t3a {AfA bEj CEj dEj EEj fEj GEj hEj IEj jEj}} +do_test analyze2-4.3 { + execsql { + SELECT tbl,idx,group_concat(sample,' ') + FROM sqlite_stat2 + WHERE idx = 't3b' + GROUP BY tbl,idx + } +} {t3 t3b {AbA CIj EIj GIj IIj bIj dIj fIj hIj jIj}} + +do_test analyze2-4.4 { + eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C'" +} {0 0 {TABLE t3 WITH INDEX t3b}} +do_test analyze2-4.5 { + eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c'" +} {0 0 {TABLE t3 WITH INDEX t3a}} + +ifcapable utf16 { + proc test_collate {enc lhs rhs} { + # puts $enc + return [string compare $lhs $rhs] + } + do_test analyze2-5.1 { + add_test_collate db 0 0 1 + execsql { CREATE TABLE t4(x COLLATE test_collate) } + execsql { CREATE INDEX t4x ON t4(x) } + set alphabet [list a b c d e f g h i j] + execsql BEGIN + for {set i 0} {$i < 1000} {incr i} { + set str [lindex $alphabet [expr ($i/100)%10]] + append str [lindex $alphabet [expr ($i/ 10)%10]] + append str [lindex $alphabet [expr ($i/ 1)%10]] + execsql { INSERT INTO t4 VALUES($str) } + } + execsql COMMIT + execsql ANALYZE + } {} + do_test analyze2-5.2 { + execsql { + SELECT tbl,idx,group_concat(sample,' ') + FROM sqlite_stat2 + WHERE tbl = 't4' + GROUP BY tbl,idx + } + } {t4 t4x {afa bej cej dej eej fej gej hej iej jej}} + do_test analyze2-5.3 { + eqp "SELECT * FROM t4 WHERE x>'ccc'" + } {0 0 {TABLE t4 WITH INDEX t4x}} + do_test analyze2-5.4 { + eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg'" + } {0 1 {TABLE t4 AS t42 WITH INDEX t4x} 1 0 {TABLE t4 AS t41 WITH INDEX t4x}} + do_test analyze2-5.5 { + eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc'" + } {0 0 {TABLE t4 AS t41 WITH INDEX t4x} 1 1 {TABLE t4 AS t42 WITH INDEX t4x}} +} + +#-------------------------------------------------------------------- +# These tests, analyze2-6.*, verify that the library behaves correctly +# when one of the sqlite_stat1 and sqlite_stat2 tables is missing. +# +# If the sqlite_stat1 table is not present, then the sqlite_stat2 +# table is not read. However, if it is the sqlite_stat2 table that +# is missing, the data in the sqlite_stat1 table is still used. +# +# Tests analyze2-6.1.* test the libary when the sqlite_stat2 table +# is missing. Tests analyze2-6.2.* test the library when sqlite_stat1 +# is not present. +# +do_test analyze2-6.0 { + execsql { + DROP TABLE IF EXISTS t4; + CREATE TABLE t5(a, b); CREATE INDEX t5i ON t5(a, b); + CREATE TABLE t6(a, b); CREATE INDEX t6i ON t6(a, b); + } + for {set ii 0} {$ii < 20} {incr ii} { + execsql { + INSERT INTO t5 VALUES($ii, $ii); + INSERT INTO t6 VALUES($ii/10, $ii/10); + } + } + execsql { + CREATE TABLE master AS + SELECT * FROM sqlite_master WHERE name LIKE 'sqlite_stat%' + } +} {} + +do_test analyze2-6.1.1 { + eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.1.2 { + db cache flush + execsql ANALYZE + eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.1.3 { + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.1.4 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.1.5 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.1.6 { + execsql { + PRAGMA writable_schema = 1; + INSERT INTO sqlite_master SELECT * FROM master; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a = 1 AND + t6.a = 1 AND t6.b = 1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} + +do_test analyze2-6.2.1 { + execsql { + DELETE FROM sqlite_stat1; + DELETE FROM sqlite_stat2; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.2.2 { + db cache flush + execsql ANALYZE + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.2.3 { + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} +do_test analyze2-6.2.4 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.2.5 { + execsql { + PRAGMA writable_schema = 1; + DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2'; + } + sqlite3 db test.db + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} +do_test analyze2-6.2.6 { + execsql { + PRAGMA writable_schema = 1; + INSERT INTO sqlite_master SELECT * FROM master; + } + sqlite3 db test.db + execsql ANALYZE + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } +} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + +#-------------------------------------------------------------------- +# These tests, analyze2-7.*, test that the sqlite_stat2 functionality +# works in shared-cache mode. Note that these tests reuse the database +# created for the analyze2-6.* tests. +# +ifcapable shared_cache { + db close + set ::enable_shared_cache [sqlite3_enable_shared_cache 1] + + proc incr_schema_cookie {zDb} { + foreach iOffset {24 40} { + set cookie [hexio_get_int [hexio_read $zDb $iOffset 4]] + incr cookie + hexio_write $zDb $iOffset [hexio_render_int32 $cookie] + } + } + + do_test analyze2-7.1 { + sqlite3 db1 test.db + sqlite3 db2 test.db + db1 cache size 0 + db2 cache size 0 + execsql { SELECT count(*) FROM t5 } db1 + } {20} + do_test analyze2-7.2 { + incr_schema_cookie test.db + execsql { SELECT count(*) FROM t5 } db2 + } {20} + do_test analyze2-7.3 { + incr_schema_cookie test.db + execsql { SELECT count(*) FROM t5 } db1 + } {20} + do_test analyze2-7.4 { + incr_schema_cookie test.db + execsql { SELECT count(*) FROM t5 } db2 + } {20} + + do_test analyze2-7.5 { + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } db1 + } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + do_test analyze2-7.6 { + incr_schema_cookie test.db + execsql { SELECT * FROM sqlite_master } db2 + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } db2 + } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + do_test analyze2-7.7 { + incr_schema_cookie test.db + execsql { SELECT * FROM sqlite_master } db1 + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } db1 + } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + + do_test analyze2-7.8 { + execsql { DELETE FROM sqlite_stat2 } db2 + execsql { SELECT * FROM sqlite_master } db1 + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } db1 + } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + do_test analyze2-7.9 { + execsql { SELECT * FROM sqlite_master } db2 + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } db2 + } {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}} + + do_test analyze2-7.10 { + incr_schema_cookie test.db + execsql { SELECT * FROM sqlite_master } db1 + eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND + t5.a>1 AND t5.a<15 AND + t6.a>1 + } db1 + } {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}} + + db1 close + db2 close + sqlite3_enable_shared_cache $::enable_shared_cache +} + +finish_test ADDED test/analyze3.test Index: test/analyze3.test ================================================================== --- /dev/null +++ test/analyze3.test @@ -0,0 +1,601 @@ +# 2009 August 06 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements regression tests for SQLite library. This file +# implements tests for range and LIKE constraints that use bound variables +# instead of literal constant arguments. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat2 { + finish_test + return +} + +#---------------------------------------------------------------------- +# Test Organization: +# +# analyze3-1.*: Test that the values of bound parameters are considered +# in the same way as constants when planning queries that +# use range constraints. +# +# analyze3-2.*: Test that the values of bound parameters are considered +# in the same way as constants when planning queries that +# use LIKE expressions in the WHERE clause. +# +# analyze3-3.*: Test that binding to a variable does not invalidate the +# query plan when there is no way in which replanning the +# query may produce a superior outcome. +# +# analyze3-4.*: Test that SQL or authorization callback errors occuring +# within sqlite3Reprepare() are handled correctly. +# +# analyze3-5.*: Check that the query plans of applicable statements are +# invalidated if the values of SQL parameter are modified +# using the clear_bindings() or transfer_bindings() APIs. +# + +proc getvar {varname} { uplevel #0 set $varname } +db function var getvar + +proc eqp {sql {db db}} { + uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db +} + +proc sf_execsql {sql {db db}} { + set ::sqlite_search_count 0 + set r [uplevel [list execsql $sql $db]] + + concat $::sqlite_search_count [$db status step] $r +} + +#------------------------------------------------------------------------- +# +# analyze3-1.1.1: +# Create a table with two columns. Populate the first column (affinity +# INTEGER) with integer values from 100 to 1100. Create an index on this +# column. ANALYZE the table. +# +# analyze3-1.1.2 - 3.1.3 +# Show that there are two possible plans for querying the table with +# a range constraint on the indexed column - "full table scan" or "use +# the index". When the range is specified using literal values, SQLite +# is able to pick the best plan based on the samples in sqlite_stat2. +# +# analyze3-1.1.4 - 3.1.9 +# Show that using SQL variables produces the same results as using +# literal values to constrain the range scan. +# +# These tests also check that the compiler code considers column +# affinities when estimating the number of rows scanned by the "use +# index strategy". +# +do_test analyze3-1.1.1 { + execsql { + BEGIN; + CREATE TABLE t1(x INTEGER, y); + CREATE INDEX i1 ON t1(x); + } + for {set i 0} {$i < 1000} {incr i} { + execsql { INSERT INTO t1 VALUES($i+100, $i) } + } + execsql { + COMMIT; + ANALYZE; + } +} {} + +do_test analyze3-1.1.2 { + eqp { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } +} {0 0 {TABLE t1 WITH INDEX i1}} +do_test analyze3-1.1.3 { + eqp { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } +} {0 0 {TABLE t1}} + +do_test analyze3-1.1.4 { + sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } +} {199 0 14850} +do_test analyze3-1.1.5 { + set l [string range "200" 0 end] + set u [string range "300" 0 end] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.1.6 { + set l [expr int(200)] + set u [expr int(300)] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.1.7 { + sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } +} {999 999 499500} +do_test analyze3-1.1.8 { + set l [string range "0" 0 end] + set u [string range "1100" 0 end] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {999 999 499500} +do_test analyze3-1.1.9 { + set l [expr int(0)] + set u [expr int(1100)] + sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u } +} {999 999 499500} + + +# The following tests are similar to the block above. The difference is +# that the indexed column has TEXT affinity in this case. In the tests +# above the affinity is INTEGER. +# +do_test analyze3-1.2.1 { + execsql { + BEGIN; + CREATE TABLE t2(x TEXT, y); + INSERT INTO t2 SELECT * FROM t1; + CREATE INDEX i2 ON t2(x); + COMMIT; + ANALYZE; + } +} {} +do_test analyze3-1.2.2 { + eqp { SELECT sum(y) FROM t2 WHERE x>1 AND x<2 } +} {0 0 {TABLE t2 WITH INDEX i2}} +do_test analyze3-1.2.3 { + eqp { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 } +} {0 0 {TABLE t2}} +do_test analyze3-1.2.4 { + sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 } +} {161 0 4760} +do_test analyze3-1.2.5 { + set l [string range "12" 0 end] + set u [string range "20" 0 end] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {161 0 text text 4760} +do_test analyze3-1.2.6 { + set l [expr int(12)] + set u [expr int(20)] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {161 0 integer integer 4760} +do_test analyze3-1.2.7 { + sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 } +} {999 999 490555} +do_test analyze3-1.2.8 { + set l [string range "0" 0 end] + set u [string range "99" 0 end] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {999 999 text text 490555} +do_test analyze3-1.2.9 { + set l [expr int(0)] + set u [expr int(99)] + sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} +} {999 999 integer integer 490555} + +# Same tests a third time. This time, column x has INTEGER affinity and +# is not the leftmost column of the table. This triggered a bug causing +# SQLite to use sub-optimal query plans in 3.6.18 and earlier. +# +do_test analyze3-1.3.1 { + execsql { + BEGIN; + CREATE TABLE t3(y TEXT, x INTEGER); + INSERT INTO t3 SELECT y, x FROM t1; + CREATE INDEX i3 ON t3(x); + COMMIT; + ANALYZE; + } +} {} +do_test analyze3-1.3.2 { + eqp { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } +} {0 0 {TABLE t3 WITH INDEX i3}} +do_test analyze3-1.3.3 { + eqp { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 } +} {0 0 {TABLE t3}} + +do_test analyze3-1.3.4 { + sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } +} {199 0 14850} +do_test analyze3-1.3.5 { + set l [string range "200" 0 end] + set u [string range "300" 0 end] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.3.6 { + set l [expr int(200)] + set u [expr int(300)] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {199 0 14850} +do_test analyze3-1.3.7 { + sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 } +} {999 999 499500} +do_test analyze3-1.3.8 { + set l [string range "0" 0 end] + set u [string range "1100" 0 end] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {999 999 499500} +do_test analyze3-1.3.9 { + set l [expr int(0)] + set u [expr int(1100)] + sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u } +} {999 999 499500} + +#------------------------------------------------------------------------- +# Test that the values of bound SQL variables may be used for the LIKE +# optimization. +# +drop_all_tables +do_test analyze3-2.1 { + execsql { + PRAGMA case_sensitive_like=off; + BEGIN; + CREATE TABLE t1(a, b TEXT COLLATE nocase); + CREATE INDEX i1 ON t1(b); + } + for {set i 0} {$i < 1000} {incr i} { + set t "" + append t [lindex {a b c d e f g h i j} [expr $i/100]] + append t [lindex {a b c d e f g h i j} [expr ($i/10)%10]] + append t [lindex {a b c d e f g h i j} [expr ($i%10)]] + execsql { INSERT INTO t1 VALUES($i, $t) } + } + execsql COMMIT +} {} +do_test analyze3-2.2 { + eqp { SELECT count(a) FROM t1 WHERE b LIKE 'a%' } +} {0 0 {TABLE t1 WITH INDEX i1}} +do_test analyze3-2.3 { + eqp { SELECT count(a) FROM t1 WHERE b LIKE '%a' } +} {0 0 {TABLE t1}} + +do_test analyze3-2.4 { + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' } +} {101 0 100} +do_test analyze3-2.5 { + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE '%a' } +} {999 999 100} + +do_test analyze3-2.4 { + set like "a%" + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } +} {101 0 100} +do_test analyze3-2.5 { + set like "%a" + sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like } +} {999 999 100} + + +#------------------------------------------------------------------------- +# This block of tests checks that statements are correctly marked as +# expired when the values bound to any parameters that may affect the +# query plan are modified. +# +drop_all_tables +db auth auth +proc auth {args} { + set ::auth 1 + return SQLITE_OK +} + +do_test analyze3-3.1 { + execsql { + BEGIN; + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(b); + } + for {set i 0} {$i < 100} {incr i} { + execsql { INSERT INTO t1 VALUES($i, $i, $i) } + } + execsql COMMIT + execsql ANALYZE +} {} + +do_test analyze3-3.2.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy] + sqlite3_expired $S +} {0} +do_test analyze3-3.2.2 { + sqlite3_bind_text $S 1 "abc" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.2.4 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.2.5 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b=?" -1 dummy] + sqlite3_expired $S +} {0} +do_test analyze3-3.2.6 { + sqlite3_bind_text $S 1 "abc" 3 + sqlite3_expired $S +} {0} +do_test analyze3-3.2.7 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.4.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + sqlite3_expired $S +} {0} +do_test analyze3-3.4.2 { + sqlite3_bind_text $S 1 "abc" 3 + sqlite3_expired $S +} {0} +do_test analyze3-3.4.3 { + sqlite3_bind_text $S 2 "def" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.4.4 { + sqlite3_bind_text $S 2 "ghi" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.4.5 { + sqlite3_expired $S +} {1} +do_test analyze3-3.4.6 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.5.1 { + set S [sqlite3_prepare_v2 db { + SELECT * FROM t1 WHERE a IN ( + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, + ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, + ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31 + ) AND b>?32; + } -1 dummy] + sqlite3_expired $S +} {0} +do_test analyze3-3.5.2 { + sqlite3_bind_text $S 31 "abc" 3 + sqlite3_expired $S +} {0} +do_test analyze3-3.5.3 { + sqlite3_bind_text $S 32 "def" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.5.5 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.6.1 { + set S [sqlite3_prepare_v2 db { + SELECT * FROM t1 WHERE a IN ( + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, + ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, + ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31, ?32 + ) AND b>?33; + } -1 dummy] + sqlite3_expired $S +} {0} +do_test analyze3-3.6.2 { + sqlite3_bind_text $S 32 "abc" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.6.3 { + sqlite3_bind_text $S 33 "def" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.6.5 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.7.1 { +breakpoint + set S [sqlite3_prepare_v2 db { + SELECT * FROM t1 WHERE a IN ( + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?33, + ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, + ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31, ?32 + ) AND b>?10; + } -1 dummy] + sqlite3_expired $S +} {0} +do_test analyze3-3.7.2 { + sqlite3_bind_text $S 32 "abc" 3 + sqlite3_expired $S +} {0} +do_test analyze3-3.7.3 { + sqlite3_bind_text $S 33 "def" 3 + sqlite3_expired $S +} {0} +do_test analyze3-3.7.4 { + sqlite3_bind_text $S 10 "def" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.7.6 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-3.8.1 { + execsql { + CREATE TABLE t4(x, y TEXT COLLATE NOCASE); + CREATE INDEX i4 ON t4(y); + } +} {} +do_test analyze3-3.8.2 { + set S [sqlite3_prepare_v2 db { + SELECT * FROM t4 WHERE x != ? AND y LIKE ? + } -1 dummy] + sqlite3_expired $S +} {0} +do_test analyze3-3.8.3 { + sqlite3_bind_text $S 1 "abc" 3 + sqlite3_expired $S +} {0} +do_test analyze3-3.8.4 { + sqlite3_bind_text $S 2 "def" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.8.7 { + sqlite3_bind_text $S 2 "ghi%" 4 + sqlite3_expired $S +} {1} +do_test analyze3-3.8.8 { + sqlite3_expired $S +} {1} +do_test analyze3-3.8.9 { + sqlite3_bind_text $S 2 "ghi%def" 7 + sqlite3_expired $S +} {1} +do_test analyze3-3.8.10 { + sqlite3_expired $S +} {1} +do_test analyze3-3.8.11 { + sqlite3_bind_text $S 2 "%ab" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.8.12 { + sqlite3_expired $S +} {1} +do_test analyze3-3.8.12 { + sqlite3_bind_text $S 2 "%de" 3 + sqlite3_expired $S +} {1} +do_test analyze3-3.8.13 { + sqlite3_expired $S +} {1} +do_test analyze3-3.8.14 { + sqlite3_finalize $S +} {SQLITE_OK} + +#------------------------------------------------------------------------- +# These tests check that errors encountered while repreparing an SQL +# statement within sqlite3Reprepare() are handled correctly. +# + +# Check a schema error. +# +do_test analyze3-4.1.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + sqlite3_step $S +} {SQLITE_DONE} +do_test analyze3-4.1.2 { + sqlite3_reset $S + sqlite3_bind_text $S 2 "abc" 3 + execsql { DROP TABLE t1 } + sqlite3_step $S +} {SQLITE_SCHEMA} +do_test analyze3-4.1.3 { + sqlite3_finalize $S +} {SQLITE_SCHEMA} + +# Check an authorization error. +# +do_test analyze3-4.2.1 { + execsql { + BEGIN; + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(b); + } + for {set i 0} {$i < 100} {incr i} { + execsql { INSERT INTO t1 VALUES($i, $i, $i) } + } + execsql COMMIT + execsql ANALYZE + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + sqlite3_step $S +} {SQLITE_DONE} +db auth auth +proc auth {args} { + if {[lindex $args 0] == "SQLITE_READ"} {return SQLITE_DENY} + return SQLITE_OK +} +do_test analyze3-4.2.2 { + sqlite3_reset $S + sqlite3_bind_text $S 2 "abc" 3 + sqlite3_step $S +} {SQLITE_SCHEMA} +do_test analyze3-4.2.4 { + sqlite3_finalize $S +} {SQLITE_SCHEMA} + +# Check the effect of an authorization error that occurs in a re-prepare +# performed by sqlite3_step() is the same as one that occurs within +# sqlite3Reprepare(). +# +do_test analyze3-4.3.1 { + db auth {} + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy] + execsql { CREATE TABLE t2(d, e, f) } + db auth auth + sqlite3_step $S +} {SQLITE_SCHEMA} +do_test analyze3-4.3.2 { + sqlite3_finalize $S +} {SQLITE_SCHEMA} +db auth {} + +#------------------------------------------------------------------------- +# Test that modifying bound variables using the clear_bindings() or +# transfer_bindings() APIs works. +# +# analyze3-5.1.*: sqlite3_clear_bindings() +# analyze3-5.2.*: sqlite3_transfer_bindings() +# +do_test analyze3-5.1.1 { + drop_all_tables + execsql { + CREATE TABLE t1(x TEXT COLLATE NOCASE); + CREATE INDEX i1 ON t1(x); + INSERT INTO t1 VALUES('aaa'); + INSERT INTO t1 VALUES('abb'); + INSERT INTO t1 VALUES('acc'); + INSERT INTO t1 VALUES('baa'); + INSERT INTO t1 VALUES('bbb'); + INSERT INTO t1 VALUES('bcc'); + } + + set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x LIKE ?" -1 dummy] + sqlite3_bind_text $S 1 "a%" 2 + set R [list] + while { "SQLITE_ROW" == [sqlite3_step $S] } { + lappend R [sqlite3_column_text $S 0] + } + concat [sqlite3_reset $S] $R +} {SQLITE_OK aaa abb acc} +do_test analyze3-5.1.2 { + sqlite3_clear_bindings $S + set R [list] + while { "SQLITE_ROW" == [sqlite3_step $S] } { + lappend R [sqlite3_column_text $S 0] + } + concat [sqlite3_reset $S] $R +} {SQLITE_OK} +do_test analyze3-5.1.3 { + sqlite3_finalize $S +} {SQLITE_OK} + +do_test analyze3-5.1.1 { + set S1 [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x LIKE ?" -1 dummy] + sqlite3_bind_text $S1 1 "b%" 2 + set R [list] + while { "SQLITE_ROW" == [sqlite3_step $S1] } { + lappend R [sqlite3_column_text $S1 0] + } + concat [sqlite3_reset $S1] $R +} {SQLITE_OK baa bbb bcc} + +do_test analyze3-5.1.2 { + set S2 [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x = ?" -1 dummy] + sqlite3_bind_text $S2 1 "a%" 2 + sqlite3_transfer_bindings $S2 $S1 + set R [list] + while { "SQLITE_ROW" == [sqlite3_step $S1] } { + lappend R [sqlite3_column_text $S1 0] + } + concat [sqlite3_reset $S1] $R +} {SQLITE_OK aaa abb acc} +do_test analyze3-5.1.3 { + sqlite3_finalize $S2 + sqlite3_finalize $S1 +} {SQLITE_OK} + +finish_test + ADDED test/async5.test Index: test/async5.test ================================================================== --- /dev/null +++ test/async5.test @@ -0,0 +1,69 @@ +# 2009 July 19 +# +# 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 tests that asynchronous IO is compatible with multi-file +# transactions. +# +# $Id: async5.test,v 1.1 2009/07/18 11:52:04 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +if {[info commands sqlite3async_initialize] eq ""} { + # The async logic is not built into this system + finish_test + return +} + +db close +file delete -force test2.db +sqlite3async_initialize "" 1 +sqlite3async_control halt never +sqlite3 db test.db + +do_test async5-1.1 { + execsql { + ATTACH 'test2.db' AS next; + CREATE TABLE main.t1(a, b); + CREATE TABLE next.t2(a, b); + BEGIN; + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t2 VALUES(3, 4); + COMMIT; + } +} {} +do_test async5-1.2 { + execsql { SELECT * FROM t1 } +} {1 2} +do_test async5-1.3 { + execsql { SELECT * FROM t2 } +} {3 4} +do_test async5-1.4 { + execsql { + BEGIN; + INSERT INTO t1 VALUES('a', 'b'); + INSERT INTO t2 VALUES('c', 'd'); + COMMIT; + } +} {} +do_test async5-1.5 { + execsql { SELECT * FROM t1 } +} {1 2 a b} +do_test async5-1.6 { + execsql { SELECT * FROM t2 } +} {3 4 c d} + +db close + +sqlite3async_control halt idle +sqlite3async_start +sqlite3async_wait +sqlite3async_control halt never +sqlite3async_shutdown +set sqlite3async_trace 0 +finish_test + Index: test/attach3.test ================================================================== --- test/attach3.test +++ test/attach3.test @@ -20,10 +20,15 @@ ifcapable !attach { finish_test return } + +# The tests in this file were written before SQLite supported recursive +# trigger invocation, and some tests depend on that to pass. So disable +# recursive triggers for this file. +catchsql { pragma recursive_triggers = off } # Create tables t1 and t2 in the main database execsql { CREATE TABLE t1(a, b); CREATE TABLE t2(c, d); @@ -315,10 +320,11 @@ ATTACH DATABASE '' AS NULL } db_list } {main temp {}} do_test attach3-12.10 { +breakpoint execsql { DETACH ? } db_list } {main temp} Index: test/auth.test ================================================================== --- test/auth.test +++ test/auth.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is testing the sqlite3_set_authorizer() API # and related functionality. # -# $Id: auth.test,v 1.45 2009/05/04 01:58:31 drh Exp $ +# $Id: auth.test,v 1.46 2009/07/02 18:40:35 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -2246,19 +2246,20 @@ UPDATE v1 SET x=1 WHERE x=117 } set authargs } [list \ SQLITE_UPDATE v1 x main {} \ - SQLITE_INSERT v1chng {} main r2 \ - SQLITE_READ v1 x main r2 \ - SQLITE_READ v1 x main r2 \ SQLITE_SELECT {} {} {} v1 \ SQLITE_READ t2 a main v1 \ SQLITE_READ t2 b main v1 \ SQLITE_SELECT {} {} {} {} \ SQLITE_READ v1 x main v1 \ + SQLITE_INSERT v1chng {} main r2 \ + SQLITE_READ v1 x main r2 \ + SQLITE_READ v1 x main r2 \ ] + do_test auth-4.4 { execsql { CREATE TRIGGER r3 INSTEAD OF DELETE ON v1 BEGIN INSERT INTO v1chng VALUES(OLD.x,NULL); END; @@ -2271,17 +2272,17 @@ DELETE FROM v1 WHERE x=117 } set authargs } [list \ SQLITE_DELETE v1 {} main {} \ - SQLITE_INSERT v1chng {} main r3 \ - SQLITE_READ v1 x main r3 \ SQLITE_SELECT {} {} {} v1 \ SQLITE_READ t2 a main v1 \ SQLITE_READ t2 b main v1 \ SQLITE_SELECT {} {} {} {} \ SQLITE_READ v1 x main v1 \ + SQLITE_INSERT v1chng {} main r3 \ + SQLITE_READ v1 x main r3 \ ] } ;# ifcapable view && trigger # Ticket #1338: Make sure authentication works in the presence of an AS @@ -2306,22 +2307,55 @@ ifcapable view { execsql { DROP TABLE v1chng; } } + } + ifcapable stat2 { + set stat2 "sqlite_stat2 " + } else { + set stat2 "" } do_test auth-5.2 { execsql { SELECT name FROM ( SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type='table' ORDER BY name } - } {sqlite_stat1 t1 t2 t3 t4} + } "sqlite_stat1 ${stat2}t1 t2 t3 t4" +} + +# Ticket #3944 +# +ifcapable trigger { + do_test auth-5.3.1 { + execsql { + CREATE TABLE t5 ( x ); + CREATE TRIGGER t5_tr1 AFTER INSERT ON t5 BEGIN + UPDATE t5 SET x = 1 WHERE NEW.x = 0; + END; + } + } {} + set ::authargs [list] + proc auth {args} { + eval lappend ::authargs $args + return SQLITE_OK + } + do_test auth-5.3.2 { + execsql { INSERT INTO t5 (x) values(0) } + set ::authargs + } [list SQLITE_INSERT t5 {} main {} \ + SQLITE_UPDATE t5 x main t5_tr1 \ + SQLITE_READ t5 x main t5_tr1 \ + ] + do_test auth-5.3.2 { + execsql { SELECT * FROM t5 } + } {1} } rename proc {} rename proc_real proc finish_test Index: test/autoinc.test ================================================================== --- test/autoinc.test +++ test/autoinc.test @@ -23,10 +23,12 @@ ifcapable {!autoinc} { finish_test return } +sqlite3_db_config_lookaside db 0 0 0 + # The database is initially empty. # do_test autoinc-1.1 { execsql { SELECT name FROM sqlite_master WHERE type='table'; @@ -554,84 +556,113 @@ SELECT * FROM sqlite_sequence WHERE name='t3'; } } {t3 0} -# Ticket #3928. Make sure that triggers to not make extra slots in -# the SQLITE_SEQUENCE table. -# -do_test autoinc-3928.1 { - db eval { - CREATE TABLE t3928(a INTEGER PRIMARY KEY AUTOINCREMENT, b); - CREATE TRIGGER t3928r1 BEFORE INSERT ON t3928 BEGIN - INSERT INTO t3928(b) VALUES('before1'); - INSERT INTO t3928(b) VALUES('before2'); - END; - CREATE TRIGGER t3928r2 AFTER INSERT ON t3928 BEGIN - INSERT INTO t3928(b) VALUES('after1'); - INSERT INTO t3928(b) VALUES('after2'); - END; - INSERT INTO t3928(b) VALUES('test'); - SELECT * FROM t3928 ORDER BY a; - } -} {1 before1 2 after1 3 after2 4 before2 5 after1 6 after2 7 test 8 before1 9 before2 10 after1 11 before1 12 before2 13 after2} -do_test autoinc-3928.2 { - db eval { - SELECT * FROM sqlite_sequence WHERE name='t3928' - } -} {t3928 13} - -do_test autoinc-3928.3 { - db eval { - DROP TRIGGER t3928r1; - DROP TRIGGER t3928r2; - CREATE TRIGGER t3928r3 BEFORE UPDATE ON t3928 - WHEN typeof(new.b)=='integer' BEGIN - INSERT INTO t3928(b) VALUES('before-int-' || new.b); - END; - CREATE TRIGGER t3928r4 AFTER UPDATE ON t3928 - WHEN typeof(new.b)=='integer' BEGIN - INSERT INTO t3928(b) VALUES('after-int-' || new.b); - END; - DELETE FROM t3928 WHERE a!=1; - UPDATE t3928 SET b=456 WHERE a=1; - SELECT * FROM t3928 ORDER BY a; - } -} {1 456 14 before-int-456 15 after-int-456} -do_test autoinc-3928.4 { - db eval { - SELECT * FROM sqlite_sequence WHERE name='t3928' - } -} {t3928 15} - -do_test autoinc-3928.5 { - db eval { - CREATE TABLE t3928b(x); - INSERT INTO t3928b VALUES(100); - INSERT INTO t3928b VALUES(200); - INSERT INTO t3928b VALUES(300); - DELETE FROM t3928; - CREATE TABLE t3928c(y INTEGER PRIMARY KEY AUTOINCREMENT, z); - CREATE TRIGGER t3928br1 BEFORE DELETE ON t3928b BEGIN - INSERT INTO t3928(b) VALUES('before-del-'||old.x); - INSERT INTO t3928c(z) VALUES('before-del-'||old.x); - END; - CREATE TRIGGER t3928br2 AFTER DELETE ON t3928b BEGIN - INSERT INTO t3928(b) VALUES('after-del-'||old.x); - INSERT INTO t3928c(z) VALUES('after-del-'||old.x); - END; - DELETE FROM t3928b; - SELECT * FROM t3928 ORDER BY a; - } -} {16 before-del-100 17 after-del-100 18 before-del-200 19 after-del-200 20 before-del-300 21 after-del-300} -do_test autoinc-3928.6 { - db eval { - SELECT * FROM t3928c ORDER BY y; - } -} {1 before-del-100 2 after-del-100 3 before-del-200 4 after-del-200 5 before-del-300 6 after-del-300} -do_test autoinc-3928.7 { - db eval { - SELECT * FROM sqlite_sequence WHERE name LIKE 't3928%' ORDER BY name; - } -} {t3928 21 t3928c 6} +ifcapable trigger { + catchsql { pragma recursive_triggers = off } + + # Ticket #3928. Make sure that triggers to not make extra slots in + # the SQLITE_SEQUENCE table. + # + do_test autoinc-3928.1 { + db eval { + CREATE TABLE t3928(a INTEGER PRIMARY KEY AUTOINCREMENT, b); + CREATE TRIGGER t3928r1 BEFORE INSERT ON t3928 BEGIN + INSERT INTO t3928(b) VALUES('before1'); + INSERT INTO t3928(b) VALUES('before2'); + END; + CREATE TRIGGER t3928r2 AFTER INSERT ON t3928 BEGIN + INSERT INTO t3928(b) VALUES('after1'); + INSERT INTO t3928(b) VALUES('after2'); + END; + INSERT INTO t3928(b) VALUES('test'); + SELECT * FROM t3928 ORDER BY a; + } + } {1 before1 2 after1 3 after2 4 before2 5 after1 6 after2 7 test 8 before1 9 before2 10 after1 11 before1 12 before2 13 after2} + do_test autoinc-3928.2 { + db eval { + SELECT * FROM sqlite_sequence WHERE name='t3928' + } + } {t3928 13} + + do_test autoinc-3928.3 { + db eval { + DROP TRIGGER t3928r1; + DROP TRIGGER t3928r2; + CREATE TRIGGER t3928r3 BEFORE UPDATE ON t3928 + WHEN typeof(new.b)=='integer' BEGIN + INSERT INTO t3928(b) VALUES('before-int-' || new.b); + END; + CREATE TRIGGER t3928r4 AFTER UPDATE ON t3928 + WHEN typeof(new.b)=='integer' BEGIN + INSERT INTO t3928(b) VALUES('after-int-' || new.b); + END; + DELETE FROM t3928 WHERE a!=1; + UPDATE t3928 SET b=456 WHERE a=1; + SELECT * FROM t3928 ORDER BY a; + } + } {1 456 14 before-int-456 15 after-int-456} + do_test autoinc-3928.4 { + db eval { + SELECT * FROM sqlite_sequence WHERE name='t3928' + } + } {t3928 15} + + do_test autoinc-3928.5 { + db eval { + CREATE TABLE t3928b(x); + INSERT INTO t3928b VALUES(100); + INSERT INTO t3928b VALUES(200); + INSERT INTO t3928b VALUES(300); + DELETE FROM t3928; + CREATE TABLE t3928c(y INTEGER PRIMARY KEY AUTOINCREMENT, z); + CREATE TRIGGER t3928br1 BEFORE DELETE ON t3928b BEGIN + INSERT INTO t3928(b) VALUES('before-del-'||old.x); + INSERT INTO t3928c(z) VALUES('before-del-'||old.x); + END; + CREATE TRIGGER t3928br2 AFTER DELETE ON t3928b BEGIN + INSERT INTO t3928(b) VALUES('after-del-'||old.x); + INSERT INTO t3928c(z) VALUES('after-del-'||old.x); + END; + DELETE FROM t3928b; + SELECT * FROM t3928 ORDER BY a; + } + } {16 before-del-100 17 after-del-100 18 before-del-200 19 after-del-200 20 before-del-300 21 after-del-300} + do_test autoinc-3928.6 { + db eval { + SELECT * FROM t3928c ORDER BY y; + } + } {1 before-del-100 2 after-del-100 3 before-del-200 4 after-del-200 5 before-del-300 6 after-del-300} + do_test autoinc-3928.7 { + db eval { + SELECT * FROM sqlite_sequence WHERE name LIKE 't3928%' ORDER BY name; + } + } {t3928 21 t3928c 6} + + # Ticket [a696379c1f0886615541a48b35bd8181a80e88f8] + do_test autoinc-a69637.1 { + db eval { + CREATE TABLE ta69637_1(x INTEGER PRIMARY KEY AUTOINCREMENT, y); + CREATE TABLE ta69637_2(z); + CREATE TRIGGER ra69637_1 AFTER INSERT ON ta69637_2 BEGIN + INSERT INTO ta69637_1(y) VALUES(new.z+1); + END; + INSERT INTO ta69637_2 VALUES(123); + SELECT * FROM ta69637_1; + } + } {1 124} + do_test autoinc-a69637.2 { + db eval { + CREATE VIEW va69637_2 AS SELECT * FROM ta69637_2; + CREATE TRIGGER ra69637_2 INSTEAD OF INSERT ON va69637_2 BEGIN + INSERT INTO ta69637_1(y) VALUES(new.z+10000); + END; + INSERT INTO va69637_2 VALUES(123); + SELECT * FROM ta69637_1; + } + } {1 124 2 10123} +} + + finish_test Index: test/bind.test ================================================================== --- test/bind.test +++ test/bind.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script testing the sqlite_bind API. # -# $Id: bind.test,v 1.47 2009/02/20 03:55:05 drh Exp $ +# $Id: bind.test,v 1.48 2009/07/22 07:27:57 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -289,11 +289,11 @@ sqlite_step $VM N VALUES COLNAMES sqlite3_reset $VM execsql {SELECT * FROM t1} } {hello hello hello} set enc [db eval {PRAGMA encoding}] -if {$enc=="UTF-8"} { +if {$enc=="UTF-8" || $enc==""} { do_test bind-6.5 { execsql {SELECT hex(a), hex(b), hex(c) FROM t1} } {68656C6C6F00746865726500 68656C6C6F007468657265 68656C6C6F} } elseif {$enc=="UTF-16le"} { do_test bind-6.5 { Index: test/boundary4.tcl ================================================================== --- test/boundary4.tcl +++ test/boundary4.tcl @@ -18,10 +18,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl # Many of the boundary tests depend on a working 64-bit implementation. if {![working_64bit_int]} { finish_test; return } +ifcapable !altertable { finish_test; return } } expr srand(0) # Generate interesting boundary numbers Index: test/boundary4.test ================================================================== --- test/boundary4.test +++ test/boundary4.test @@ -18,10 +18,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl # Many of the boundary tests depend on a working 64-bit implementation. if {![working_64bit_int]} { finish_test; return } +ifcapable !altertable { finish_test; return } do_test boundary4-1.1 { db eval { CREATE TABLE t1(a,x); INSERT INTO t1(oid,a,x) VALUES(549755813887,1,'0000007fffffffff'); Index: test/capi3c.test ================================================================== --- test/capi3c.test +++ test/capi3c.test @@ -11,11 +11,11 @@ # This file implements regression tests for SQLite library. # # This is a copy of the capi3.test file that has been adapted to # test the new sqlite3_prepare_v2 interface. # -# $Id: capi3c.test,v 1.22 2008/11/05 16:37:35 drh Exp $ +# $Id: capi3c.test,v 1.23 2009/07/22 07:27:57 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -1290,33 +1290,69 @@ # For a multi-column result set where the same table column is repeated # in multiple columns of the output, verify that doing a UTF-8 to UTF-16 # conversion (or vice versa) on one column does not change the value of # the second. # -do_test capi3c-23.1 { - set STMT [sqlite3_prepare_v2 db {SELECT b,b,b,b FROM t1} -1 TAIL] - sqlite3_step $STMT -} {SQLITE_ROW} -do_test capi3c-23.2 { - sqlite3_column_text16 $STMT 0 - sqlite3_column_text $STMT 1 -} {one} -do_test capi3c-23.3 { - sqlite3_column_text16 $STMT 2 - sqlite3_column_text $STMT 3 -} {one} -sqlite3_finalize $STMT -do_test capi3c-23.4 { - set STMT [sqlite3_prepare_v2 db {SELECT b||'x',b,b,b FROM t1} -1 TAIL] - sqlite3_step $STMT -} {SQLITE_ROW} -do_test capi3c-23.5 { - sqlite3_column_text16 $STMT 0 - sqlite3_column_text $STMT 1 -} {one} -do_test capi3c-23.6 { - sqlite3_column_text16 $STMT 2 - sqlite3_column_text $STMT 3 -} {one} -sqlite3_finalize $STMT +ifcapable utf16 { + do_test capi3c-23.1 { + set STMT [sqlite3_prepare_v2 db {SELECT b,b,b,b FROM t1} -1 TAIL] + sqlite3_step $STMT + } {SQLITE_ROW} + do_test capi3c-23.2 { + sqlite3_column_text16 $STMT 0 + sqlite3_column_text $STMT 1 + } {one} + do_test capi3c-23.3 { + sqlite3_column_text16 $STMT 2 + sqlite3_column_text $STMT 3 + } {one} + sqlite3_finalize $STMT + do_test capi3c-23.4 { + set STMT [sqlite3_prepare_v2 db {SELECT b||'x',b,b,b FROM t1} -1 TAIL] + sqlite3_step $STMT + } {SQLITE_ROW} + do_test capi3c-23.5 { + sqlite3_column_text16 $STMT 0 + sqlite3_column_text $STMT 1 + } {one} + do_test capi3c-23.6 { + sqlite3_column_text16 $STMT 2 + sqlite3_column_text $STMT 3 + } {one} + sqlite3_finalize $STMT +} + +# Test decltype on some SELECT statements that contain sub-selects. +# +proc decltype {zSql} { + set ret [list] + set STMT [sqlite3_prepare_v2 db $zSql -1 TAIL] + for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} { + lappend ret [sqlite3_column_decltype $STMT $i] + } + sqlite3_finalize $STMT + return $ret +} +do_test capi3c-24.1 { + execsql { CREATE TABLE t5(a INTEGER, b STRING, c DATETIME) } + decltype {SELECT * FROM t5} +} {INTEGER STRING DATETIME} +do_test capi3c-24.2 { + decltype {SELECT (SELECT c) FROM t5} +} {DATETIME} +do_test capi3c-24.3 { + decltype {SELECT (SELECT * FROM (SELECT c)) FROM t5} +} {DATETIME} +do_test capi3c-24.4 { + decltype {SELECT * FROM (SELECT * FROM t5 ORDER BY c LIMIT 1) ORDER BY b} +} {INTEGER STRING DATETIME} +do_test capi3c-24.5 { + decltype { + SELECT (SELECT x FROM (SELECT c AS x)) + FROM (SELECT * FROM t5 ORDER BY c LIMIT 1) ORDER BY b + } +} {DATETIME} +do_test capi3c-24.3 { + decltype {SELECT (SELECT x FROM (SELECT t5.a AS x)) FROM t5} +} {INTEGER} finish_test ADDED test/coalesce.test Index: test/coalesce.test ================================================================== --- /dev/null +++ test/coalesce.test @@ -0,0 +1,84 @@ +# 2009 November 10 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# Additional test cases for the COALESCE() and IFNULL() functions. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + + +do_test coalesce-1.0 { + db eval { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d); + INSERT INTO t1 VALUES(1, null, null, null); + INSERT INTO t1 VALUES(2, 2, 99, 99); + INSERT INTO t1 VALUES(3, null, 3, 99); + INSERT INTO t1 VALUES(4, null, null, 4); + INSERT INTO t1 VALUES(5, null, null, null); + INSERT INTO t1 VALUES(6, 22, 99, 99); + INSERT INTO t1 VALUES(7, null, 33, 99); + INSERT INTO t1 VALUES(8, null, null, 44); + + SELECT coalesce(b,c,d) FROM t1 ORDER BY a; + } +} {{} 2 3 4 {} 22 33 44} +do_test coalesce-1.1 { + db eval { + SELECT coalesce(d+c+b,d+c,d) FROM t1 ORDER BY a; + } +} {{} 200 102 4 {} 220 132 44} +do_test coalesce-1.2 { + db eval { + SELECT ifnull(d+c+b,ifnull(d+c,d)) FROM t1 ORDER BY a; + } +} {{} 200 102 4 {} 220 132 44} +do_test coalesce-1.3 { + db eval { + SELECT ifnull(ifnull(d+c+b,d+c),d) FROM t1 ORDER BY a; + } +} {{} 200 102 4 {} 220 132 44} +do_test coalesce-1.4 { + db eval { + SELECT ifnull(ifnull(b,c),d) FROM t1 ORDER BY a; + } +} {{} 2 3 4 {} 22 33 44} +do_test coalesce-1.5 { + db eval { + SELECT ifnull(b,ifnull(c,d)) FROM t1 ORDER BY a; + } +} {{} 2 3 4 {} 22 33 44} +do_test coalesce-1.6 { + db eval { + SELECT coalesce(b,NOT b,-b,abs(b),lower(b),length(b),min(b,5),b*123,c) + FROM t1 ORDER BY a; + } +} {{} 2 3 {} {} 22 33 {}} +do_test coalesce-1.7 { + db eval { + SELECT ifnull(nullif(a,4),99) + FROM t1 ORDER BY a; + } +} {1 2 3 99 5 6 7 8} +do_test coalesce-1.8 { + db eval { +pragma vdbe_listing=on; + SELECT coalesce( + CASE WHEN b=2 THEN 123 END, + CASE WHEN b=3 THEN 234 END, + CASE WHEN c=3 THEN 345 WHEN c=33 THEN 456 END, + d + ) + FROM t1 ORDER BY a; + } +} {{} 123 345 4 {} 99 456 44} + + +finish_test Index: test/corrupt.test ================================================================== --- test/corrupt.test +++ test/corrupt.test @@ -11,11 +11,11 @@ # This file implements regression tests for SQLite library. # # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # -# $Id: corrupt.test,v 1.10 2008/08/25 12:14:09 drh Exp $ +# $Id: corrupt.test,v 1.12 2009/07/13 09:41:45 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -172,6 +172,89 @@ catchsql { SELECT * FROM t1 WHERE x = 'abcde'; } } {1 {database disk image is malformed}} +do_test corrupt-4.1 { + db close + file delete -force test.db test.db-journal + sqlite3 db test.db + execsql { + PRAGMA page_size = 1024; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); + } + for {set i 0} {$i < 10} {incr i} { + set text [string repeat $i 220] + execsql { INSERT INTO t1 VALUES($i, $text) } + } + execsql { CREATE INDEX i1 ON t1(b) } +} {} +do_test corrupt-4.2 { + set iRoot [db one {SELECT rootpage FROM sqlite_master WHERE name = 'i1'}] + set iOffset [hexio_get_int [hexio_read test.db [expr 12+($iRoot-1)*1024] 2]] + set data [hexio_render_int32 [expr $iRoot - 1]] + hexio_write test.db [expr ($iRoot-1)*1024 + $iOffset] $data + db close + sqlite3 db test.db + + # The following DELETE statement attempts to delete a cell stored on the + # root page of index i1. After this cell is deleted it must be replaced + # by a cell retrieved from the child page (a leaf) of the deleted cell. + # This will fail, as the block modified the database image so that the + # child page of the deleted cell is from a table (intkey) b-tree, not an + # index b-tree as expected. At one point this was causing an assert() + # to fail. + catchsql { DELETE FROM t1 WHERE rowid = 3 } +} {1 {database disk image is malformed}} + +do_test corrupt-5.1 { + db close + file delete -force test.db test.db-journal + sqlite3 db test.db + + execsql { PRAGMA page_size = 1024 } + set ct "CREATE TABLE t1(c0 " + set i 0 + while {[string length $ct] < 950} { append ct ", c[incr i]" } + append ct ")" + execsql $ct +} {} + +do_test corrupt-5.2 { + db close + hexio_write test.db 108 00000000 + sqlite3 db test.db + catchsql { SELECT * FROM sqlite_master } +} {1 {database disk image is malformed}} + +# At one point, the specific corruption caused by this test case was +# causing a buffer overwrite. Although a crash was never demonstrated, +# running this testcase under valgrind revealed the problem. +do_test corrupt-6.1 { + db close + file delete -force test.db test.db-journal + sqlite3 db test.db + execsql { + PRAGMA page_size = 1024; CREATE TABLE t1(x); + } + + # The root page of t1 is 1024 bytes in size. The header is 8 bytes, and + # each of the cells inserted by the following INSERT statements consume + # 16 bytes (including the 2 byte cell-offset array entry). So the page + # can contain up to 63 cells. + for {set i 0} {$i < 63} {incr i} { + execsql { INSERT INTO t1 VALUES( randomblob(10) ) } + } + + # Free the cell stored right at the end of the page (at offset pgsz-14). + execsql { DELETE FROM t1 WHERE rowid=1 } + set rootpage [db one {SELECT rootpage FROM sqlite_master WHERE name = 't1'}] + db close + + set offset [expr ($rootpage * 1024)-14+2] + hexio_write test.db $offset 00FF + sqlite3 db test.db + + catchsql { INSERT INTO t1 VALUES( randomblob(10) ) } +} {1 {database disk image is malformed}} + finish_test Index: test/corrupt7.test ================================================================== --- test/corrupt7.test +++ test/corrupt7.test @@ -12,11 +12,11 @@ # # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. It specifically focuses # on corrupt cell offsets in a btree page. # -# $Id: corrupt7.test,v 1.7 2009/06/09 13:42:25 drh Exp $ +# $Id: corrupt7.test,v 1.8 2009/08/10 10:18:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # We must have the page_size pragma for these tests to work. @@ -65,18 +65,18 @@ db close hexio_write test.db 1062 FF sqlite3 db test.db db eval {PRAGMA integrity_check(1)} } {{*** in database main *** -Page 2: sqlite3BtreeInitPage() returns error code 11}} +Page 2: btreeInitPage() returns error code 11}} do_test corrupt7-2.2 { db close hexio_write test.db 1062 04 sqlite3 db test.db db eval {PRAGMA integrity_check(1)} } {{*** in database main *** -Page 2: sqlite3BtreeInitPage() returns error code 11}} +Page 2: btreeInitPage() returns error code 11}} } else { do_test corrupt7-2.1 { db close hexio_write test.db 1062 FF sqlite3 db test.db Index: test/corruptB.test ================================================================== --- test/corruptB.test +++ test/corruptB.test @@ -18,11 +18,11 @@ # # Also test that an SQLITE_CORRUPT error is returned if a B-Tree page # contains a (corrupt) reference to a page greater than the configured # maximum page number. # -# $Id: corruptB.test,v 1.3 2009/06/05 17:09:12 drh Exp $ +# $Id: corruptB.test,v 1.4 2009/07/21 19:25:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -152,11 +152,11 @@ hexio_write test.db [expr $offset+8] [hexio_render_int32 0x6FFFFFFF] } {4} do_test corruptB-2.1.2 { sqlite3 db test.db catchsql { SELECT * FROM t1 } -} {1 {database disk image is malformed}} +} {1 {database or disk is full}} #--------------------------------------------------------------------------- # Corrupt the header-size field of a database record. # Index: test/corruptC.test ================================================================== --- test/corruptC.test +++ test/corruptC.test @@ -13,11 +13,11 @@ # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. It creates a base # data base file, then tests that single byte corruptions in # increasingly larger quantities are handled gracefully. # -# $Id: corruptC.test,v 1.13 2009/06/06 19:21:13 drh Exp $ +# $Id: corruptC.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $ catch {file delete -force test.db test.db-journal test.bu} set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -152,13 +152,16 @@ sqlite3 db test.db catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;} catchsql {PRAGMA integrity_check} } {0 {{*** in database main *** -Corruption detected in cell 710 on page 4 -Multiple uses for byte 661 of page 4 -Fragmented space is 249 byte reported as 21 on page 4}}} +Page 4: btreeInitPage() returns error code 11}}} + +# {0 {{*** in database main *** +# Corruption detected in cell 710 on page 4 +# Multiple uses for byte 661 of page 4 +# Fragmented space is 249 byte reported as 21 on page 4}}} # test that a corrupt free cell size is handled (seed 169595) do_test corruptC-2.6 { db close copy_file test.bu test.db ADDED test/e_fkey.test Index: test/e_fkey.test ================================================================== --- /dev/null +++ test/e_fkey.test @@ -0,0 +1,2845 @@ +# 2009 October 7 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements tests to verify the "testable statements" in the +# foreignkeys.in document. +# +# The tests in this file are arranged to mirror the structure of +# foreignkey.in, with one exception: The statements in section 2, which +# deals with enabling/disabling foreign key support, is tested first, +# before section 1. This is because some statements in section 2 deal +# with builds that do not include complete foreign key support (because +# either SQLITE_OMIT_TRIGGER or SQLITE_OMIT_FOREIGN_KEY was defined +# at build time). +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +proc eqp {sql {db db}} { uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db } + +########################################################################### +### SECTION 2: Enabling Foreign Key Support +########################################################################### + +#------------------------------------------------------------------------- +# /* EV: R-33710-56344 */ +# +# Test builds neither OMIT_FOREIGN_KEY or OMIT_TRIGGER defined have +# foreign key functionality. +# +ifcapable trigger&&foreignkey { + do_test e_fkey-49 { + execsql { + PRAGMA foreign_keys = ON; + CREATE TABLE p(i PRIMARY KEY); + CREATE TABLE c(j REFERENCES p ON UPDATE CASCADE); + INSERT INTO p VALUES('hello'); + INSERT INTO c VALUES('hello'); + UPDATE p SET i = 'world'; + SELECT * FROM c; + } + } {world} +} + +#------------------------------------------------------------------------- +# /* EV: R-44697-61543 */ +# +# Test the effects of defining OMIT_TRIGGER but not OMIT_FOREIGN_KEY. +# +# /* EV: R-22567-44039 */ +# /* EV: R-41784-13339 */ +# +# Specifically, test that "PRAGMA foreign_keys" is a no-op in this case. +# When using the pragma to query the current setting, 0 rows are returned. +# +reset_db +ifcapable !trigger&&foreignkey { + do_test e_fkey-51.1 { + execsql { + PRAGMA foreign_keys = ON; + CREATE TABLE p(i PRIMARY KEY); + CREATE TABLE c(j REFERENCES p ON UPDATE CASCADE); + INSERT INTO p VALUES('hello'); + INSERT INTO c VALUES('hello'); + UPDATE p SET i = 'world'; + SELECT * FROM c; + } + } {hello} + do_test e_fkey-51.2 { + execsql { PRAGMA foreign_key_list(c) } + } {0 0 p j {} CASCADE {NO ACTION} NONE} + do_test e_fkey-51.3 { + execsql { PRAGMA foreign_keys } + } {} +} + + +#------------------------------------------------------------------------- +# /* EV: R-58428-36660 */ +# +# Test the effects of defining OMIT_FOREIGN_KEY. +# +# /* EV: R-58428-36660 */ +# +# Specifically, test that foreign key constraints cannot even be parsed +# in such a build. +# +reset_db +ifcapable !foreignkey { + do_test e_fkey-52.1 { + execsql { CREATE TABLE p(i PRIMARY KEY) } + catchsql { CREATE TABLE c(j REFERENCES p ON UPDATE CASCADE) } + } {1 {near "ON": syntax error}} + do_test e_fkey-52.2 { + # This is allowed, as in this build, "REFERENCES" is not a keyword. + # The declared datatype of column j is "REFERENCES p". + execsql { CREATE TABLE c(j REFERENCES p) } + } {} + do_test e_fkey-52.3 { + execsql { PRAGMA table_info(c) } + } {0 j {REFERENCES p} 0 {} 0} + do_test e_fkey-52.4 { + execsql { PRAGMA foreign_key_list(c) } + } {} + do_test e_fkey-52.5 { + execsql { PRAGMA foreign_keys } + } {} +} + +ifcapable !foreignkey||!trigger { finish_test ; return } +reset_db + + +#------------------------------------------------------------------------- +# /* EV: R-07280-60510 */ +# +# Test that even if foreign keys are supported by the build, they must +# be enabled using "PRAGMA foreign_keys = ON" (or similar). +# +# /* EV: R-59578-04990 */ +# +# This also tests that foreign key constraints are disabled by default. +# +drop_all_tables +do_test e_fkey-53.1 { + execsql { + CREATE TABLE p(i PRIMARY KEY); + CREATE TABLE c(j REFERENCES p ON UPDATE CASCADE); + INSERT INTO p VALUES('hello'); + INSERT INTO c VALUES('hello'); + UPDATE p SET i = 'world'; + SELECT * FROM c; + } +} {hello} +do_test e_fkey-53.2 { + execsql { + DELETE FROM c; + DELETE FROM p; + PRAGMA foreign_keys = ON; + INSERT INTO p VALUES('hello'); + INSERT INTO c VALUES('hello'); + UPDATE p SET i = 'world'; + SELECT * FROM c; + } +} {world} + +#------------------------------------------------------------------------- +# /* EV: R-15278-54456 */ +# /* EV: R-11255-19907 */ +# +# Test that the application can use "PRAGMA foreign_keys" to query for +# whether or not foreign keys are currently enabled. This also tests +# the example code in section 2 of foreignkeys.in. +# +reset_db +do_test e_fkey-54.1 { + execsql { PRAGMA foreign_keys } +} {0} +do_test e_fkey-54.2 { + execsql { + PRAGMA foreign_keys = ON; + PRAGMA foreign_keys; + } +} {1} +do_test e_fkey-54.3 { + execsql { + PRAGMA foreign_keys = OFF; + PRAGMA foreign_keys; + } +} {0} + +#------------------------------------------------------------------------- +# /* EV: R-46649-58537 */ +# +# Test that it is not possible to enable or disable foreign key support +# while not in auto-commit mode. +# +reset_db +do_test e_fkey-55.1 { + execsql { + PRAGMA foreign_keys = ON; + CREATE TABLE t1(a UNIQUE, b); + CREATE TABLE t2(c, d REFERENCES t1(a)); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t2 VALUES(2, 1); + BEGIN; + PRAGMA foreign_keys = OFF; + } + catchsql { + DELETE FROM t1 + } +} {1 {foreign key constraint failed}} +do_test e_fkey-55.2 { + execsql { PRAGMA foreign_keys } +} {1} +do_test e_fkey-55.3 { + execsql { + COMMIT; + PRAGMA foreign_keys = OFF; + BEGIN; + PRAGMA foreign_keys = ON; + DELETE FROM t1; + PRAGMA foreign_keys; + } +} {0} +do_test e_fkey-55.4 { + execsql COMMIT +} {} + +########################################################################### +### SECTION 1: Introduction to Foreign Key Constraints +########################################################################### +execsql "PRAGMA foreign_keys = ON" + +#------------------------------------------------------------------------- +# /* EV: R-04042-24825 */ +# +# Verify that the syntax in the first example in section 1 is valid. +# +do_test e_fkey-38.1 { + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER, + FOREIGN KEY(trackartist) REFERENCES artist(artistid) + ); + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-61362-32087 */ +# +# Attempting to insert a row into the 'track' table that corresponds +# to no row in the 'artist' table fails. +# +do_test e_fkey-39.1 { + catchsql { INSERT INTO track VALUES(1, 'track 1', 1) } +} {1 {foreign key constraint failed}} +do_test e_fkey-39.2 { + execsql { INSERT INTO artist VALUES(2, 'artist 1') } + catchsql { INSERT INTO track VALUES(1, 'track 1', 1) } +} {1 {foreign key constraint failed}} +do_test e_fkey-39.2 { + execsql { INSERT INTO track VALUES(1, 'track 1', 2) } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-24401-52400 */ +# +# Attempting to delete a row from the 'artist' table while there are +# dependent rows in the track table also fails. +# +do_test e_fkey-40.1 { + catchsql { DELETE FROM artist WHERE artistid = 2 } +} {1 {foreign key constraint failed}} +do_test e_fkey-40.2 { + execsql { + DELETE FROM track WHERE trackartist = 2; + DELETE FROM artist WHERE artistid = 2; + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-23980-48859 */ +# +# If the foreign key column (trackartist) in table 'track' is set to NULL, +# there is no requirement for a matching row in the 'artist' table. +# +do_test e_fkey-41.1 { + execsql { + INSERT INTO track VALUES(1, 'track 1', NULL); + INSERT INTO track VALUES(2, 'track 2', NULL); + } +} {} +do_test e_fkey-41.2 { + execsql { SELECT * FROM artist } +} {} +do_test e_fkey-41.3 { + # Setting the trackid to a non-NULL value fails, of course. + catchsql { UPDATE track SET trackartist = 5 WHERE trackid = 1 } +} {1 {foreign key constraint failed}} +do_test e_fkey-41.4 { + execsql { + INSERT INTO artist VALUES(5, 'artist 5'); + UPDATE track SET trackartist = 5 WHERE trackid = 1; + } + catchsql { DELETE FROM artist WHERE artistid = 5} +} {1 {foreign key constraint failed}} +do_test e_fkey-41.5 { + execsql { + UPDATE track SET trackartist = NULL WHERE trackid = 1; + DELETE FROM artist WHERE artistid = 5; + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-52486-21352 */ +# +# Test that the following is true fo all rows in the track table: +# +# trackartist IS NULL OR +# EXISTS(SELECT 1 FROM artist WHERE artistid=trackartist) +# + +# This procedure executes a test case to check that statement +# R-52486-21352 is true after executing the SQL statement passed. +# as the second argument. +proc test_r52486_21352 {tn sql} { + set res [catchsql $sql] + set results { + {0 {}} + {1 {PRIMARY KEY must be unique}} + {1 {foreign key constraint failed}} + } + if {[lsearch $results $res]<0} { + error $res + } + + do_test e_fkey-42.$tn { + execsql { + SELECT count(*) FROM track WHERE NOT ( + trackartist IS NULL OR + EXISTS(SELECT 1 FROM artist WHERE artistid=trackartist) + ) + } + } {0} +} + +# Execute a series of random INSERT, UPDATE and DELETE operations +# (some of which may fail due to FK or PK constraint violations) on +# the two tables in the example schema. Test that R-52486-21352 +# is true after executing each operation. +# +set Template { + {INSERT INTO track VALUES($t, 'track $t', $a)} + {DELETE FROM track WHERE trackid = $t} + {UPDATE track SET trackartist = $a WHERE trackid = $t} + {INSERT INTO artist VALUES($a, 'artist $a')} + {DELETE FROM artist WHERE artistid = $a} + {UPDATE artist SET artistid = $a2 WHERE artistid = $a} +} +for {set i 0} {$i < 500} {incr i} { + set a [expr int(rand()*10)] + set a2 [expr int(rand()*10)] + set t [expr int(rand()*50)] + set sql [subst [lindex $Template [expr int(rand()*6)]]] + + test_r52486_21352 $i $sql +} + +#------------------------------------------------------------------------- +# /* EV: R-42412-59321 */ +# +# Check that a NOT NULL constraint can be added to the example schema +# to prohibit NULL child keys from being inserted. +# +drop_all_tables +do_test e_fkey-48.1 { + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER NOT NULL, + FOREIGN KEY(trackartist) REFERENCES artist(artistid) + ); + } +} {} +do_test e_fkey-48.2 { + catchsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', NULL) } +} {1 {track.trackartist may not be NULL}} + +#------------------------------------------------------------------------- +# /* EV: R-17902-59250 */ +# +# Test an example from foreignkeys.html. +# +drop_all_tables +do_test e_fkey-43.1 { + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER, + FOREIGN KEY(trackartist) REFERENCES artist(artistid) + ); + INSERT INTO artist VALUES(1, 'Dean Martin'); + INSERT INTO artist VALUES(2, 'Frank Sinatra'); + INSERT INTO track VALUES(11, 'That''s Amore', 1); + INSERT INTO track VALUES(12, 'Christmas Blues', 1); + INSERT INTO track VALUES(13, 'My Way', 2); + } +} {} +do_test e_fkey-43.2 { + catchsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', 3) } +} {1 {foreign key constraint failed}} +do_test e_fkey-43.3 { + execsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', NULL) } +} {} +do_test e_fkey-43.4 { + catchsql { + UPDATE track SET trackartist = 3 WHERE trackname = 'Mr. Bojangles'; + } +} {1 {foreign key constraint failed}} +do_test e_fkey-43.5 { + execsql { + INSERT INTO artist VALUES(3, 'Sammy Davis Jr.'); + UPDATE track SET trackartist = 3 WHERE trackname = 'Mr. Bojangles'; + INSERT INTO track VALUES(15, 'Boogie Woogie', 3); + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-15034-64331 */ +# +# Test the second example from the first section of foreignkeys.html. +# +do_test e_fkey-44.1 { + catchsql { + DELETE FROM artist WHERE artistname = 'Frank Sinatra'; + } +} {1 {foreign key constraint failed}} +do_test e_fkey-44.2 { + execsql { + DELETE FROM track WHERE trackname = 'My Way'; + DELETE FROM artist WHERE artistname = 'Frank Sinatra'; + } +} {} +do_test e_fkey-44.3 { + catchsql { + UPDATE artist SET artistid=4 WHERE artistname = 'Dean Martin'; + } +} {1 {foreign key constraint failed}} +do_test e_fkey-44.4 { + execsql { + DELETE FROM track WHERE trackname IN('That''s Amore', 'Christmas Blues'); + UPDATE artist SET artistid=4 WHERE artistname = 'Dean Martin'; + } +} {} + + +#------------------------------------------------------------------------- +# /* EV: R-56032-24923 */ +# +# Test that a foreign key constraint is satisifed if "for each row in the child +# table either one or more of the child key columns are NULL, or there exists a +# row in the parent table for which each parent key column contains a value +# equal to the value in its associated child key column". +# +# /* EV: R-57765-12380 */ +# +# Test also that the comparison rules are used when testing if there +# is a matching row in the parent table of a foreign key constraint. +# +drop_all_tables +do_test e_fkey-45.1 { + execsql { + CREATE TABLE par(p PRIMARY KEY); + CREATE TABLE chi(c REFERENCES par); + + INSERT INTO par VALUES(1); + INSERT INTO par VALUES('1'); + INSERT INTO par VALUES(X'31'); + SELECT typeof(p) FROM par; + } +} {integer text blob} + +proc test_efkey_45 {tn isError sql} { + do_test e_fkey-45.$tn.1 " + catchsql {$sql} + " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError] + + do_test e_fkey-45.$tn.2 { + execsql { + SELECT * FROM chi WHERE c IS NOT NULL AND c NOT IN (SELECT p FROM par) + } + } {} +} + +test_efkey_45 1 0 "INSERT INTO chi VALUES(1)" +test_efkey_45 2 1 "INSERT INTO chi VALUES('1.0')" +test_efkey_45 3 0 "INSERT INTO chi VALUES('1')" +test_efkey_45 4 1 "DELETE FROM par WHERE p = '1'" +test_efkey_45 5 0 "DELETE FROM chi WHERE c = '1'" +test_efkey_45 6 0 "DELETE FROM par WHERE p = '1'" +test_efkey_45 7 1 "INSERT INTO chi VALUES('1')" +test_efkey_45 8 0 "INSERT INTO chi VALUES(X'31')" +test_efkey_45 9 1 "INSERT INTO chi VALUES(X'32')" + +#------------------------------------------------------------------------- +# /* EV: R-15796-47513 */ +# +# Specifically, test that when comparing child and parent key values the +# default collation sequence of the parent key column is used. +# +drop_all_tables +do_test e_fkey-46.1 { + execsql { + CREATE TABLE t1(a COLLATE nocase PRIMARY KEY); + CREATE TABLE t2(b REFERENCES t1); + } +} {} +do_test e_fkey-46.2 { + execsql { + INSERT INTO t1 VALUES('oNe'); + INSERT INTO t2 VALUES('one'); + INSERT INTO t2 VALUES('ONE'); + UPDATE t2 SET b = 'OnE'; + UPDATE t1 SET a = 'ONE'; + } +} {} +do_test e_fkey-46.3 { + catchsql { UPDATE t2 SET b = 'two' WHERE rowid = 1 } +} {1 {foreign key constraint failed}} +do_test e_fkey-46.4 { + catchsql { DELETE FROM t1 WHERE rowid = 1 } +} {1 {foreign key constraint failed}} + +#------------------------------------------------------------------------- +# /* EV: R-04240-13860 */ +# +# Specifically, test that when comparing child and parent key values the +# affinity of the parent key column is applied to the child key value +# before the comparison takes place. +# +drop_all_tables +do_test e_fkey-47.1 { + execsql { + CREATE TABLE t1(a NUMERIC PRIMARY KEY); + CREATE TABLE t2(b TEXT REFERENCES t1); + } +} {} +do_test e_fkey-47.2 { + execsql { + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t1 VALUES('three'); + INSERT INTO t2 VALUES('2.0'); + SELECT b, typeof(b) FROM t2; + } +} {2.0 text} +do_test e_fkey-47.3 { + execsql { SELECT typeof(a) FROM t1 } +} {integer integer text} +do_test e_fkey-47.4 { + catchsql { DELETE FROM t1 WHERE rowid = 2 } +} {1 {foreign key constraint failed}} + +########################################################################### +### SECTION 3: Required and Suggested Database Indexes +########################################################################### + +#------------------------------------------------------------------------- +# /* EV: R-13435-26311 */ +# +# A parent key must be either a PRIMARY KEY, subject to a UNIQUE +# constraint, or have a UNIQUE index created on it. +# +# /* EV: R-00376-39212 */ +# +# Also test that if a parent key is not subject to a PRIMARY KEY or UNIQUE +# constraint, but does have a UNIQUE index created on it, then the UNIQUE index +# must use the default collation sequences associated with the parent key +# columns. +# +drop_all_tables +do_test e_fkey-57.1 { + execsql { + CREATE TABLE t2(a REFERENCES t1(x)); + } +} {} +proc test_efkey_57 {tn isError sql} { + catchsql { DROP TABLE t1 } + execsql $sql + do_test e_fkey-57.$tn { + catchsql { INSERT INTO t2 VALUES(NULL) } + } [lindex {{0 {}} {1 {foreign key mismatch}}} $isError] +} +test_efkey_57 2 0 { CREATE TABLE t1(x PRIMARY KEY) } +test_efkey_57 3 0 { CREATE TABLE t1(x UNIQUE) } +test_efkey_57 4 0 { CREATE TABLE t1(x); CREATE UNIQUE INDEX t1i ON t1(x) } +test_efkey_57 5 1 { + CREATE TABLE t1(x); + CREATE UNIQUE INDEX t1i ON t1(x COLLATE nocase); +} +test_efkey_57 6 1 { CREATE TABLE t1(x) } +test_efkey_57 7 1 { CREATE TABLE t1(x, y, PRIMARY KEY(x, y)) } +test_efkey_57 8 1 { CREATE TABLE t1(x, y, UNIQUE(x, y)) } +test_efkey_57 9 1 { + CREATE TABLE t1(x, y); + CREATE UNIQUE INDEX t1i ON t1(x, y); +} + + +#------------------------------------------------------------------------- +# This block tests an example in foreignkeys.html. Several testable +# statements refer to this example, as follows +# +# /* EV: R-27484-01467 */ +# +# FK Constraints on child1, child2 and child3 are Ok. +# +# /* EV: R-51039-44840 */ +# +# Problem with FK on child4. +# +# /* EV: R-01060-48788 */ +# +# Problem with FK on child5. +# +# /* EV: R-63088-37469 */ +# +# Problem with FK on child6 and child7. +# +drop_all_tables +do_test e_fkey-56.1 { + execsql { + CREATE TABLE parent(a PRIMARY KEY, b UNIQUE, c, d, e, f); + CREATE UNIQUE INDEX i1 ON parent(c, d); + CREATE INDEX i2 ON parent(e); + CREATE UNIQUE INDEX i3 ON parent(f COLLATE nocase); + + CREATE TABLE child1(f, g REFERENCES parent(a)); -- Ok + CREATE TABLE child2(h, i REFERENCES parent(b)); -- Ok + CREATE TABLE child3(j, k, FOREIGN KEY(j, k) REFERENCES parent(c, d)); -- Ok + CREATE TABLE child4(l, m REFERENCES parent(e)); -- Err + CREATE TABLE child5(n, o REFERENCES parent(f)); -- Err + CREATE TABLE child6(p, q, FOREIGN KEY(p,q) REFERENCES parent(b, c)); -- Err + CREATE TABLE child7(r REFERENCES parent(c)); -- Err + } +} {} +do_test e_fkey-56.2 { + execsql { + INSERT INTO parent VALUES(1, 2, 3, 4, 5, 6); + INSERT INTO child1 VALUES('xxx', 1); + INSERT INTO child2 VALUES('xxx', 2); + INSERT INTO child3 VALUES(3, 4); + } +} {} +do_test e_fkey-56.2 { + catchsql { INSERT INTO child4 VALUES('xxx', 5) } +} {1 {foreign key mismatch}} +do_test e_fkey-56.3 { + catchsql { INSERT INTO child5 VALUES('xxx', 6) } +} {1 {foreign key mismatch}} +do_test e_fkey-56.4 { + catchsql { INSERT INTO child6 VALUES(2, 3) } +} {1 {foreign key mismatch}} +do_test e_fkey-56.5 { + catchsql { INSERT INTO child7 VALUES(3) } +} {1 {foreign key mismatch}} + +#------------------------------------------------------------------------- +# /* EV: R-45488-08504 */ +# /* EV: R-48391-38472 */ +# /* EV: R-03108-63659 */ +# /* EV: R-60781-26576 */ +# +# Test errors in the database schema that are detected while preparing +# DML statements. The error text for these messages always matches +# either "foreign key mismatch" or "no such table*" (using [string match]). +# +do_test e_fkey-66.1 { + execsql { + CREATE TABLE c1(c REFERENCES nosuchtable, d); + + CREATE TABLE p2(a, b, UNIQUE(a, b)); + CREATE TABLE c2(c, d, FOREIGN KEY(c, d) REFERENCES p2(a, x)); + + CREATE TABLE p3(a PRIMARY KEY, b); + CREATE TABLE c3(c REFERENCES p3(b), d); + + CREATE TABLE p4(a PRIMARY KEY, b); + CREATE UNIQUE INDEX p4i ON p4(b COLLATE nocase); + CREATE TABLE c4(c REFERENCES p4(b), d); + + CREATE TABLE p5(a PRIMARY KEY, b COLLATE nocase); + CREATE UNIQUE INDEX p5i ON p5(b COLLATE binary); + CREATE TABLE c5(c REFERENCES p5(b), d); + + CREATE TABLE p6(a PRIMARY KEY, b); + CREATE TABLE c6(c, d, FOREIGN KEY(c, d) REFERENCES p6); + + CREATE TABLE p7(a, b, PRIMARY KEY(a, b)); + CREATE TABLE c7(c, d REFERENCES p7); + } +} {} + +foreach {tn tbl ptbl err} { + 2 c1 {} "no such table: main.nosuchtable" + 3 c2 p2 "foreign key mismatch" + 4 c3 p3 "foreign key mismatch" + 5 c4 p4 "foreign key mismatch" + 6 c5 p5 "foreign key mismatch" + 7 c6 p6 "foreign key mismatch" + 8 c7 p7 "foreign key mismatch" +} { + do_test e_fkey-66.$tn.1 { + catchsql "INSERT INTO $tbl VALUES('a', 'b')" + } [list 1 $err] + do_test e_fkey-66.$tn.2 { + catchsql "UPDATE $tbl SET c = ?, d = ?" + } [list 1 $err] + do_test e_fkey-66.$tn.3 { + catchsql "INSERT INTO $tbl SELECT ?, ?" + } [list 1 $err] + + if {$ptbl ne ""} { + do_test e_fkey-66.$tn.4 { + catchsql "DELETE FROM $ptbl" + } [list 1 $err] + do_test e_fkey-66.$tn.5 { + catchsql "UPDATE $ptbl SET a = ?, b = ?" + } [list 1 $err] + do_test e_fkey-66.$tn.6 { + catchsql "INSERT INTO $ptbl SELECT ?, ?" + } [list 1 $err] + } +} + +#------------------------------------------------------------------------- +# /* EV: R-19353-43643 */ +# +# Test the example of foreign key mismatch errors caused by implicitly +# mapping a child key to the primary key of the parent table when the +# child key consists of a different number of columns to that primary key. +# +drop_all_tables +do_test e_fkey-58.1 { + execsql { + CREATE TABLE parent2(a, b, PRIMARY KEY(a,b)); + + CREATE TABLE child8(x, y, FOREIGN KEY(x,y) REFERENCES parent2); -- Ok + CREATE TABLE child9(x REFERENCES parent2); -- Err + CREATE TABLE child10(x,y,z, FOREIGN KEY(x,y,z) REFERENCES parent2); -- Err + } +} {} +do_test e_fkey-58.2 { + execsql { + INSERT INTO parent2 VALUES('I', 'II'); + INSERT INTO child8 VALUES('I', 'II'); + } +} {} +do_test e_fkey-58.3 { + catchsql { INSERT INTO child9 VALUES('I') } +} {1 {foreign key mismatch}} +do_test e_fkey-58.4 { + catchsql { INSERT INTO child9 VALUES('II') } +} {1 {foreign key mismatch}} +do_test e_fkey-58.5 { + catchsql { INSERT INTO child9 VALUES(NULL) } +} {1 {foreign key mismatch}} +do_test e_fkey-58.6 { + catchsql { INSERT INTO child10 VALUES('I', 'II', 'III') } +} {1 {foreign key mismatch}} +do_test e_fkey-58.7 { + catchsql { INSERT INTO child10 VALUES(1, 2, 3) } +} {1 {foreign key mismatch}} +do_test e_fkey-58.8 { + catchsql { INSERT INTO child10 VALUES(NULL, NULL, NULL) } +} {1 {foreign key mismatch}} + +#------------------------------------------------------------------------- +# /* EV: R-23682-59820 */ +# +# Test errors that are reported when creating the child table. +# Specifically: +# +# * different number of child and parent key columns, and +# * child columns that do not exist. +# +# /* EV: R-33883-28833 */ +# +# These errors are reported whether or not FK support is enabled. +# +drop_all_tables +foreach fk [list OFF ON] { + execsql "PRAGMA foreign_keys = $fk" + set i 0 + foreach {sql error} { + "CREATE TABLE child1(a, b, FOREIGN KEY(a, b) REFERENCES p(c))" + {number of columns in foreign key does not match the number of columns in the referenced table} + "CREATE TABLE child2(a, b, FOREIGN KEY(a, b) REFERENCES p(c, d, e))" + {number of columns in foreign key does not match the number of columns in the referenced table} + "CREATE TABLE child2(a, b, FOREIGN KEY(a, c) REFERENCES p(c, d))" + {unknown column "c" in foreign key definition} + "CREATE TABLE child2(a, b, FOREIGN KEY(c, b) REFERENCES p(c, d))" + {unknown column "c" in foreign key definition} + } { + do_test e_fkey-59.$fk.[incr i] { + catchsql $sql + } [list 1 $error] + } +} + +#------------------------------------------------------------------------- +# /* EV: R-47109-40581 */ +# +# Test that a REFERENCING clause that does not specify parent key columns +# implicitly maps to the primary key of the parent table. +# +do_test e_fkey-60.1 { + execsql { + CREATE TABLE p1(a, b, PRIMARY KEY(a, b)); + CREATE TABLE p2(a, b PRIMARY KEY); + CREATE TABLE c1(c, d, FOREIGN KEY(c, d) REFERENCES p1); + CREATE TABLE c2(a, b REFERENCES p2); + } +} {} +proc test_efkey_60 {tn isError sql} { + do_test e_fkey-60.$tn " + catchsql {$sql} + " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError] +} + +test_efkey_60 2 1 "INSERT INTO c1 VALUES(239, 231)" +test_efkey_60 3 0 "INSERT INTO p1 VALUES(239, 231)" +test_efkey_60 4 0 "INSERT INTO c1 VALUES(239, 231)" +test_efkey_60 5 1 "INSERT INTO c2 VALUES(239, 231)" +test_efkey_60 6 0 "INSERT INTO p2 VALUES(239, 231)" +test_efkey_60 7 0 "INSERT INTO c2 VALUES(239, 231)" + +#------------------------------------------------------------------------- +# /* EV: R-15417-28014 */ +# +# Test that an index on on the child key columns of an FK constraint +# is optional. +# +# /* EV: R-15741-50893 */ +# +# Also test that if an index is created on the child key columns, it does +# not make a difference whether or not it is a UNIQUE index. +# +drop_all_tables +do_test e_fkey-61.1 { + execsql { + CREATE TABLE parent(x, y, UNIQUE(y, x)); + CREATE TABLE c1(a, b, FOREIGN KEY(a, b) REFERENCES parent(x, y)); + CREATE TABLE c2(a, b, FOREIGN KEY(a, b) REFERENCES parent(x, y)); + CREATE TABLE c3(a, b, FOREIGN KEY(a, b) REFERENCES parent(x, y)); + CREATE INDEX c2i ON c2(a, b); + CREATE UNIQUE INDEX c3i ON c2(b, a); + } +} {} +proc test_efkey_61 {tn isError sql} { + do_test e_fkey-61.$tn " + catchsql {$sql} + " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError] +} +foreach {tn c} [list 2 c1 3 c2 4 c3] { + test_efkey_61 $tn.1 1 "INSERT INTO $c VALUES(1, 2)" + test_efkey_61 $tn.2 0 "INSERT INTO parent VALUES(1, 2)" + test_efkey_61 $tn.3 0 "INSERT INTO $c VALUES(1, 2)" + + execsql "DELETE FROM $c ; DELETE FROM parent" +} + +#------------------------------------------------------------------------- +# /* EV: R-00279-52283 */ +# +# Test an example showing that when a row is deleted from the parent +# table, the child table is queried for orphaned rows as follows: +# +# SELECT rowid FROM track WHERE trackartist = ? +# +# /* EV: R-23302-30956 */ +# +# Also test that if the SELECT above would return any rows, a foreign +# key constraint is violated. +# +do_test e_fkey-62.1 { + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER, + FOREIGN KEY(trackartist) REFERENCES artist(artistid) + ); + } +} {} +do_test e_fkey-62.2 { + execsql { + PRAGMA foreign_keys = OFF; + EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; + EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?; + } +} {0 0 {TABLE artist} 0 0 {TABLE track}} +do_test e_fkey-62.3 { + execsql { + PRAGMA foreign_keys = ON; + EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; + } +} {0 0 {TABLE artist} 0 0 {TABLE track}} +do_test e_fkey-62.4 { + execsql { + INSERT INTO artist VALUES(5, 'artist 5'); + INSERT INTO artist VALUES(6, 'artist 6'); + INSERT INTO artist VALUES(7, 'artist 7'); + INSERT INTO track VALUES(1, 'track 1', 5); + INSERT INTO track VALUES(2, 'track 2', 6); + } +} {} + +do_test e_fkey-62.5 { + concat \ + [execsql { SELECT rowid FROM track WHERE trackartist = 5 }] \ + [catchsql { DELETE FROM artist WHERE artistid = 5 }] +} {1 1 {foreign key constraint failed}} + +do_test e_fkey-62.6 { + concat \ + [execsql { SELECT rowid FROM track WHERE trackartist = 7 }] \ + [catchsql { DELETE FROM artist WHERE artistid = 7 }] +} {0 {}} + +do_test e_fkey-62.7 { + concat \ + [execsql { SELECT rowid FROM track WHERE trackartist = 6 }] \ + [catchsql { DELETE FROM artist WHERE artistid = 6 }] +} {2 1 {foreign key constraint failed}} + +#------------------------------------------------------------------------- +# /* EV: R-54172-55848 */ +# +# Test that when a row is deleted from the parent table of an FK +# constraint, the child table is queried for orphaned rows. The +# query is equivalent to: +# +# SELECT rowid FROM WHERE = :parent_key_value +# +# /* EV: R-61616-46700 */ +# +# Also test that when a row is inserted into the parent table, or when the +# parent key values of an existing row are modified, a query equivalent +# to the following is planned. In some cases it is not executed, but it +# is always planned. +# +# SELECT rowid FROM WHERE = :parent_key_value +# +# +drop_all_tables +do_test e_fkey-64.1 { + execsql { CREATE TABLE parent(x, y, UNIQUE(y, x)) } +} {} +foreach {tn sql} { + 2 { + CREATE TABLE child(a, b, FOREIGN KEY(a, b) REFERENCES parent(x, y)) + } + 3 { + CREATE TABLE child(a, b, FOREIGN KEY(a, b) REFERENCES parent(x, y)); + CREATE INDEX childi ON child(a, b); + } + 4 { + CREATE TABLE child(a, b, FOREIGN KEY(a, b) REFERENCES parent(x, y)); + CREATE UNIQUE INDEX childi ON child(b, a); + } +} { + execsql $sql + + execsql {PRAGMA foreign_keys = OFF} + set delete [concat \ + [eqp "DELETE FROM parent WHERE 1"] \ + [eqp "SELECT rowid FROM child WHERE a = ? AND b = ?"] + ] + set update [concat \ + [eqp "UPDATE parent SET x=?, y=?"] \ + [eqp "SELECT rowid FROM child WHERE a = ? AND b = ?"] \ + [eqp "SELECT rowid FROM child WHERE a = ? AND b = ?"] + ] + execsql {PRAGMA foreign_keys = ON} + + do_test e_fkey-64.$tn.1 { eqp "DELETE FROM parent WHERE 1" } $delete + do_test e_fkey-64.$tn.2 { eqp "UPDATE parent set x=?, y=?" } $update + + execsql {DROP TABLE child} +} + +#------------------------------------------------------------------------- +# /* EV: R-14553-34013 */ +# +# Test the example schema at the end of section 3. Also test that is +# is "efficient". In this case "efficient" means that foreign key +# related operations on the parent table do not provoke linear scans. +# +drop_all_tables +do_test e_fkey-63.1 { + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER REFERENCES artist + ); + CREATE INDEX trackindex ON track(trackartist); + } +} {} +do_test e_fkey-63.2 { + eqp { INSERT INTO artist VALUES(?, ?) } +} {} +do_test e_fkey-63.3 { + eqp { UPDATE artist SET artistid = ?, artistname = ? } +} [list \ + 0 0 {TABLE artist} \ + 0 0 {TABLE track WITH INDEX trackindex} \ + 0 0 {TABLE track WITH INDEX trackindex} +] +do_test e_fkey-63.4 { + eqp { DELETE FROM artist } +} [list \ + 0 0 {TABLE artist} \ + 0 0 {TABLE track WITH INDEX trackindex} +] + + +########################################################################### +### SECTION 4.1: Composite Foreign Key Constraints +########################################################################### + +#------------------------------------------------------------------------- +# /* EV: R-41062-34431 */ +# +# Check that parent and child keys must have the same number of columns. +# +foreach {tn sql err} { + 1 "CREATE TABLE c(jj REFERENCES p(x, y))" + {foreign key on jj should reference only one column of table p} + + 2 "CREATE TABLE c(jj REFERENCES p())" {near ")": syntax error} + + 3 "CREATE TABLE c(jj, FOREIGN KEY(jj) REFERENCES p(x, y))" + {number of columns in foreign key does not match the number of columns in the referenced table} + + 4 "CREATE TABLE c(jj, FOREIGN KEY(jj) REFERENCES p())" + {near ")": syntax error} + + 5 "CREATE TABLE c(ii, jj, FOREIGN KEY(jj, ii) REFERENCES p())" + {near ")": syntax error} + + 6 "CREATE TABLE c(ii, jj, FOREIGN KEY(jj, ii) REFERENCES p(x))" + {number of columns in foreign key does not match the number of columns in the referenced table} + + 7 "CREATE TABLE c(ii, jj, FOREIGN KEY(jj, ii) REFERENCES p(x,y,z))" + {number of columns in foreign key does not match the number of columns in the referenced table} +} { + drop_all_tables + do_test e_fkey-65.$tn [list catchsql $sql] [list 1 $err] +} +do_test e_fkey-65.8 { + drop_all_tables + execsql { + CREATE TABLE p(x PRIMARY KEY); + CREATE TABLE c(a, b, FOREIGN KEY(a,b) REFERENCES p); + } + catchsql {DELETE FROM p} +} {1 {foreign key mismatch}} +do_test e_fkey-65.9 { + drop_all_tables + execsql { + CREATE TABLE p(x, y, PRIMARY KEY(x,y)); + CREATE TABLE c(a REFERENCES p); + } + catchsql {DELETE FROM p} +} {1 {foreign key mismatch}} + + +#------------------------------------------------------------------------- +# /* EV: R-24676-09859 */ +# +# Test the example schema in the "Composite Foreign Key Constraints" +# section. +# +do_test e_fkey-36.1 { + execsql { + CREATE TABLE album( + albumartist TEXT, + albumname TEXT, + albumcover BINARY, + PRIMARY KEY(albumartist, albumname) + ); + CREATE TABLE song( + songid INTEGER, + songartist TEXT, + songalbum TEXT, + songname TEXT, + FOREIGN KEY(songartist, songalbum) REFERENCES album(albumartist,albumname) + ); + } +} {} + +do_test e_fkey-36.2 { + execsql { + INSERT INTO album VALUES('Elvis Presley', 'Elvis'' Christmas Album', NULL); + INSERT INTO song VALUES( + 1, 'Elvis Presley', 'Elvis'' Christmas Album', 'Here Comes Santa Clause' + ); + } +} {} +do_test e_fkey-36.3 { + catchsql { + INSERT INTO song VALUES(2, 'Elvis Presley', 'Elvis Is Back!', 'Fever'); + } +} {1 {foreign key constraint failed}} + + +#------------------------------------------------------------------------- +# /* EV: R-33626-48418 */ +# +# Check that if any of the child key columns in the above schema are NULL, +# there is no requirement for a corresponding parent key. +# +do_test e_fkey-37.1 { + execsql { + INSERT INTO song VALUES(2, 'Elvis Presley', NULL, 'Fever'); + INSERT INTO song VALUES(3, NULL, 'Elvis Is Back', 'Soldier Boy'); + } +} {} + +########################################################################### +### SECTION 4.2: Deferred Foreign Key Constraints +########################################################################### + +#------------------------------------------------------------------------- +# Note: R-35290-16460 is tested below. +# +# TODO: R-30323-21917 + +#------------------------------------------------------------------------- +# /* EV: R-09323-30470 */ +# +# Test that if a statement violates an immediate FK constraint, and the +# database does not satisfy the FK constraint once all effects of the +# statement have been applied, an error is reported and the effects of +# the statement rolled back. +# +drop_all_tables +do_test e_fkey-33.1 { + execsql { + CREATE TABLE king(a, b, PRIMARY KEY(a)); + CREATE TABLE prince(c REFERENCES king, d); + } +} {} + +do_test e_fkey-33.2 { + # Execute a statement that violates the immediate FK constraint. + catchsql { INSERT INTO prince VALUES(1, 2) } +} {1 {foreign key constraint failed}} + +do_test e_fkey-33.3 { + # This time, use a trigger to fix the constraint violation before the + # statement has finished executing. Then execute the same statement as + # in the previous test case. This time, no error. + execsql { + CREATE TRIGGER kt AFTER INSERT ON prince WHEN + NOT EXISTS (SELECT a FROM king WHERE a = new.c) + BEGIN + INSERT INTO king VALUES(new.c, NULL); + END + } + execsql { INSERT INTO prince VALUES(1, 2) } +} {} + +# Test that operating inside a transaction makes no difference to +# immediate constraint violation handling. +do_test e_fkey-33.4 { + execsql { + BEGIN; + INSERT INTO prince VALUES(2, 3); + DROP TRIGGER kt; + } + catchsql { INSERT INTO prince VALUES(3, 4) } +} {1 {foreign key constraint failed}} +do_test e_fkey-33.5 { + execsql { + COMMIT; + SELECT * FROM king; + } +} {1 {} 2 {}} + +#------------------------------------------------------------------------- +# /* EV: R-49178-21358 */ +# /* EV: R-39692-12488 */ +# /* EV: R-55147-47664 */ +# /* EV: R-29604-30395 */ +# +# Test that if a deferred constraint is violated within a transaction, +# nothing happens immediately and the database is allowed to persist +# in a state that does not satisfy the FK constraint. However attempts +# to COMMIT the transaction fail until the FK constraint is satisfied. +# +proc test_efkey_34 {tn isError sql} { + do_test e_fkey-34.$tn " + catchsql {$sql} + " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError] +} +drop_all_tables + +test_efkey_34 1 0 { + CREATE TABLE ll(k PRIMARY KEY); + CREATE TABLE kk(c REFERENCES ll DEFERRABLE INITIALLY DEFERRED); +} +test_efkey_34 2 0 "BEGIN" +test_efkey_34 3 0 "INSERT INTO kk VALUES(5)" +test_efkey_34 4 0 "INSERT INTO kk VALUES(10)" +test_efkey_34 5 1 "COMMIT" +test_efkey_34 6 0 "INSERT INTO ll VALUES(10)" +test_efkey_34 7 1 "COMMIT" +test_efkey_34 8 0 "INSERT INTO ll VALUES(5)" +test_efkey_34 9 0 "COMMIT" + +#------------------------------------------------------------------------- +# /* EV: R-56844-61705 */ +# +# When not running inside a transaction, a deferred constraint is similar +# to an immediate constraint (violations are reported immediately). +# +drop_all_tables +proc test_efkey_35 {tn isError sql} { + do_test e_fkey-35.$tn " + catchsql {$sql} + " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError] +} +do_test e_fkey-35.1 { + execsql { + CREATE TABLE parent(x, y); + CREATE UNIQUE INDEX pi ON parent(x, y); + CREATE TABLE child(a, b, + FOREIGN KEY(a, b) REFERENCES parent(x, y) DEFERRABLE INITIALLY DEFERRED + ); + } +} {} +test_efkey_35 2 1 "INSERT INTO child VALUES('x', 'y')" +test_efkey_35 3 0 "INSERT INTO parent VALUES('x', 'y')" +test_efkey_35 4 0 "INSERT INTO child VALUES('x', 'y')" + + +#------------------------------------------------------------------------- +# /* EV: R-12782-61841 */ +# +# Test that an FK constraint is made deferred by adding the following +# to the definition: +# +# DEFERRABLE INITIALLY DEFERRED +# +# /* EV: R-09005-28791 */ +# +# Also test that adding any of the following to a foreign key definition +# makes the constraint IMMEDIATE: +# +# NOT DEFERRABLE INITIALLY DEFERRED +# NOT DEFERRABLE INITIALLY IMMEDIATE +# NOT DEFERRABLE +# DEFERRABLE INITIALLY IMMEDIATE +# DEFERRABLE +# +# /* EV: R-35290-16460 */ +# +# Foreign keys are IMMEDIATE by default (if there is no DEFERRABLE or NOT +# DEFERRABLE clause). +# +# /* EV: R-30323-21917 */ FKs are either IMMEDIATE or DEFERRED. +# +drop_all_tables +do_test e_fkey-29.1 { + execsql { + CREATE TABLE parent(x, y, z, PRIMARY KEY(x,y,z)); + CREATE TABLE c1(a, b, c, + FOREIGN KEY(a, b, c) REFERENCES parent NOT DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE c2(a, b, c, + FOREIGN KEY(a, b, c) REFERENCES parent NOT DEFERRABLE INITIALLY IMMEDIATE + ); + CREATE TABLE c3(a, b, c, + FOREIGN KEY(a, b, c) REFERENCES parent NOT DEFERRABLE + ); + CREATE TABLE c4(a, b, c, + FOREIGN KEY(a, b, c) REFERENCES parent DEFERRABLE INITIALLY IMMEDIATE + ); + CREATE TABLE c5(a, b, c, + FOREIGN KEY(a, b, c) REFERENCES parent DEFERRABLE + ); + CREATE TABLE c6(a, b, c, FOREIGN KEY(a, b, c) REFERENCES parent); + + -- This FK constraint is the only deferrable one. + CREATE TABLE c7(a, b, c, + FOREIGN KEY(a, b, c) REFERENCES parent DEFERRABLE INITIALLY DEFERRED + ); + + INSERT INTO parent VALUES('a', 'b', 'c'); + INSERT INTO parent VALUES('d', 'e', 'f'); + INSERT INTO parent VALUES('g', 'h', 'i'); + INSERT INTO parent VALUES('j', 'k', 'l'); + INSERT INTO parent VALUES('m', 'n', 'o'); + INSERT INTO parent VALUES('p', 'q', 'r'); + INSERT INTO parent VALUES('s', 't', 'u'); + + INSERT INTO c1 VALUES('a', 'b', 'c'); + INSERT INTO c2 VALUES('d', 'e', 'f'); + INSERT INTO c3 VALUES('g', 'h', 'i'); + INSERT INTO c4 VALUES('j', 'k', 'l'); + INSERT INTO c5 VALUES('m', 'n', 'o'); + INSERT INTO c6 VALUES('p', 'q', 'r'); + INSERT INTO c7 VALUES('s', 't', 'u'); + } +} {} + +proc test_efkey_29 {tn sql isError} { + do_test e_fkey-29.$tn "catchsql {$sql}" [ + lindex {{0 {}} {1 {foreign key constraint failed}}} $isError + ] +} +test_efkey_29 2 "BEGIN" 0 +test_efkey_29 3 "DELETE FROM parent WHERE x = 'a'" 1 +test_efkey_29 4 "DELETE FROM parent WHERE x = 'd'" 1 +test_efkey_29 5 "DELETE FROM parent WHERE x = 'g'" 1 +test_efkey_29 6 "DELETE FROM parent WHERE x = 'j'" 1 +test_efkey_29 7 "DELETE FROM parent WHERE x = 'm'" 1 +test_efkey_29 8 "DELETE FROM parent WHERE x = 'p'" 1 +test_efkey_29 9 "DELETE FROM parent WHERE x = 's'" 0 +test_efkey_29 10 "COMMIT" 1 +test_efkey_29 11 "ROLLBACK" 0 + +test_efkey_29 9 "BEGIN" 0 +test_efkey_29 10 "UPDATE parent SET z = 'z' WHERE z = 'c'" 1 +test_efkey_29 11 "UPDATE parent SET z = 'z' WHERE z = 'f'" 1 +test_efkey_29 12 "UPDATE parent SET z = 'z' WHERE z = 'i'" 1 +test_efkey_29 13 "UPDATE parent SET z = 'z' WHERE z = 'l'" 1 +test_efkey_29 14 "UPDATE parent SET z = 'z' WHERE z = 'o'" 1 +test_efkey_29 15 "UPDATE parent SET z = 'z' WHERE z = 'r'" 1 +test_efkey_29 16 "UPDATE parent SET z = 'z' WHERE z = 'u'" 0 +test_efkey_29 17 "COMMIT" 1 +test_efkey_29 18 "ROLLBACK" 0 + +test_efkey_29 17 "BEGIN" 0 +test_efkey_29 18 "INSERT INTO c1 VALUES(1, 2, 3)" 1 +test_efkey_29 19 "INSERT INTO c2 VALUES(1, 2, 3)" 1 +test_efkey_29 20 "INSERT INTO c3 VALUES(1, 2, 3)" 1 +test_efkey_29 21 "INSERT INTO c4 VALUES(1, 2, 3)" 1 +test_efkey_29 22 "INSERT INTO c5 VALUES(1, 2, 3)" 1 +test_efkey_29 22 "INSERT INTO c6 VALUES(1, 2, 3)" 1 +test_efkey_29 22 "INSERT INTO c7 VALUES(1, 2, 3)" 0 +test_efkey_29 23 "COMMIT" 1 +test_efkey_29 24 "INSERT INTO parent VALUES(1, 2, 3)" 0 +test_efkey_29 25 "COMMIT" 0 + +test_efkey_29 26 "BEGIN" 0 +test_efkey_29 27 "UPDATE c1 SET a = 10" 1 +test_efkey_29 28 "UPDATE c2 SET a = 10" 1 +test_efkey_29 29 "UPDATE c3 SET a = 10" 1 +test_efkey_29 30 "UPDATE c4 SET a = 10" 1 +test_efkey_29 31 "UPDATE c5 SET a = 10" 1 +test_efkey_29 31 "UPDATE c6 SET a = 10" 1 +test_efkey_29 31 "UPDATE c7 SET a = 10" 0 +test_efkey_29 32 "COMMIT" 1 +test_efkey_29 33 "ROLLBACK" 0 + +#------------------------------------------------------------------------- +# /* EV: R-35043-01546 */ +# +# Test an example from foreignkeys.html dealing with a deferred foreign +# key constraint. +# +do_test e_fkey-28.1 { + drop_all_tables + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER REFERENCES artist(artistid) DEFERRABLE INITIALLY DEFERRED + ); + } +} {} +do_test e_fkey-28.2 { + execsql { + BEGIN; + INSERT INTO track VALUES(1, 'White Christmas', 5); + } + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-28.3 { + execsql { + INSERT INTO artist VALUES(5, 'Bing Crosby'); + COMMIT; + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-07223-48323 */ +# +# Verify that a nested savepoint may be released without satisfying +# deferred foreign key constraints. +# +drop_all_tables +do_test e_fkey-30.1 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, + b REFERENCES t1 DEFERRABLE INITIALLY DEFERRED + ); + INSERT INTO t1 VALUES(1, 1); + INSERT INTO t1 VALUES(2, 2); + INSERT INTO t1 VALUES(3, 3); + } +} {} +do_test e_fkey-30.2 { + execsql { + BEGIN; + SAVEPOINT one; + INSERT INTO t1 VALUES(4, 5); + RELEASE one; + } +} {} +do_test e_fkey-30.3 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-30.4 { + execsql { + UPDATE t1 SET a = 5 WHERE a = 4; + COMMIT; + } +} {} + + +#------------------------------------------------------------------------- +# /* EV: R-44295-13823 */ +# +# Check that a transaction savepoint (an outermost savepoint opened when +# the database was in auto-commit mode) cannot be released without +# satisfying deferred foreign key constraints. It may be rolled back. +# +do_test e_fkey-31.1 { + execsql { + SAVEPOINT one; + SAVEPOINT two; + INSERT INTO t1 VALUES(6, 7); + RELEASE two; + } +} {} +do_test e_fkey-31.2 { + catchsql {RELEASE one} +} {1 {foreign key constraint failed}} +do_test e_fkey-31.3 { + execsql { + UPDATE t1 SET a = 7 WHERE a = 6; + RELEASE one; + } +} {} +do_test e_fkey-31.4 { + execsql { + SAVEPOINT one; + SAVEPOINT two; + INSERT INTO t1 VALUES(9, 10); + RELEASE two; + } +} {} +do_test e_fkey-31.5 { + catchsql {RELEASE one} +} {1 {foreign key constraint failed}} +do_test e_fkey-31.6 { + execsql {ROLLBACK TO one ; RELEASE one} +} {} + +#------------------------------------------------------------------------- +# /* EV: R-37736-42616 */ +# +# Test that if a COMMIT operation fails due to deferred foreign key +# constraints, any nested savepoints remain open. +# +do_test e_fkey-32.1 { + execsql { + DELETE FROM t1 WHERE a>3; + SELECT * FROM t1; + } +} {1 1 2 2 3 3} +do_test e_fkey-32.2 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(4, 4); + SAVEPOINT one; + INSERT INTO t1 VALUES(5, 6); + SELECT * FROM t1; + } +} {1 1 2 2 3 3 4 4 5 6} +do_test e_fkey-32.3 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-32.4 { + execsql { + ROLLBACK TO one; + COMMIT; + SELECT * FROM t1; + } +} {1 1 2 2 3 3 4 4} + +do_test e_fkey-32.5 { + execsql { + SAVEPOINT a; + INSERT INTO t1 VALUES(5, 5); + SAVEPOINT b; + INSERT INTO t1 VALUES(6, 7); + SAVEPOINT c; + INSERT INTO t1 VALUES(7, 8); + } +} {} +do_test e_fkey-32.6 { + catchsql {RELEASE a} +} {1 {foreign key constraint failed}} +do_test e_fkey-32.7 { + execsql {ROLLBACK TO c} + catchsql {RELEASE a} +} {1 {foreign key constraint failed}} +do_test e_fkey-32.8 { + execsql { + ROLLBACK TO b; + RELEASE a; + SELECT * FROM t1; + } +} {1 1 2 2 3 3 4 4 5 5} + +########################################################################### +### SECTION 4.3: ON DELETE and ON UPDATE Actions +########################################################################### + +#------------------------------------------------------------------------- +# /* EV: R-48270-44282 */ +# +# Test that configured ON DELETE and ON UPDATE actions take place when +# deleting or modifying rows of the parent table, respectively. +# +# /* EV: R-48124-63225 */ +# +# Test that a single FK constraint may have different actions configured +# for ON DELETE and ON UPDATE. +# +do_test e_fkey-16.1 { + execsql { + CREATE TABLE p(a, b PRIMARY KEY, c); + CREATE TABLE c1(d, e, f DEFAULT 'k0' REFERENCES p + ON UPDATE SET DEFAULT + ON DELETE SET NULL + ); + + INSERT INTO p VALUES(0, 'k0', ''); + INSERT INTO p VALUES(1, 'k1', 'I'); + INSERT INTO p VALUES(2, 'k2', 'II'); + INSERT INTO p VALUES(3, 'k3', 'III'); + + INSERT INTO c1 VALUES(1, 'xx', 'k1'); + INSERT INTO c1 VALUES(2, 'xx', 'k2'); + INSERT INTO c1 VALUES(3, 'xx', 'k3'); + } +} {} +do_test e_fkey-16.2 { + execsql { + UPDATE p SET b = 'k4' WHERE a = 1; + SELECT * FROM c1; + } +} {1 xx k0 2 xx k2 3 xx k3} +do_test e_fkey-16.3 { + execsql { + DELETE FROM p WHERE a = 2; + SELECT * FROM c1; + } +} {1 xx k0 2 xx {} 3 xx k3} +do_test e_fkey-16.4 { + execsql { + CREATE UNIQUE INDEX pi ON p(c); + REPLACE INTO p VALUES(5, 'k5', 'III'); + SELECT * FROM c1; + } +} {1 xx k0 2 xx {} 3 xx {}} + +#------------------------------------------------------------------------- +# /* EV: R-33326-45252 */ +# +# Each foreign key in the system has an ON UPDATE and ON DELETE action, +# either "NO ACTION", "RESTRICT", "SET NULL", "SET DEFAULT" or "CASCADE". +# +# /* EV: R-19803-45884 */ +# +# If none is specified explicitly, "NO ACTION" is the default. +# +drop_all_tables +do_test e_fkey-17.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY, y); + CREATE TABLE child1(a, + b REFERENCES parent ON UPDATE NO ACTION ON DELETE RESTRICT + ); + CREATE TABLE child2(a, + b REFERENCES parent ON UPDATE RESTRICT ON DELETE SET NULL + ); + CREATE TABLE child3(a, + b REFERENCES parent ON UPDATE SET NULL ON DELETE SET DEFAULT + ); + CREATE TABLE child4(a, + b REFERENCES parent ON UPDATE SET DEFAULT ON DELETE CASCADE + ); + + -- Create some foreign keys that use the default action - "NO ACTION" + CREATE TABLE child5(a, b REFERENCES parent ON UPDATE CASCADE); + CREATE TABLE child6(a, b REFERENCES parent ON DELETE RESTRICT); + CREATE TABLE child7(a, b REFERENCES parent ON DELETE NO ACTION); + CREATE TABLE child8(a, b REFERENCES parent ON UPDATE NO ACTION); + } +} {} + +foreach {tn zTab lRes} { + 2 child1 {0 0 parent b {} {NO ACTION} RESTRICT NONE} + 3 child2 {0 0 parent b {} RESTRICT {SET NULL} NONE} + 4 child3 {0 0 parent b {} {SET NULL} {SET DEFAULT} NONE} + 5 child4 {0 0 parent b {} {SET DEFAULT} CASCADE NONE} + 6 child5 {0 0 parent b {} CASCADE {NO ACTION} NONE} + 7 child6 {0 0 parent b {} {NO ACTION} RESTRICT NONE} + 8 child7 {0 0 parent b {} {NO ACTION} {NO ACTION} NONE} + 9 child8 {0 0 parent b {} {NO ACTION} {NO ACTION} NONE} +} { + do_test e_fkey-17.$tn { execsql "PRAGMA foreign_key_list($zTab)" } $lRes +} + +#------------------------------------------------------------------------- +# /* EV: R-19971-54976 */ +# +# Test that "NO ACTION" means that nothing happens to a child row when +# it's parent row is updated or deleted. +# +drop_all_tables +do_test e_fkey-18.1 { + execsql { + CREATE TABLE parent(p1, p2, PRIMARY KEY(p1, p2)); + CREATE TABLE child(c1, c2, + FOREIGN KEY(c1, c2) REFERENCES parent + ON UPDATE NO ACTION + ON DELETE NO ACTION + DEFERRABLE INITIALLY DEFERRED + ); + INSERT INTO parent VALUES('j', 'k'); + INSERT INTO parent VALUES('l', 'm'); + INSERT INTO child VALUES('j', 'k'); + INSERT INTO child VALUES('l', 'm'); + } +} {} +do_test e_fkey-18.2 { + execsql { + BEGIN; + UPDATE parent SET p1='k' WHERE p1='j'; + DELETE FROM parent WHERE p1='l'; + SELECT * FROM child; + } +} {j k l m} +do_test e_fkey-18.3 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-18.4 { + execsql ROLLBACK +} {} + +#------------------------------------------------------------------------- +# /* EV: R-04272-38653 */ +# +# Test that "RESTRICT" means the application is prohibited from deleting +# or updating a parent table row when there exists one or more child keys +# mapped to it. +# +drop_all_tables +do_test e_fkey-18.1 { + execsql { + CREATE TABLE parent(p1, p2); + CREATE UNIQUE INDEX parent_i ON parent(p1, p2); + CREATE TABLE child1(c1, c2, + FOREIGN KEY(c2, c1) REFERENCES parent(p1, p2) ON DELETE RESTRICT + ); + CREATE TABLE child2(c1, c2, + FOREIGN KEY(c2, c1) REFERENCES parent(p1, p2) ON UPDATE RESTRICT + ); + } +} {} +do_test e_fkey-18.2 { + execsql { + INSERT INTO parent VALUES('a', 'b'); + INSERT INTO parent VALUES('c', 'd'); + INSERT INTO child1 VALUES('b', 'a'); + INSERT INTO child2 VALUES('d', 'c'); + } +} {} +do_test e_fkey-18.3 { + catchsql { DELETE FROM parent WHERE p1 = 'a' } +} {1 {foreign key constraint failed}} +do_test e_fkey-18.4 { + catchsql { UPDATE parent SET p2 = 'e' WHERE p1 = 'c' } +} {1 {foreign key constraint failed}} + +#------------------------------------------------------------------------- +# /* EV: R-37997-42187 */ +# +# Test that RESTRICT is slightly different from NO ACTION for IMMEDIATE +# constraints, in that it is enforced immediately, not at the end of the +# statement. +# +drop_all_tables +do_test e_fkey-19.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON UPDATE RESTRICT); + CREATE TABLE child2(c REFERENCES parent ON UPDATE NO ACTION); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + + CREATE TRIGGER parent_t AFTER UPDATE ON parent BEGIN + UPDATE child1 set c = new.x WHERE c = old.x; + UPDATE child2 set c = new.x WHERE c = old.x; + END; + } +} {} +do_test e_fkey-19.2 { + catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-19.3 { + execsql { + UPDATE parent SET x = 'key two' WHERE x = 'key2'; + SELECT * FROM child2; + } +} {{key two}} + +drop_all_tables +do_test e_fkey-19.4 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON DELETE RESTRICT); + CREATE TABLE child2(c REFERENCES parent ON DELETE NO ACTION); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + + CREATE TRIGGER parent_t AFTER DELETE ON parent BEGIN + UPDATE child1 SET c = NULL WHERE c = old.x; + UPDATE child2 SET c = NULL WHERE c = old.x; + END; + } +} {} +do_test e_fkey-19.5 { + catchsql { DELETE FROM parent WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-19.6 { + execsql { + DELETE FROM parent WHERE x = 'key2'; + SELECT * FROM child2; + } +} {{}} + +drop_all_tables +do_test e_fkey-19.7 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON DELETE RESTRICT); + CREATE TABLE child2(c REFERENCES parent ON DELETE NO ACTION); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + } +} {} +do_test e_fkey-19.8 { + catchsql { REPLACE INTO parent VALUES('key1') } +} {1 {foreign key constraint failed}} +do_test e_fkey-19.9 { + execsql { + REPLACE INTO parent VALUES('key2'); + SELECT * FROM child2; + } +} {key2} + +#------------------------------------------------------------------------- +# /* EV: R-24179-60523 */ +# +# Test that RESTRICT is enforced immediately, even for a DEFERRED constraint. +# +drop_all_tables +do_test e_fkey-20.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON UPDATE RESTRICT + DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE child2(c REFERENCES parent ON UPDATE NO ACTION + DEFERRABLE INITIALLY DEFERRED + ); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + BEGIN; + } +} {} +do_test e_fkey-20.2 { + catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-20.3 { + execsql { UPDATE parent SET x = 'key two' WHERE x = 'key2' } +} {} +do_test e_fkey-20.4 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-20.5 { + execsql { + UPDATE child2 SET c = 'key two'; + COMMIT; + } +} {} + +drop_all_tables +do_test e_fkey-20.6 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child1(c REFERENCES parent ON DELETE RESTRICT + DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE child2(c REFERENCES parent ON DELETE NO ACTION + DEFERRABLE INITIALLY DEFERRED + ); + + INSERT INTO parent VALUES('key1'); + INSERT INTO parent VALUES('key2'); + INSERT INTO child1 VALUES('key1'); + INSERT INTO child2 VALUES('key2'); + BEGIN; + } +} {} +do_test e_fkey-20.7 { + catchsql { DELETE FROM parent WHERE x = 'key1' } +} {1 {foreign key constraint failed}} +do_test e_fkey-20.8 { + execsql { DELETE FROM parent WHERE x = 'key2' } +} {} +do_test e_fkey-20.9 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-20.10 { + execsql { + UPDATE child2 SET c = NULL; + COMMIT; + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-03353-05327 */ +# +# Test SET NULL actions. +# +drop_all_tables +do_test e_fkey-21.1 { + execsql { + CREATE TABLE pA(x PRIMARY KEY); + CREATE TABLE cA(c REFERENCES pA ON DELETE SET NULL); + CREATE TABLE cB(c REFERENCES pA ON UPDATE SET NULL); + + INSERT INTO pA VALUES(X'ABCD'); + INSERT INTO pA VALUES(X'1234'); + INSERT INTO cA VALUES(X'ABCD'); + INSERT INTO cB VALUES(X'1234'); + } +} {} +do_test e_fkey-21.2 { + execsql { + DELETE FROM pA WHERE rowid = 1; + SELECT quote(x) FROM pA; + } +} {X'1234'} +do_test e_fkey-21.3 { + execsql { + SELECT quote(c) FROM cA; + } +} {NULL} +do_test e_fkey-21.4 { + execsql { + UPDATE pA SET x = X'8765' WHERE rowid = 2; + SELECT quote(x) FROM pA; + } +} {X'8765'} +do_test e_fkey-21.5 { + execsql { SELECT quote(c) FROM cB } +} {NULL} + +#------------------------------------------------------------------------- +# /* EV: R-43054-54832 */ +# +# Test SET DEFAULT actions. +# +drop_all_tables +do_test e_fkey-22.1 { + execsql { + CREATE TABLE pA(x PRIMARY KEY); + CREATE TABLE cA(c DEFAULT X'0000' REFERENCES pA ON DELETE SET DEFAULT); + CREATE TABLE cB(c DEFAULT X'9999' REFERENCES pA ON UPDATE SET DEFAULT); + + INSERT INTO pA(rowid, x) VALUES(1, X'0000'); + INSERT INTO pA(rowid, x) VALUES(2, X'9999'); + INSERT INTO pA(rowid, x) VALUES(3, X'ABCD'); + INSERT INTO pA(rowid, x) VALUES(4, X'1234'); + + INSERT INTO cA VALUES(X'ABCD'); + INSERT INTO cB VALUES(X'1234'); + } +} {} +do_test e_fkey-22.2 { + execsql { + DELETE FROM pA WHERE rowid = 3; + SELECT quote(x) FROM pA; + } +} {X'0000' X'9999' X'1234'} +do_test e_fkey-22.3 { + execsql { SELECT quote(c) FROM cA } +} {X'0000'} +do_test e_fkey-22.4 { + execsql { + UPDATE pA SET x = X'8765' WHERE rowid = 4; + SELECT quote(x) FROM pA; + } +} {X'0000' X'9999' X'8765'} +do_test e_fkey-22.5 { + execsql { SELECT quote(c) FROM cB } +} {X'9999'} + +#------------------------------------------------------------------------- +# /* EV: R-61376-57267 */ +# /* EV: R-61809-62207 */ +# +# Test ON DELETE CASCADE actions. +# +drop_all_tables +do_test e_fkey-23.1 { + execsql { + CREATE TABLE p1(a, b UNIQUE); + CREATE TABLE c1(c REFERENCES p1(b) ON DELETE CASCADE, d); + INSERT INTO p1 VALUES(NULL, NULL); + INSERT INTO p1 VALUES(4, 4); + INSERT INTO p1 VALUES(5, 5); + INSERT INTO c1 VALUES(NULL, NULL); + INSERT INTO c1 VALUES(4, 4); + INSERT INTO c1 VALUES(5, 5); + SELECT count(*) FROM c1; + } +} {3} +do_test e_fkey-23.2 { + execsql { + DELETE FROM p1 WHERE a = 4; + SELECT d, c FROM c1; + } +} {{} {} 5 5} +do_test e_fkey-23.3 { + execsql { + DELETE FROM p1; + SELECT d, c FROM c1; + } +} {{} {}} +do_test e_fkey-23.4 { + execsql { SELECT * FROM p1 } +} {} + + +#------------------------------------------------------------------------- +# /* EV: R-61376-57267 */ +# /* EV: R-13877-64542 */ +# +# Test ON UPDATE CASCADE actions. +# +drop_all_tables +do_test e_fkey-24.1 { + execsql { + CREATE TABLE p1(a, b UNIQUE); + CREATE TABLE c1(c REFERENCES p1(b) ON UPDATE CASCADE, d); + INSERT INTO p1 VALUES(NULL, NULL); + INSERT INTO p1 VALUES(4, 4); + INSERT INTO p1 VALUES(5, 5); + INSERT INTO c1 VALUES(NULL, NULL); + INSERT INTO c1 VALUES(4, 4); + INSERT INTO c1 VALUES(5, 5); + SELECT count(*) FROM c1; + } +} {3} +do_test e_fkey-24.2 { + execsql { + UPDATE p1 SET b = 10 WHERE b = 5; + SELECT d, c FROM c1; + } +} {{} {} 4 4 5 10} +do_test e_fkey-24.3 { + execsql { + UPDATE p1 SET b = 11 WHERE b = 4; + SELECT d, c FROM c1; + } +} {{} {} 4 11 5 10} +do_test e_fkey-24.4 { + execsql { + UPDATE p1 SET b = 6 WHERE b IS NULL; + SELECT d, c FROM c1; + } +} {{} {} 4 11 5 10} +do_test e_fkey-23.5 { + execsql { SELECT * FROM p1 } +} {{} 6 4 11 5 10} + +#------------------------------------------------------------------------- +# /* EV: R-51329-33438 */ +# +# Test an example from the "ON DELETE and ON UPDATE Actions" section +# of foreignkeys.html. +# +drop_all_tables +do_test e_fkey-15.1 { + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER REFERENCES artist(artistid) ON UPDATE CASCADE + ); + + INSERT INTO artist VALUES(1, 'Dean Martin'); + INSERT INTO artist VALUES(2, 'Frank Sinatra'); + INSERT INTO track VALUES(11, 'That''s Amore', 1); + INSERT INTO track VALUES(12, 'Christmas Blues', 1); + INSERT INTO track VALUES(13, 'My Way', 2); + } +} {} +do_test e_fkey-15.2 { + execsql { + UPDATE artist SET artistid = 100 WHERE artistname = 'Dean Martin'; + } +} {} +do_test e_fkey-15.3 { + execsql { SELECT * FROM artist } +} {2 {Frank Sinatra} 100 {Dean Martin}} +do_test e_fkey-15.4 { + execsql { SELECT * FROM track } +} {11 {That's Amore} 100 12 {Christmas Blues} 100 13 {My Way} 2} + + +#------------------------------------------------------------------------- +# /* EV: R-53968-51642 */ +# +# Verify that adding an FK action does not absolve the user of the +# requirement not to violate the foreign key constraint. +# +drop_all_tables +do_test e_fkey-25.1 { + execsql { + CREATE TABLE parent(a COLLATE nocase, b, c, PRIMARY KEY(c, a)); + CREATE TABLE child(d DEFAULT 'a', e, f DEFAULT 'c', + FOREIGN KEY(f, d) REFERENCES parent ON UPDATE SET DEFAULT + ); + + INSERT INTO parent VALUES('A', 'b', 'c'); + INSERT INTO parent VALUES('ONE', 'two', 'three'); + INSERT INTO child VALUES('one', 'two', 'three'); + } +} {} +do_test e_fkey-25.2 { + execsql { + BEGIN; + UPDATE parent SET a = '' WHERE a = 'oNe'; + SELECT * FROM child; + } +} {a two c} +do_test e_fkey-25.3 { + execsql { + ROLLBACK; + DELETE FROM parent WHERE a = 'A'; + SELECT * FROM parent; + } +} {ONE two three} +do_test e_fkey-25.4 { + catchsql { UPDATE parent SET a = '' WHERE a = 'oNe' } +} {1 {foreign key constraint failed}} + + +#------------------------------------------------------------------------- +# /* EV: R-07065-59588 */ +# /* EV: R-28220-46694 */ +# +# Test an example from the "ON DELETE and ON UPDATE Actions" section +# of foreignkeys.html. This example shows that adding an "ON DELETE DEFAULT" +# clause does not abrogate the need to satisfy the foreign key constraint +# (R-28220-46694). +# +drop_all_tables +do_test e_fkey-14.1 { + execsql { + CREATE TABLE artist( + artistid INTEGER PRIMARY KEY, + artistname TEXT + ); + CREATE TABLE track( + trackid INTEGER, + trackname TEXT, + trackartist INTEGER DEFAULT 0 REFERENCES artist(artistid) ON DELETE SET DEFAULT + ); + INSERT INTO artist VALUES(3, 'Sammy Davis Jr.'); + INSERT INTO track VALUES(14, 'Mr. Bojangles', 3); + } +} {} +do_test e_fkey-14.2 { + catchsql { DELETE FROM artist WHERE artistname = 'Sammy Davis Jr.' } +} {1 {foreign key constraint failed}} +do_test e_fkey-14.3 { + execsql { + INSERT INTO artist VALUES(0, 'Unknown Artist'); + DELETE FROM artist WHERE artistname = 'Sammy Davis Jr.'; + } +} {} +do_test e_fkey-14.4 { + execsql { SELECT * FROM artist } +} {0 {Unknown Artist}} +do_test e_fkey-14.5 { + execsql { SELECT * FROM track } +} {14 {Mr. Bojangles} 0} + +#------------------------------------------------------------------------- +# /* EV: R-09564-22170 */ +# +# Check that the order of steps in an UPDATE or DELETE on a parent +# table is as follows: +# +# 1. Execute applicable BEFORE trigger programs, +# 2. Check local (non foreign key) constraints, +# 3. Update or delete the row in the parent table, +# 4. Perform any required foreign key actions, +# 5. Execute applicable AFTER trigger programs. +# +drop_all_tables +do_test e_fkey-27.1 { + proc maxparent {args} { db one {SELECT max(x) FROM parent} } + db func maxparent maxparent + + execsql { + CREATE TABLE parent(x PRIMARY KEY); + + CREATE TRIGGER bu BEFORE UPDATE ON parent BEGIN + INSERT INTO parent VALUES(new.x-old.x); + END; + CREATE TABLE child( + a DEFAULT (maxparent()) REFERENCES parent ON UPDATE SET DEFAULT + ); + CREATE TRIGGER au AFTER UPDATE ON parent BEGIN + INSERT INTO parent VALUES(new.x+old.x); + END; + + INSERT INTO parent VALUES(1); + INSERT INTO child VALUES(1); + } +} {} +do_test e_fkey-27.2 { + execsql { + UPDATE parent SET x = 22; + SELECT * FROM parent UNION ALL SELECT 'xxx' UNION ALL SELECT a FROM child; + } +} {22 21 23 xxx 22} +do_test e_fkey-27.3 { + execsql { + DELETE FROM child; + DELETE FROM parent; + INSERT INTO parent VALUES(-1); + INSERT INTO child VALUES(-1); + UPDATE parent SET x = 22; + SELECT * FROM parent UNION ALL SELECT 'xxx' UNION ALL SELECT a FROM child; + } +} {22 23 21 xxx 23} + + +#------------------------------------------------------------------------- +# /* EV: R-27383-10246 */ +# +# Verify that ON UPDATE actions only actually take place if the parent key +# is set to a new value that is distinct from the old value. The default +# collation sequence and affinity are used to determine if the new value +# is 'distinct' from the old or not. +# +drop_all_tables +do_test e_fkey-26.1 { + execsql { + CREATE TABLE zeus(a INTEGER COLLATE NOCASE, b, PRIMARY KEY(a, b)); + CREATE TABLE apollo(c, d, + FOREIGN KEY(c, d) REFERENCES zeus ON UPDATE CASCADE + ); + INSERT INTO zeus VALUES('abc', 'xyz'); + INSERT INTO apollo VALUES('ABC', 'xyz'); + } + execsql { + UPDATE zeus SET a = 'aBc'; + SELECT * FROM apollo; + } +} {ABC xyz} +do_test e_fkey-26.2 { + execsql { + UPDATE zeus SET a = 1, b = 1; + SELECT * FROM apollo; + } +} {1 1} +do_test e_fkey-26.3 { + execsql { + UPDATE zeus SET a = 1, b = 1; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 integer 1} +do_test e_fkey-26.4 { + execsql { + UPDATE zeus SET a = '1'; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 integer 1} +do_test e_fkey-26.5 { + execsql { + UPDATE zeus SET b = '1'; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 text 1} +do_test e_fkey-26.6 { + execsql { + UPDATE zeus SET b = NULL; + SELECT typeof(c), c, typeof(d), d FROM apollo; + } +} {integer 1 null {}} + +#------------------------------------------------------------------------- +# /* EV: R-58589-50781 */ +# +# Test an example from the "ON DELETE and ON UPDATE Actions" section +# of foreignkeys.html. This example demonstrates that ON UPDATE actions +# only take place if at least one parent key column is set to a value +# that is distinct from its previous value. +# +drop_all_tables +do_test e_fkey-13.1 { + execsql { + CREATE TABLE parent(x PRIMARY KEY); + CREATE TABLE child(y REFERENCES parent ON UPDATE SET NULL); + INSERT INTO parent VALUES('key'); + INSERT INTO child VALUES('key'); + } +} {} +do_test e_fkey-13.2 { + execsql { + UPDATE parent SET x = 'key'; + SELECT IFNULL(y, 'null') FROM child; + } +} {key} +do_test e_fkey-13.3 { + execsql { + UPDATE parent SET x = 'key2'; + SELECT IFNULL(y, 'null') FROM child; + } +} {null} + +########################################################################### +### SECTION 5: CREATE, ALTER and DROP TABLE commands +########################################################################### + +#------------------------------------------------------------------------- +# /* EV: R-36018-21755 */ +# /* EV: R-25384-39337 */ +# +# Test that parent keys are not checked when tables are created. +# +# Child keys are checked to ensure all component columns exist. If parent +# key columns are explicitly specified, SQLite checks to make sure there +# are the same number of columns in the child and parent keys. (TODO: This +# is tested but does not correspond to any testable statement.) +# +# /* EV: R-08908-23439 */ +# +# Also test that the above statements are true regardless of whether or not +# foreign keys are enabled: "A CREATE TABLE command operates the same whether +# or not foreign key constraints are enabled." +# +foreach {tn zCreateTbl lRes} { + 1 "CREATE TABLE t1(a, b REFERENCES t1)" {0 {}} + 2 "CREATE TABLE t1(a, b REFERENCES t2)" {0 {}} + 3 "CREATE TABLE t1(a, b, FOREIGN KEY(a,b) REFERENCES t1)" {0 {}} + 4 "CREATE TABLE t1(a, b, FOREIGN KEY(a,b) REFERENCES t2)" {0 {}} + 5 "CREATE TABLE t1(a, b, FOREIGN KEY(a,b) REFERENCES t2)" {0 {}} + 6 "CREATE TABLE t1(a, b, FOREIGN KEY(a,b) REFERENCES t2(n,d))" {0 {}} + 7 "CREATE TABLE t1(a, b, FOREIGN KEY(a,b) REFERENCES t1(a,b))" {0 {}} + + A "CREATE TABLE t1(a, b, FOREIGN KEY(c,b) REFERENCES t2)" + {1 {unknown column "c" in foreign key definition}} + B "CREATE TABLE t1(a, b, FOREIGN KEY(c,b) REFERENCES t2(d))" + {1 {number of columns in foreign key does not match the number of columns in the referenced table}} +} { + do_test e_fkey-5.$tn.off { + drop_all_tables + execsql {PRAGMA foreign_keys = OFF} + catchsql $zCreateTbl + } $lRes + do_test e_fkey-5.$tn.on { + drop_all_tables + execsql {PRAGMA foreign_keys = ON} + catchsql $zCreateTbl + } $lRes +} + +#------------------------------------------------------------------------- +# /* EV: R-47952-62498 */ +# +proc test_efkey_6 {tn zAlter isError} { + drop_all_tables + + do_test e_fkey-6.$tn.1 " + execsql { CREATE TABLE tbl(a, b) } + [list catchsql $zAlter] + " [lindex {{0 {}} {1 {Cannot add a REFERENCES column with non-NULL default value}}} $isError] + +} + +test_efkey_6 1 "ALTER TABLE tbl ADD COLUMN c REFERENCES xx" 0 +test_efkey_6 2 "ALTER TABLE tbl ADD COLUMN c DEFAULT NULL REFERENCES xx" 0 +test_efkey_6 3 "ALTER TABLE tbl ADD COLUMN c DEFAULT 0 REFERENCES xx" 1 + +#------------------------------------------------------------------------- +# /* EV: R-47080-02069 */ +# +# Test that ALTER TABLE adjusts REFERENCES clauses when the parent table +# is RENAMED. +# +# /* EV: R-63827-54774 */ +# +# Test that these adjustments are visible in the sqlite_master table. +# +do_test e_fkey-7.1 { + drop_all_tables + execsql { + CREATE TABLE 'p 1 "parent one"'(a REFERENCES 'p 1 "parent one"', b, PRIMARY KEY(b)); + + CREATE TABLE c1(c, d REFERENCES 'p 1 "parent one"' ON UPDATE CASCADE); + CREATE TABLE c2(e, f, FOREIGN KEY(f) REFERENCES 'p 1 "parent one"' ON UPDATE CASCADE); + CREATE TABLE c3(e, 'f col 2', FOREIGN KEY('f col 2') REFERENCES 'p 1 "parent one"' ON UPDATE CASCADE); + + INSERT INTO 'p 1 "parent one"' VALUES(1, 1); + INSERT INTO c1 VALUES(1, 1); + INSERT INTO c2 VALUES(1, 1); + INSERT INTO c3 VALUES(1, 1); + + -- CREATE TABLE q(a, b, PRIMARY KEY(b)); + } +} {} +do_test e_fkey-7.2 { + execsql { ALTER TABLE 'p 1 "parent one"' RENAME TO p } +} {} +do_test e_fkey-7.3 { + execsql { + UPDATE p SET a = 'xxx', b = 'xxx'; + SELECT * FROM p; + SELECT * FROM c1; + SELECT * FROM c2; + SELECT * FROM c3; + } +} {xxx xxx 1 xxx 1 xxx 1 xxx} +do_test e_fkey-7.4 { + execsql { SELECT sql FROM sqlite_master WHERE type = 'table'} +} [list \ + {CREATE TABLE "p"(a REFERENCES "p", b, PRIMARY KEY(b))} \ + {CREATE TABLE c1(c, d REFERENCES "p" ON UPDATE CASCADE)} \ + {CREATE TABLE c2(e, f, FOREIGN KEY(f) REFERENCES "p" ON UPDATE CASCADE)} \ + {CREATE TABLE c3(e, 'f col 2', FOREIGN KEY('f col 2') REFERENCES "p" ON UPDATE CASCADE)} \ +] + +#------------------------------------------------------------------------- +# /* EV: R-14208-23986 */ +# /* EV: R-11078-03945 */ +# +# Check that a DROP TABLE does an implicit DELETE FROM. Which does not +# cause any triggers to fire, but does fire foreign key actions. +# +do_test e_fkey-8.1 { + drop_all_tables + execsql { + CREATE TABLE p(a, b, PRIMARY KEY(a, b)); + + CREATE TABLE c1(c, d, FOREIGN KEY(c, d) REFERENCES p ON DELETE SET NULL); + CREATE TABLE c2(c, d, FOREIGN KEY(c, d) REFERENCES p ON DELETE SET DEFAULT); + CREATE TABLE c3(c, d, FOREIGN KEY(c, d) REFERENCES p ON DELETE CASCADE); + CREATE TABLE c4(c, d, FOREIGN KEY(c, d) REFERENCES p ON DELETE RESTRICT); + CREATE TABLE c5(c, d, FOREIGN KEY(c, d) REFERENCES p ON DELETE NO ACTION); + + CREATE TABLE c6(c, d, + FOREIGN KEY(c, d) REFERENCES p ON DELETE RESTRICT + DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE c7(c, d, + FOREIGN KEY(c, d) REFERENCES p ON DELETE NO ACTION + DEFERRABLE INITIALLY DEFERRED + ); + + CREATE TABLE log(msg); + CREATE TRIGGER tt AFTER DELETE ON p BEGIN + INSERT INTO log VALUES('delete ' || old.rowid); + END; + } +} {} + +do_test e_fkey-8.2 { + execsql { + INSERT INTO p VALUES('a', 'b'); + INSERT INTO c1 VALUES('a', 'b'); + INSERT INTO c2 VALUES('a', 'b'); + INSERT INTO c3 VALUES('a', 'b'); + BEGIN; + DROP TABLE p; + SELECT * FROM c1; + } +} {{} {}} +do_test e_fkey-8.3 { + execsql { SELECT * FROM c2 } +} {{} {}} +do_test e_fkey-8.4 { + execsql { SELECT * FROM c3 } +} {} +do_test e_fkey-8.5 { + execsql { SELECT * FROM log } +} {} +do_test e_fkey-8.6 { + execsql ROLLBACK +} {} +do_test e_fkey-8.7 { + execsql { + BEGIN; + DELETE FROM p; + SELECT * FROM log; + ROLLBACK; + } +} {{delete 1}} + +#------------------------------------------------------------------------- +# /* EV: R-32768-47925 */ +# +# If an IMMEDIATE foreign key fails as a result of a DROP TABLE, the +# DROP TABLE command fails. +# +do_test e_fkey-9.1 { + execsql { + DELETE FROM c1; + DELETE FROM c2; + DELETE FROM c3; + } + execsql { INSERT INTO c5 VALUES('a', 'b') } + catchsql { DROP TABLE p } +} {1 {foreign key constraint failed}} +do_test e_fkey-9.2 { + execsql { SELECT * FROM p } +} {a b} +do_test e_fkey-9.3 { + catchsql { + BEGIN; + DROP TABLE p; + } +} {1 {foreign key constraint failed}} +do_test e_fkey-9.4 { + execsql { + SELECT * FROM p; + SELECT * FROM c5; + ROLLBACK; + } +} {a b a b} + +#------------------------------------------------------------------------- +# /* EV: R-05903-08460 */ +# +# If a DEFERRED foreign key fails as a result of a DROP TABLE, attempting +# to commit the transaction fails unless the violation is fixed. +# +do_test e_fkey-10.1 { + execsql { + DELETE FROM c1 ; DELETE FROM c2 ; DELETE FROM c3 ; + DELETE FROM c4 ; DELETE FROM c5 ; DELETE FROM c6 ; + DELETE FROM c7 + } +} {} +do_test e_fkey-10.2 { + execsql { INSERT INTO c7 VALUES('a', 'b') } + execsql { + BEGIN; + DROP TABLE p; + } +} {} +do_test e_fkey-10.3 { + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-10.4 { + execsql { CREATE TABLE p(a, b, PRIMARY KEY(a, b)) } + catchsql COMMIT +} {1 {foreign key constraint failed}} +do_test e_fkey-10.5 { + execsql { INSERT INTO p VALUES('a', 'b') } + execsql COMMIT +} {} + +#------------------------------------------------------------------------- +# /* EV: R-57242-37005 */ +# +# Any "foreign key mismatch" errors encountered while running an implicit +# "DELETE FROM tbl" are ignored. +# +drop_all_tables +do_test e_fkey-11.1 { + execsql { + PRAGMA foreign_keys = OFF; + + CREATE TABLE p(a PRIMARY KEY, b REFERENCES nosuchtable); + CREATE TABLE c1(c, d, FOREIGN KEY(c, d) REFERENCES a); + CREATE TABLE c2(c REFERENCES p(b), d); + CREATE TABLE c3(c REFERENCES p ON DELETE SET NULL, d); + + INSERT INTO p VALUES(1, 2); + INSERT INTO c1 VALUES(1, 2); + INSERT INTO c2 VALUES(1, 2); + INSERT INTO c3 VALUES(1, 2); + } +} {} +do_test e_fkey-11.2 { + execsql { PRAGMA foreign_keys = ON } + catchsql { DELETE FROM p } +} {1 {no such table: main.nosuchtable}} +do_test e_fkey-11.3 { + execsql { + BEGIN; + DROP TABLE p; + SELECT * FROM c3; + ROLLBACK; + } +} {{} 2} +do_test e_fkey-11.4 { + execsql { CREATE TABLE nosuchtable(x PRIMARY KEY) } + catchsql { DELETE FROM p } +} {1 {foreign key mismatch}} +do_test e_fkey-11.5 { + execsql { DROP TABLE c1 } + catchsql { DELETE FROM p } +} {1 {foreign key mismatch}} +do_test e_fkey-11.6 { + execsql { DROP TABLE c2 } + execsql { DELETE FROM p } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-54142-41346 */ +# +# Test that the special behaviours of ALTER and DROP TABLE are only +# activated when foreign keys are enabled. Special behaviours are: +# +# 1. ADD COLUMN not allowing a REFERENCES clause with a non-NULL +# default value. +# 2. Modifying foreign key definitions when a parent table is RENAMEd. +# 3. Running an implicit DELETE FROM command as part of DROP TABLE. +# +do_test e_fkey-12.1.1 { + drop_all_tables + execsql { CREATE TABLE t1(a, b) } + catchsql { ALTER TABLE t1 ADD COLUMN c DEFAULT 'xxx' REFERENCES t2 } +} {1 {Cannot add a REFERENCES column with non-NULL default value}} +do_test e_fkey-12.1.2 { + execsql { PRAGMA foreign_keys = OFF } + execsql { ALTER TABLE t1 ADD COLUMN c DEFAULT 'xxx' REFERENCES t2 } + execsql { SELECT sql FROM sqlite_master WHERE name = 't1' } +} {{CREATE TABLE t1(a, b, c DEFAULT 'xxx' REFERENCES t2)}} +do_test e_fkey-12.1.3 { + execsql { PRAGMA foreign_keys = ON } +} {} + +do_test e_fkey-12.2.1 { + drop_all_tables + execsql { + CREATE TABLE p(a UNIQUE); + CREATE TABLE c(b REFERENCES p(a)); + BEGIN; + ALTER TABLE p RENAME TO parent; + SELECT sql FROM sqlite_master WHERE name = 'c'; + ROLLBACK; + } +} {{CREATE TABLE c(b REFERENCES "parent"(a))}} +do_test e_fkey-12.2.2 { + execsql { + PRAGMA foreign_keys = OFF; + ALTER TABLE p RENAME TO parent; + SELECT sql FROM sqlite_master WHERE name = 'c'; + } +} {{CREATE TABLE c(b REFERENCES p(a))}} +do_test e_fkey-12.2.3 { + execsql { PRAGMA foreign_keys = ON } +} {} + +do_test e_fkey-12.3.1 { + drop_all_tables + execsql { + CREATE TABLE p(a UNIQUE); + CREATE TABLE c(b REFERENCES p(a) ON DELETE SET NULL); + INSERT INTO p VALUES('x'); + INSERT INTO c VALUES('x'); + BEGIN; + DROP TABLE p; + SELECT * FROM c; + ROLLBACK; + } +} {{}} +do_test e_fkey-12.3.2 { + execsql { + PRAGMA foreign_keys = OFF; + DROP TABLE p; + SELECT * FROM c; + } +} {x} +do_test e_fkey-12.3.3 { + execsql { PRAGMA foreign_keys = ON } +} {} + +########################################################################### +### SECTION 6: Limits and Unsupported Features +########################################################################### + +#------------------------------------------------------------------------- +# /* EV: R-24728-13230 */ +# /* EV: R-24450-46174 */ +# +# Test that MATCH clauses are parsed, but SQLite treats every foreign key +# constraint as if it were "MATCH SIMPLE". +# +foreach zMatch [list SIMPLE PARTIAL FULL Simple parTIAL FuLL ] { + drop_all_tables + do_test e_fkey-1.$zMatch.1 { + execsql " + CREATE TABLE p(a, b, c, PRIMARY KEY(b, c)); + CREATE TABLE c(d, e, f, FOREIGN KEY(e, f) REFERENCES p MATCH $zMatch); + " + } {} + do_test e_fkey-1.$zMatch.2 { + execsql { INSERT INTO p VALUES(1, 2, 3) } + + # MATCH SIMPLE behaviour: Allow any child key that contains one or more + # NULL value to be inserted. Non-NULL values do not have to map to any + # parent key values, so long as at least one field of the child key is + # NULL. + execsql { INSERT INTO c VALUES('w', 2, 3) } + execsql { INSERT INTO c VALUES('x', 'x', NULL) } + execsql { INSERT INTO c VALUES('y', NULL, 'x') } + execsql { INSERT INTO c VALUES('z', NULL, NULL) } + + # Check that the FK is enforced properly if there are no NULL values + # in the child key columns. + catchsql { INSERT INTO c VALUES('a', 2, 4) } + } {1 {foreign key constraint failed}} +} + +#------------------------------------------------------------------------- +# /* EV: R-21599-16038 */ +# +# Test that SQLite does not support the SET CONSTRAINT statement. And +# that it is possible to create both immediate and deferred constraints. +# +drop_all_tables +do_test e_fkey-2.1 { + catchsql { SET CONSTRAINTS ALL IMMEDIATE } +} {1 {near "SET": syntax error}} +do_test e_fkey-2.2 { + catchsql { SET CONSTRAINTS ALL DEFERRED } +} {1 {near "SET": syntax error}} + +do_test e_fkey-2.3 { + execsql { + CREATE TABLE p(a, b, PRIMARY KEY(a, b)); + CREATE TABLE cd(c, d, + FOREIGN KEY(c, d) REFERENCES p DEFERRABLE INITIALLY DEFERRED); + CREATE TABLE ci(c, d, + FOREIGN KEY(c, d) REFERENCES p DEFERRABLE INITIALLY IMMEDIATE); + BEGIN; + } +} {} +do_test e_fkey-2.4 { + catchsql { INSERT INTO ci VALUES('x', 'y') } +} {1 {foreign key constraint failed}} +do_test e_fkey-2.5 { + catchsql { INSERT INTO cd VALUES('x', 'y') } +} {0 {}} +do_test e_fkey-2.6 { + catchsql { COMMIT } +} {1 {foreign key constraint failed}} +do_test e_fkey-2.7 { + execsql { + DELETE FROM cd; + COMMIT; + } +} {} + +#------------------------------------------------------------------------- +# /* EV: R-42264-30503 */ +# +# Test that the maximum recursion depth of foreign key action programs is +# governed by the SQLITE_MAX_TRIGGER_DEPTH and SQLITE_LIMIT_TRIGGER_DEPTH +# settings. +# +proc test_on_delete_recursion {limit} { + drop_all_tables + execsql { + BEGIN; + CREATE TABLE t0(a PRIMARY KEY, b); + INSERT INTO t0 VALUES('x0', NULL); + } + for {set i 1} {$i <= $limit} {incr i} { + execsql " + CREATE TABLE t$i ( + a PRIMARY KEY, b REFERENCES t[expr $i-1] ON DELETE CASCADE + ); + INSERT INTO t$i VALUES('x$i', 'x[expr $i-1]'); + " + } + execsql COMMIT + catchsql " + DELETE FROM t0; + SELECT count(*) FROM t$limit; + " +} +proc test_on_update_recursion {limit} { + drop_all_tables + execsql { + BEGIN; + CREATE TABLE t0(a PRIMARY KEY); + INSERT INTO t0 VALUES('xxx'); + } + for {set i 1} {$i <= $limit} {incr i} { + set j [expr $i-1] + + execsql " + CREATE TABLE t$i (a PRIMARY KEY REFERENCES t$j ON UPDATE CASCADE); + INSERT INTO t$i VALUES('xxx'); + " + } + execsql COMMIT + catchsql " + UPDATE t0 SET a = 'yyy'; + SELECT NOT (a='yyy') FROM t$limit; + " +} + +do_test e_fkey-3.1.1 { + test_on_delete_recursion $SQLITE_MAX_TRIGGER_DEPTH +} {0 0} +do_test e_fkey-3.1.2 { + test_on_delete_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1] +} {1 {too many levels of trigger recursion}} +do_test e_fkey-3.1.3 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5 + test_on_delete_recursion 5 +} {0 0} +do_test e_fkey-3.1.4 { + test_on_delete_recursion 6 +} {1 {too many levels of trigger recursion}} +do_test e_fkey-3.1.5 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000 +} {5} +do_test e_fkey-3.2.1 { + test_on_update_recursion $SQLITE_MAX_TRIGGER_DEPTH +} {0 0} +do_test e_fkey-3.2.2 { + test_on_update_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1] +} {1 {too many levels of trigger recursion}} +do_test e_fkey-3.2.3 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5 + test_on_update_recursion 5 +} {0 0} +do_test e_fkey-3.2.4 { + test_on_update_recursion 6 +} {1 {too many levels of trigger recursion}} +do_test e_fkey-3.2.5 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000 +} {5} + +#------------------------------------------------------------------------- +# /* EV: R-51769-32730 */ +# +# The setting of the recursive_triggers pragma does not affect foreign +# key actions. +# +foreach recursive_triggers_setting [list 0 1 ON OFF] { + drop_all_tables + execsql "PRAGMA recursive_triggers = $recursive_triggers_setting" + + do_test e_fkey-4.$recursive_triggers_setting.1 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1 ON DELETE CASCADE); + INSERT INTO t1 VALUES(1, NULL); + INSERT INTO t1 VALUES(2, 1); + INSERT INTO t1 VALUES(3, 2); + INSERT INTO t1 VALUES(4, 3); + INSERT INTO t1 VALUES(5, 4); + SELECT count(*) FROM t1; + } + } {5} + do_test e_fkey-4.$recursive_triggers_setting.2 { + execsql { SELECT count(*) FROM t1 WHERE a = 1 } + } {1} + do_test e_fkey-4.$recursive_triggers_setting.3 { + execsql { + DELETE FROM t1 WHERE a = 1; + SELECT count(*) FROM t1; + } + } {0} +} + +finish_test Index: test/expr.test ================================================================== --- test/expr.test +++ test/expr.test @@ -163,10 +163,35 @@ if {[working_64bit_int]} { test_expr expr-1.110 {i1=0} {-9223372036854775807/-1} 9223372036854775807 } +test_expr expr-1.111 {i1=NULL, i2=8} {i1 IS i2} 0 +test_expr expr-1.112 {i1=NULL, i2=NULL} {i1 IS i2} 1 +test_expr expr-1.113 {i1=6, i2=NULL} {i1 IS i2} 0 +test_expr expr-1.114 {i1=6, i2=6} {i1 IS i2} 1 +test_expr expr-1.115 {i1=NULL, i2=8} \ + {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no +test_expr expr-1.116 {i1=NULL, i2=NULL} \ + {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes +test_expr expr-1.117 {i1=6, i2=NULL} \ + {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no +test_expr expr-1.118 {i1=8, i2=8} \ + {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes +test_expr expr-1.119 {i1=NULL, i2=8} {i1 IS NOT i2} 1 +test_expr expr-1.120 {i1=NULL, i2=NULL} {i1 IS NOT i2} 0 +test_expr expr-1.121 {i1=6, i2=NULL} {i1 IS NOT i2} 1 +test_expr expr-1.122 {i1=6, i2=6} {i1 IS NOT i2} 0 +test_expr expr-1.123 {i1=NULL, i2=8} \ + {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes +test_expr expr-1.124 {i1=NULL, i2=NULL} \ + {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no +test_expr expr-1.125 {i1=6, i2=NULL} \ + {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes +test_expr expr-1.126 {i1=8, i2=8} \ + {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no + ifcapable floatingpoint { test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57 test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11 test_expr expr-2.3 {r1=1.23, r2=2.34} {r1*r2} 2.8782 } Index: test/fkey1.test ================================================================== --- test/fkey1.test +++ test/fkey1.test @@ -44,11 +44,11 @@ } } {} do_test fkey1-1.2 { execsql { CREATE TABLE t3( - a INTEGER REFERENCES t2 ON INSERT RESTRICT, + a INTEGER REFERENCES t2, b INTEGER REFERENCES t1, FOREIGN KEY (a,b) REFERENCES t2(x,y) ); } } {} @@ -78,24 +78,24 @@ d REFERENCES t5, e REFERENCES t5(c) ); PRAGMA foreign_key_list(t6); } -} [concat \ - {0 0 t5 e c RESTRICT RESTRICT NONE} \ - {1 0 t5 d {} RESTRICT RESTRICT NONE} \ +} [concat \ + {0 0 t5 e c {NO ACTION} {NO ACTION} NONE} \ + {1 0 t5 d {} {NO ACTION} {NO ACTION} NONE} \ ] do_test fkey1-3.2 { execsql { CREATE TABLE t7(d, e, f, FOREIGN KEY (d, e) REFERENCES t5(a, b) ); PRAGMA foreign_key_list(t7); } -} [concat \ - {0 0 t5 d a RESTRICT RESTRICT NONE} \ - {0 1 t5 e b RESTRICT RESTRICT NONE} \ +} [concat \ + {0 0 t5 d a {NO ACTION} {NO ACTION} NONE} \ + {0 1 t5 e b {NO ACTION} {NO ACTION} NONE} \ ] do_test fkey1-3.3 { execsql { CREATE TABLE t8(d, e, f, FOREIGN KEY (d, e) REFERENCES t5 ON DELETE CASCADE ON UPDATE SET NULL ADDED test/fkey2.test Index: test/fkey2.test ================================================================== --- /dev/null +++ test/fkey2.test @@ -0,0 +1,1614 @@ +# 2009 September 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests for foreign keys. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable {!foreignkey||!trigger} { + finish_test + return +} + +#------------------------------------------------------------------------- +# Test structure: +# +# fkey2-1.*: Simple tests to check that immediate and deferred foreign key +# constraints work when not inside a transaction. +# +# fkey2-2.*: Tests to verify that deferred foreign keys work inside +# explicit transactions (i.e that processing really is deferred). +# +# fkey2-3.*: Tests that a statement transaction is rolled back if an +# immediate foreign key constraint is violated. +# +# fkey2-4.*: Test that FK actions may recurse even when recursive triggers +# are disabled. +# +# fkey2-5.*: Check that if foreign-keys are enabled, it is not possible +# to write to an FK column using the incremental blob API. +# +# fkey2-6.*: Test that FK processing is automatically disabled when +# running VACUUM. +# +# fkey2-7.*: Test using an IPK as the key in the child (referencing) table. +# +# fkey2-8.*: Test that enabling/disabling foreign key support while a +# transaction is active is not possible. +# +# fkey2-9.*: Test SET DEFAULT actions. +# +# fkey2-10.*: Test errors. +# +# fkey2-11.*: Test CASCADE actions. +# +# fkey2-12.*: Test RESTRICT actions. +# +# fkey2-13.*: Test that FK processing is performed when a row is REPLACED by +# an UPDATE or INSERT statement. +# +# fkey2-14.*: Test the ALTER TABLE and DROP TABLE commands. +# +# fkey2-15.*: Test that if there are no (known) outstanding foreign key +# constraint violations in the database, inserting into a parent +# table or deleting from a child table does not cause SQLite +# to check if this has repaired an outstanding violation. +# +# fkey2-16.*: Test that rows that refer to themselves may be inserted, +# updated and deleted. +# +# fkey2-17.*: Test that the "count_changes" pragma does not interfere with +# FK constraint processing. +# +# fkey2-18.*: Test that the authorization callback is invoked when processing +# FK constraints. +# +# fkey2-genfkey.*: Tests that were used with the shell tool .genfkey +# command. Recycled to test the built-in implementation. +# + + +execsql { PRAGMA foreign_keys = on } + +set FkeySimpleSchema { + PRAGMA foreign_keys = on; + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(c REFERENCES t1(a) /D/ , d); + + CREATE TABLE t3(a PRIMARY KEY, b); + CREATE TABLE t4(c REFERENCES t3 /D/, d); + + CREATE TABLE t7(a, b INTEGER PRIMARY KEY); + CREATE TABLE t8(c REFERENCES t7 /D/, d); + + CREATE TABLE t9(a REFERENCES nosuchtable, b); + CREATE TABLE t10(a REFERENCES t9(c) /D/, b); +} + + +set FkeySimpleTests { + 1.1 "INSERT INTO t2 VALUES(1, 3)" {1 {foreign key constraint failed}} + 1.2 "INSERT INTO t1 VALUES(1, 2)" {0 {}} + 1.3 "INSERT INTO t2 VALUES(1, 3)" {0 {}} + 1.4 "INSERT INTO t2 VALUES(2, 4)" {1 {foreign key constraint failed}} + 1.5 "INSERT INTO t2 VALUES(NULL, 4)" {0 {}} + 1.6 "UPDATE t2 SET c=2 WHERE d=4" {1 {foreign key constraint failed}} + 1.7 "UPDATE t2 SET c=1 WHERE d=4" {0 {}} + 1.9 "UPDATE t2 SET c=1 WHERE d=4" {0 {}} + 1.10 "UPDATE t2 SET c=NULL WHERE d=4" {0 {}} + 1.11 "DELETE FROM t1 WHERE a=1" {1 {foreign key constraint failed}} + 1.12 "UPDATE t1 SET a = 2" {1 {foreign key constraint failed}} + 1.13 "UPDATE t1 SET a = 1" {0 {}} + + 2.1 "INSERT INTO t4 VALUES(1, 3)" {1 {foreign key constraint failed}} + 2.2 "INSERT INTO t3 VALUES(1, 2)" {0 {}} + 2.3 "INSERT INTO t4 VALUES(1, 3)" {0 {}} + + 4.1 "INSERT INTO t8 VALUES(1, 3)" {1 {foreign key constraint failed}} + 4.2 "INSERT INTO t7 VALUES(2, 1)" {0 {}} + 4.3 "INSERT INTO t8 VALUES(1, 3)" {0 {}} + 4.4 "INSERT INTO t8 VALUES(2, 4)" {1 {foreign key constraint failed}} + 4.5 "INSERT INTO t8 VALUES(NULL, 4)" {0 {}} + 4.6 "UPDATE t8 SET c=2 WHERE d=4" {1 {foreign key constraint failed}} + 4.7 "UPDATE t8 SET c=1 WHERE d=4" {0 {}} + 4.9 "UPDATE t8 SET c=1 WHERE d=4" {0 {}} + 4.10 "UPDATE t8 SET c=NULL WHERE d=4" {0 {}} + 4.11 "DELETE FROM t7 WHERE b=1" {1 {foreign key constraint failed}} + 4.12 "UPDATE t7 SET b = 2" {1 {foreign key constraint failed}} + 4.13 "UPDATE t7 SET b = 1" {0 {}} + 4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {foreign key constraint failed}} + 4.15 "UPDATE t7 SET b = 5" {1 {foreign key constraint failed}} + 4.16 "UPDATE t7 SET rowid = 5" {1 {foreign key constraint failed}} + 4.17 "UPDATE t7 SET a = 10" {0 {}} + + 5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}} + 5.2 "INSERT INTO t10 VALUES(1, 3)" {1 {foreign key mismatch}} +} + +do_test fkey2-1.1.0 { + execsql [string map {/D/ {}} $FkeySimpleSchema] +} {} +foreach {tn zSql res} $FkeySimpleTests { + do_test fkey2-1.1.$tn { catchsql $zSql } $res +} +drop_all_tables + +do_test fkey2-1.2.0 { + execsql [string map {/D/ {DEFERRABLE INITIALLY DEFERRED}} $FkeySimpleSchema] +} {} +foreach {tn zSql res} $FkeySimpleTests { + do_test fkey2-1.2.$tn { catchsql $zSql } $res +} +drop_all_tables + +do_test fkey2-1.3.0 { + execsql [string map {/D/ {}} $FkeySimpleSchema] + execsql { PRAGMA count_changes = 1 } +} {} +foreach {tn zSql res} $FkeySimpleTests { + if {$res == "0 {}"} { set res {0 1} } + do_test fkey2-1.3.$tn { catchsql $zSql } $res +} +execsql { PRAGMA count_changes = 0 } +drop_all_tables + +do_test fkey2-1.4.0 { + execsql [string map {/D/ {}} $FkeySimpleSchema] + execsql { PRAGMA count_changes = 1 } +} {} +foreach {tn zSql res} $FkeySimpleTests { + if {$res == "0 {}"} { set res {0 1} } + execsql BEGIN + do_test fkey2-1.4.$tn { catchsql $zSql } $res + execsql COMMIT +} +execsql { PRAGMA count_changes = 0 } +drop_all_tables + +# Special test: When the parent key is an IPK, make sure the affinity of +# the IPK is not applied to the child key value before it is inserted +# into the child table. +do_test fkey2-1.5.1 { + execsql { + CREATE TABLE i(i INTEGER PRIMARY KEY); + CREATE TABLE j(j REFERENCES i); + INSERT INTO i VALUES(35); + INSERT INTO j VALUES('35.0'); + SELECT j, typeof(j) FROM j; + } +} {35.0 text} +do_test fkey2-1.5.2 { + catchsql { DELETE FROM i } +} {1 {foreign key constraint failed}} + +# Same test using a regular primary key with integer affinity. +drop_all_tables +do_test fkey2-1.6.1 { + execsql { + CREATE TABLE i(i INT UNIQUE); + CREATE TABLE j(j REFERENCES i(i)); + INSERT INTO i VALUES('35.0'); + INSERT INTO j VALUES('35.0'); + SELECT j, typeof(j) FROM j; + SELECT i, typeof(i) FROM i; + } +} {35.0 text 35 integer} +do_test fkey2-1.6.2 { + catchsql { DELETE FROM i } +} {1 {foreign key constraint failed}} + +# Use a collation sequence on the parent key. +drop_all_tables +do_test fkey2-1.7.1 { + execsql { + CREATE TABLE i(i TEXT COLLATE nocase PRIMARY KEY); + CREATE TABLE j(j TEXT COLLATE binary REFERENCES i(i)); + INSERT INTO i VALUES('SQLite'); + INSERT INTO j VALUES('sqlite'); + } + catchsql { DELETE FROM i } +} {1 {foreign key constraint failed}} + +# Use the parent key collation even if it is default and the child key +# has an explicit value. +drop_all_tables +do_test fkey2-1.7.2 { + execsql { + CREATE TABLE i(i TEXT PRIMARY KEY); -- Colseq is "BINARY" + CREATE TABLE j(j TEXT COLLATE nocase REFERENCES i(i)); + INSERT INTO i VALUES('SQLite'); + } + catchsql { INSERT INTO j VALUES('sqlite') } +} {1 {foreign key constraint failed}} +do_test fkey2-1.7.3 { + execsql { + INSERT INTO i VALUES('sqlite'); + INSERT INTO j VALUES('sqlite'); + DELETE FROM i WHERE i = 'SQLite'; + } + catchsql { DELETE FROM i WHERE i = 'sqlite' } +} {1 {foreign key constraint failed}} + +#------------------------------------------------------------------------- +# This section (test cases fkey2-2.*) contains tests to check that the +# deferred foreign key constraint logic works. +# +proc fkey2-2-test {tn nocommit sql {res {}}} { + if {$res eq "FKV"} { + set expected {1 {foreign key constraint failed}} + } else { + set expected [list 0 $res] + } + do_test fkey2-2.$tn [list catchsql $sql] $expected + if {$nocommit} { + do_test fkey2-2.${tn}c { + catchsql COMMIT + } {1 {foreign key constraint failed}} + } +} + +fkey2-2-test 1 0 { + CREATE TABLE node( + nodeid PRIMARY KEY, + parent REFERENCES node DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE leaf( + cellid PRIMARY KEY, + parent REFERENCES node DEFERRABLE INITIALLY DEFERRED + ); +} + +fkey2-2-test 1 0 "INSERT INTO node VALUES(1, 0)" FKV +fkey2-2-test 2 0 "BEGIN" +fkey2-2-test 3 1 "INSERT INTO node VALUES(1, 0)" +fkey2-2-test 4 0 "UPDATE node SET parent = NULL" +fkey2-2-test 5 0 "COMMIT" +fkey2-2-test 6 0 "SELECT * FROM node" {1 {}} + +fkey2-2-test 7 0 "BEGIN" +fkey2-2-test 8 1 "INSERT INTO leaf VALUES('a', 2)" +fkey2-2-test 9 1 "INSERT INTO node VALUES(2, 0)" +fkey2-2-test 10 0 "UPDATE node SET parent = 1 WHERE nodeid = 2" +fkey2-2-test 11 0 "COMMIT" +fkey2-2-test 12 0 "SELECT * FROM node" {1 {} 2 1} +fkey2-2-test 13 0 "SELECT * FROM leaf" {a 2} + +fkey2-2-test 14 0 "BEGIN" +fkey2-2-test 15 1 "DELETE FROM node WHERE nodeid = 2" +fkey2-2-test 16 0 "INSERT INTO node VALUES(2, NULL)" +fkey2-2-test 17 0 "COMMIT" +fkey2-2-test 18 0 "SELECT * FROM node" {1 {} 2 {}} +fkey2-2-test 19 0 "SELECT * FROM leaf" {a 2} + +fkey2-2-test 20 0 "BEGIN" +fkey2-2-test 21 0 "INSERT INTO leaf VALUES('b', 1)" +fkey2-2-test 22 0 "SAVEPOINT save" +fkey2-2-test 23 0 "DELETE FROM node WHERE nodeid = 1" +fkey2-2-test 24 0 "ROLLBACK TO save" +fkey2-2-test 25 0 "COMMIT" +fkey2-2-test 26 0 "SELECT * FROM node" {1 {} 2 {}} +fkey2-2-test 27 0 "SELECT * FROM leaf" {a 2 b 1} + +fkey2-2-test 28 0 "BEGIN" +fkey2-2-test 29 0 "INSERT INTO leaf VALUES('c', 1)" +fkey2-2-test 30 0 "SAVEPOINT save" +fkey2-2-test 31 0 "DELETE FROM node WHERE nodeid = 1" +fkey2-2-test 32 1 "RELEASE save" +fkey2-2-test 33 1 "DELETE FROM leaf WHERE cellid = 'b'" +fkey2-2-test 34 0 "DELETE FROM leaf WHERE cellid = 'c'" +fkey2-2-test 35 0 "COMMIT" +fkey2-2-test 36 0 "SELECT * FROM node" {2 {}} +fkey2-2-test 37 0 "SELECT * FROM leaf" {a 2} + +fkey2-2-test 38 0 "SAVEPOINT outer" +fkey2-2-test 39 1 "INSERT INTO leaf VALUES('d', 3)" +fkey2-2-test 40 1 "RELEASE outer" FKV +fkey2-2-test 41 1 "INSERT INTO leaf VALUES('e', 3)" +fkey2-2-test 42 0 "INSERT INTO node VALUES(3, 2)" +fkey2-2-test 43 0 "RELEASE outer" + +fkey2-2-test 44 0 "SAVEPOINT outer" +fkey2-2-test 45 1 "DELETE FROM node WHERE nodeid=3" +fkey2-2-test 47 0 "INSERT INTO node VALUES(3, 2)" +fkey2-2-test 48 0 "ROLLBACK TO outer" +fkey2-2-test 49 0 "RELEASE outer" + +fkey2-2-test 50 0 "SAVEPOINT outer" +fkey2-2-test 51 1 "INSERT INTO leaf VALUES('f', 4)" +fkey2-2-test 52 1 "SAVEPOINT inner" +fkey2-2-test 53 1 "INSERT INTO leaf VALUES('g', 4)" +fkey2-2-test 54 1 "RELEASE outer" FKV +fkey2-2-test 55 1 "ROLLBACK TO inner" +fkey2-2-test 56 0 "COMMIT" FKV +fkey2-2-test 57 0 "INSERT INTO node VALUES(4, NULL)" +fkey2-2-test 58 0 "RELEASE outer" +fkey2-2-test 59 0 "SELECT * FROM node" {2 {} 3 2 4 {}} +fkey2-2-test 60 0 "SELECT * FROM leaf" {a 2 d 3 e 3 f 4} + +# The following set of tests check that if a statement that affects +# multiple rows violates some foreign key constraints, then strikes a +# constraint that causes the statement-transaction to be rolled back, +# the deferred constraint counter is correctly reset to the value it +# had before the statement-transaction was opened. +# +fkey2-2-test 61 0 "BEGIN" +fkey2-2-test 62 0 "DELETE FROM leaf" +fkey2-2-test 63 0 "DELETE FROM node" +fkey2-2-test 64 1 "INSERT INTO leaf VALUES('a', 1)" +fkey2-2-test 65 1 "INSERT INTO leaf VALUES('b', 2)" +fkey2-2-test 66 1 "INSERT INTO leaf VALUES('c', 1)" +do_test fkey2-2-test-67 { + catchsql "INSERT INTO node SELECT parent, 3 FROM leaf" +} {1 {column nodeid is not unique}} +fkey2-2-test 68 0 "COMMIT" FKV +fkey2-2-test 69 1 "INSERT INTO node VALUES(1, NULL)" +fkey2-2-test 70 0 "INSERT INTO node VALUES(2, NULL)" +fkey2-2-test 71 0 "COMMIT" + +fkey2-2-test 72 0 "BEGIN" +fkey2-2-test 73 1 "DELETE FROM node" +fkey2-2-test 74 0 "INSERT INTO node(nodeid) SELECT DISTINCT parent FROM leaf" +fkey2-2-test 75 0 "COMMIT" + +#------------------------------------------------------------------------- +# Test cases fkey2-3.* test that a program that executes foreign key +# actions (CASCADE, SET DEFAULT, SET NULL etc.) or tests FK constraints +# opens a statement transaction if required. +# +# fkey2-3.1.*: Test UPDATE statements. +# fkey2-3.2.*: Test DELETE statements. +# +drop_all_tables +do_test fkey2-3.1.1 { + execsql { + CREATE TABLE ab(a PRIMARY KEY, b); + CREATE TABLE cd( + c PRIMARY KEY REFERENCES ab ON UPDATE CASCADE ON DELETE CASCADE, + d + ); + CREATE TABLE ef( + e REFERENCES cd ON UPDATE CASCADE, + f, CHECK (e!=5) + ); + } +} {} +do_test fkey2-3.1.2 { + execsql { + INSERT INTO ab VALUES(1, 'b'); + INSERT INTO cd VALUES(1, 'd'); + INSERT INTO ef VALUES(1, 'e'); + } +} {} +do_test fkey2-3.1.3 { + catchsql { UPDATE ab SET a = 5 } +} {1 {constraint failed}} +do_test fkey2-3.1.4 { + execsql { SELECT * FROM ab } +} {1 b} +do_test fkey2-3.1.4 { + execsql BEGIN; + catchsql { UPDATE ab SET a = 5 } +} {1 {constraint failed}} +do_test fkey2-3.1.5 { + execsql COMMIT; + execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef } +} {1 b 1 d 1 e} + +do_test fkey2-3.2.1 { + execsql BEGIN; + catchsql { DELETE FROM ab } +} {1 {foreign key constraint failed}} +do_test fkey2-3.2.2 { + execsql COMMIT + execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef } +} {1 b 1 d 1 e} + +#------------------------------------------------------------------------- +# Test cases fkey2-4.* test that recursive foreign key actions +# (i.e. CASCADE) are allowed even if recursive triggers are disabled. +# +drop_all_tables +do_test fkey2-4.1 { + execsql { + CREATE TABLE t1( + node PRIMARY KEY, + parent REFERENCES t1 ON DELETE CASCADE + ); + CREATE TABLE t2(node PRIMARY KEY, parent); + CREATE TRIGGER t2t AFTER DELETE ON t2 BEGIN + DELETE FROM t2 WHERE parent = old.node; + END; + INSERT INTO t1 VALUES(1, NULL); + INSERT INTO t1 VALUES(2, 1); + INSERT INTO t1 VALUES(3, 1); + INSERT INTO t1 VALUES(4, 2); + INSERT INTO t1 VALUES(5, 2); + INSERT INTO t1 VALUES(6, 3); + INSERT INTO t1 VALUES(7, 3); + INSERT INTO t2 SELECT * FROM t1; + } +} {} +do_test fkey2-4.2 { + execsql { PRAGMA recursive_triggers = off } + execsql { + BEGIN; + DELETE FROM t1 WHERE node = 1; + SELECT node FROM t1; + } +} {} +do_test fkey2-4.3 { + execsql { + DELETE FROM t2 WHERE node = 1; + SELECT node FROM t2; + ROLLBACK; + } +} {4 5 6 7} +do_test fkey2-4.4 { + execsql { PRAGMA recursive_triggers = on } + execsql { + BEGIN; + DELETE FROM t1 WHERE node = 1; + SELECT node FROM t1; + } +} {} +do_test fkey2-4.3 { + execsql { + DELETE FROM t2 WHERE node = 1; + SELECT node FROM t2; + ROLLBACK; + } +} {} + +#------------------------------------------------------------------------- +# Test cases fkey2-5.* verify that the incremental blob API may not +# write to a foreign key column while foreign-keys are enabled. +# +drop_all_tables +ifcapable incrblob { + do_test fkey2-5.1 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1(a)); + INSERT INTO t1 VALUES('hello', 'world'); + INSERT INTO t2 VALUES('key', 'hello'); + } + } {} + do_test fkey2-5.2 { + set rc [catch { set fd [db incrblob t2 b 1] } msg] + list $rc $msg + } {1 {cannot open foreign key column for writing}} + do_test fkey2-5.3 { + set rc [catch { set fd [db incrblob -readonly t2 b 1] } msg] + close $fd + set rc + } {0} + do_test fkey2-5.4 { + execsql { PRAGMA foreign_keys = off } + set rc [catch { set fd [db incrblob t2 b 1] } msg] + close $fd + set rc + } {0} + do_test fkey2-5.5 { + execsql { PRAGMA foreign_keys = on } + } {} +} + +drop_all_tables +ifcapable vacuum { + do_test fkey2-6.1 { + execsql { + CREATE TABLE t1(a REFERENCES t2(c), b); + CREATE TABLE t2(c UNIQUE, b); + INSERT INTO t2 VALUES(1, 2); + INSERT INTO t1 VALUES(1, 2); + VACUUM; + } + } {} +} + +#------------------------------------------------------------------------- +# Test that it is possible to use an INTEGER PRIMARY KEY as the child key +# of a foreign constraint. +# +drop_all_tables +do_test fkey2-7.1 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(c INTEGER PRIMARY KEY REFERENCES t1, b); + } +} {} +do_test fkey2-7.2 { + catchsql { INSERT INTO t2 VALUES(1, 'A'); } +} {1 {foreign key constraint failed}} +do_test fkey2-7.3 { + execsql { + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(2, 3); + INSERT INTO t2 VALUES(1, 'A'); + } +} {} +do_test fkey2-7.4 { + execsql { UPDATE t2 SET c = 2 } +} {} +do_test fkey2-7.5 { + catchsql { UPDATE t2 SET c = 3 } +} {1 {foreign key constraint failed}} +do_test fkey2-7.6 { + catchsql { DELETE FROM t1 WHERE a = 2 } +} {1 {foreign key constraint failed}} +do_test fkey2-7.7 { + execsql { DELETE FROM t1 WHERE a = 1 } +} {} +do_test fkey2-7.8 { + catchsql { UPDATE t1 SET a = 3 } +} {1 {foreign key constraint failed}} +do_test fkey2-7.9 { + catchsql { UPDATE t2 SET rowid = 3 } +} {1 {foreign key constraint failed}} + +#------------------------------------------------------------------------- +# Test that it is not possible to enable/disable FK support while a +# transaction is open. +# +drop_all_tables +proc fkey2-8-test {tn zSql value} { + do_test fkey-2.8.$tn.1 [list execsql $zSql] {} + do_test fkey-2.8.$tn.2 { execsql "PRAGMA foreign_keys" } $value +} +fkey2-8-test 1 { PRAGMA foreign_keys = 0 } 0 +fkey2-8-test 2 { PRAGMA foreign_keys = 1 } 1 +fkey2-8-test 3 { BEGIN } 1 +fkey2-8-test 4 { PRAGMA foreign_keys = 0 } 1 +fkey2-8-test 5 { COMMIT } 1 +fkey2-8-test 6 { PRAGMA foreign_keys = 0 } 0 +fkey2-8-test 7 { BEGIN } 0 +fkey2-8-test 8 { PRAGMA foreign_keys = 1 } 0 +fkey2-8-test 9 { COMMIT } 0 +fkey2-8-test 10 { PRAGMA foreign_keys = 1 } 1 +fkey2-8-test 11 { PRAGMA foreign_keys = off } 0 +fkey2-8-test 12 { PRAGMA foreign_keys = on } 1 +fkey2-8-test 13 { PRAGMA foreign_keys = no } 0 +fkey2-8-test 14 { PRAGMA foreign_keys = yes } 1 +fkey2-8-test 15 { PRAGMA foreign_keys = false } 0 +fkey2-8-test 16 { PRAGMA foreign_keys = true } 1 + +#------------------------------------------------------------------------- +# The following tests, fkey2-9.*, test SET DEFAULT actions. +# +drop_all_tables +do_test fkey2-9.1.1 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE TABLE t2( + c INTEGER PRIMARY KEY, + d INTEGER DEFAULT 1 REFERENCES t1 ON DELETE SET DEFAULT + ); + DELETE FROM t1; + } +} {} +do_test fkey2-9.1.2 { + execsql { + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t2 VALUES(1, 2); + SELECT * FROM t2; + DELETE FROM t1 WHERE a = 2; + SELECT * FROM t2; + } +} {1 2 1 1} +do_test fkey2-9.1.3 { + execsql { + INSERT INTO t1 VALUES(2, 'two'); + UPDATE t2 SET d = 2; + DELETE FROM t1 WHERE a = 1; + SELECT * FROM t2; + } +} {1 2} +do_test fkey2-9.1.4 { + execsql { SELECT * FROM t1 } +} {2 two} +do_test fkey2-9.1.5 { + catchsql { DELETE FROM t1 } +} {1 {foreign key constraint failed}} + +do_test fkey2-9.2.1 { + execsql { + CREATE TABLE pp(a, b, c, PRIMARY KEY(b, c)); + CREATE TABLE cc(d DEFAULT 3, e DEFAULT 1, f DEFAULT 2, + FOREIGN KEY(f, d) REFERENCES pp + ON UPDATE SET DEFAULT + ON DELETE SET NULL + ); + INSERT INTO pp VALUES(1, 2, 3); + INSERT INTO pp VALUES(4, 5, 6); + INSERT INTO pp VALUES(7, 8, 9); + } +} {} +do_test fkey2-9.2.2 { + execsql { + INSERT INTO cc VALUES(6, 'A', 5); + INSERT INTO cc VALUES(6, 'B', 5); + INSERT INTO cc VALUES(9, 'A', 8); + INSERT INTO cc VALUES(9, 'B', 8); + UPDATE pp SET b = 1 WHERE a = 7; + SELECT * FROM cc; + } +} {6 A 5 6 B 5 3 A 2 3 B 2} +do_test fkey2-9.2.3 { + execsql { + DELETE FROM pp WHERE a = 4; + SELECT * FROM cc; + } +} {{} A {} {} B {} 3 A 2 3 B 2} + +#------------------------------------------------------------------------- +# The following tests, fkey2-10.*, test "foreign key mismatch" and +# other errors. +# +set tn 0 +foreach zSql [list { + CREATE TABLE p(a PRIMARY KEY, b); + CREATE TABLE c(x REFERENCES p(c)); +} { + CREATE TABLE c(x REFERENCES v(y)); + CREATE VIEW v AS SELECT x AS y FROM c; +} { + CREATE TABLE p(a, b, PRIMARY KEY(a, b)); + CREATE TABLE c(x REFERENCES p); +} { + CREATE TABLE p(a COLLATE binary, b); + CREATE UNIQUE INDEX i ON p(a COLLATE nocase); + CREATE TABLE c(x REFERENCES p(a)); +}] { + drop_all_tables + do_test fkey2-10.1.[incr tn] { + execsql $zSql + catchsql { INSERT INTO c DEFAULT VALUES } + } {1 {foreign key mismatch}} +} + +# "rowid" cannot be used as part of a child or parent key definition +# unless it happens to be the name of an explicitly declared column. +# +do_test fkey2-10.2.1 { + drop_all_tables + catchsql { + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(c, d, FOREIGN KEY(rowid) REFERENCES t1(a)); + } +} {1 {unknown column "rowid" in foreign key definition}} +do_test fkey2-10.2.2 { + drop_all_tables + catchsql { + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(rowid, d, FOREIGN KEY(rowid) REFERENCES t1(a)); + } +} {0 {}} +do_test fkey2-10.2.1 { + drop_all_tables + catchsql { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d, FOREIGN KEY(c) REFERENCES t1(rowid)); + INSERT INTO t1(rowid, a, b) VALUES(1, 1, 1); + INSERT INTO t2 VALUES(1, 1); + } +} {1 {foreign key mismatch}} +do_test fkey2-10.2.2 { + drop_all_tables + catchsql { + CREATE TABLE t1(rowid PRIMARY KEY, b); + CREATE TABLE t2(c, d, FOREIGN KEY(c) REFERENCES t1(rowid)); + INSERT INTO t1(rowid, b) VALUES(1, 1); + INSERT INTO t2 VALUES(1, 1); + } +} {0 {}} + + +#------------------------------------------------------------------------- +# The following tests, fkey2-11.*, test CASCADE actions. +# +drop_all_tables +do_test fkey2-11.1.1 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE TABLE t2(c, d, FOREIGN KEY(c) REFERENCES t1(a) ON UPDATE CASCADE); + + INSERT INTO t1 VALUES(10, 100); + INSERT INTO t2 VALUES(10, 100); + UPDATE t1 SET a = 15; + SELECT * FROM t2; + } +} {15 100} + +#------------------------------------------------------------------------- +# The following tests, fkey2-12.*, test RESTRICT actions. +# +drop_all_tables +do_test fkey2-12.1.1 { + execsql { + CREATE TABLE t1(a, b PRIMARY KEY); + CREATE TABLE t2( + x REFERENCES t1 ON UPDATE RESTRICT DEFERRABLE INITIALLY DEFERRED + ); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t1 VALUES(3, 'three'); + } +} {} +do_test fkey2-12.1.2 { + execsql "BEGIN" + execsql "INSERT INTO t2 VALUES('two')" +} {} +do_test fkey2-12.1.3 { + execsql "UPDATE t1 SET b = 'four' WHERE b = 'one'" +} {} +do_test fkey2-12.1.4 { + catchsql "UPDATE t1 SET b = 'five' WHERE b = 'two'" +} {1 {foreign key constraint failed}} +do_test fkey2-12.1.5 { + execsql "DELETE FROM t1 WHERE b = 'two'" +} {} +do_test fkey2-12.1.6 { + catchsql "COMMIT" +} {1 {foreign key constraint failed}} +do_test fkey2-12.1.7 { + execsql { + INSERT INTO t1 VALUES(2, 'two'); + COMMIT; + } +} {} + +drop_all_tables +do_test fkey2-12.2.1 { + execsql { + CREATE TABLE t1(x COLLATE NOCASE PRIMARY KEY); + CREATE TRIGGER tt1 AFTER DELETE ON t1 + WHEN EXISTS ( SELECT 1 FROM t2 WHERE old.x = y ) + BEGIN + INSERT INTO t1 VALUES(old.x); + END; + CREATE TABLE t2(y REFERENCES t1); + INSERT INTO t1 VALUES('A'); + INSERT INTO t1 VALUES('B'); + INSERT INTO t2 VALUES('a'); + INSERT INTO t2 VALUES('b'); + + SELECT * FROM t1; + SELECT * FROM t2; + } +} {A B a b} +do_test fkey2-12.2.2 { + execsql { DELETE FROM t1 } + execsql { + SELECT * FROM t1; + SELECT * FROM t2; + } +} {A B a b} +do_test fkey2-12.2.3 { + execsql { + DROP TABLE t2; + CREATE TABLE t2(y REFERENCES t1 ON DELETE RESTRICT); + INSERT INTO t2 VALUES('a'); + INSERT INTO t2 VALUES('b'); + } + catchsql { DELETE FROM t1 } +} {1 {foreign key constraint failed}} +do_test fkey2-12.2.4 { + execsql { + SELECT * FROM t1; + SELECT * FROM t2; + } +} {A B a b} + +drop_all_tables +do_test fkey2-12.3.1 { + execsql { + CREATE TABLE up( + c00, c01, c02, c03, c04, c05, c06, c07, c08, c09, + c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, + c20, c21, c22, c23, c24, c25, c26, c27, c28, c29, + c30, c31, c32, c33, c34, c35, c36, c37, c38, c39, + PRIMARY KEY(c34, c35) + ); + CREATE TABLE down( + c00, c01, c02, c03, c04, c05, c06, c07, c08, c09, + c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, + c20, c21, c22, c23, c24, c25, c26, c27, c28, c29, + c30, c31, c32, c33, c34, c35, c36, c37, c38, c39, + FOREIGN KEY(c39, c38) REFERENCES up ON UPDATE CASCADE + ); + } +} {} +do_test fkey2-12.3.2 { + execsql { + INSERT INTO up(c34, c35) VALUES('yes', 'no'); + INSERT INTO down(c39, c38) VALUES('yes', 'no'); + UPDATE up SET c34 = 'possibly'; + SELECT c38, c39 FROM down; + DELETE FROM down; + } +} {no possibly} +do_test fkey2-12.3.3 { + catchsql { INSERT INTO down(c39, c38) VALUES('yes', 'no') } +} {1 {foreign key constraint failed}} +do_test fkey2-12.3.4 { + execsql { + INSERT INTO up(c34, c35) VALUES('yes', 'no'); + INSERT INTO down(c39, c38) VALUES('yes', 'no'); + } + catchsql { DELETE FROM up WHERE c34 = 'yes' } +} {1 {foreign key constraint failed}} +do_test fkey2-12.3.5 { + execsql { + DELETE FROM up WHERE c34 = 'possibly'; + SELECT c34, c35 FROM up; + SELECT c39, c38 FROM down; + } +} {yes no yes no} + +#------------------------------------------------------------------------- +# The following tests, fkey2-13.*, test that FK processing is performed +# when rows are REPLACEd. +# +drop_all_tables +do_test fkey2-13.1.1 { + execsql { + CREATE TABLE pp(a UNIQUE, b, c, PRIMARY KEY(b, c)); + CREATE TABLE cc(d, e, f UNIQUE, FOREIGN KEY(d, e) REFERENCES pp); + INSERT INTO pp VALUES(1, 2, 3); + INSERT INTO cc VALUES(2, 3, 1); + } +} {} +foreach {tn stmt} { + 1 "REPLACE INTO pp VALUES(1, 4, 5)" + 2 "REPLACE INTO pp(rowid, a, b, c) VALUES(1, 2, 3, 4)" +} { + do_test fkey2-13.1.$tn.1 { + catchsql $stmt + } {1 {foreign key constraint failed}} + do_test fkey2-13.1.$tn.2 { + execsql { + SELECT * FROM pp; + SELECT * FROM cc; + } + } {1 2 3 2 3 1} + do_test fkey2-13.1.$tn.3 { + execsql BEGIN; + catchsql $stmt + } {1 {foreign key constraint failed}} + do_test fkey2-13.1.$tn.4 { + execsql { + COMMIT; + SELECT * FROM pp; + SELECT * FROM cc; + } + } {1 2 3 2 3 1} +} +do_test fkey2-13.1.3 { + execsql { + REPLACE INTO pp(rowid, a, b, c) VALUES(1, 2, 2, 3); + SELECT rowid, * FROM pp; + SELECT * FROM cc; + } +} {1 2 2 3 2 3 1} +do_test fkey2-13.1.4 { + execsql { + REPLACE INTO pp(rowid, a, b, c) VALUES(2, 2, 2, 3); + SELECT rowid, * FROM pp; + SELECT * FROM cc; + } +} {2 2 2 3 2 3 1} + +#------------------------------------------------------------------------- +# The following tests, fkey2-14.*, test that the "DROP TABLE" and "ALTER +# TABLE" commands work as expected wrt foreign key constraints. +# +# fkey2-14.1*: ALTER TABLE ADD COLUMN +# fkey2-14.2*: ALTER TABLE RENAME TABLE +# fkey2-14.3*: DROP TABLE +# +drop_all_tables +ifcapable altertable { + do_test fkey2-14.1.1 { + # Adding a column with a REFERENCES clause is not supported. + execsql { + CREATE TABLE t1(a PRIMARY KEY); + CREATE TABLE t2(a, b); + } + catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } + } {0 {}} + do_test fkey2-14.1.2 { + catchsql { ALTER TABLE t2 ADD COLUMN d DEFAULT NULL REFERENCES t1 } + } {0 {}} + do_test fkey2-14.1.3 { + catchsql { ALTER TABLE t2 ADD COLUMN e REFERENCES t1 DEFAULT NULL} + } {0 {}} + do_test fkey2-14.1.4 { + catchsql { ALTER TABLE t2 ADD COLUMN f REFERENCES t1 DEFAULT 'text'} + } {1 {Cannot add a REFERENCES column with non-NULL default value}} + do_test fkey2-14.1.5 { + catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 } + } {1 {Cannot add a REFERENCES column with non-NULL default value}} + do_test fkey2-14.1.6 { + execsql { + PRAGMA foreign_keys = off; + ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; + PRAGMA foreign_keys = on; + SELECT sql FROM sqlite_master WHERE name='t2'; + } + } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} + + + # Test the sqlite_rename_parent() function directly. + # + proc test_rename_parent {zCreate zOld zNew} { + db eval {SELECT sqlite_rename_parent($zCreate, $zOld, $zNew)} + } + do_test fkey2-14.2.1.1 { + test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 + } {{CREATE TABLE t1(a REFERENCES "t3")}} + do_test fkey2-14.2.1.2 { + test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 + } {{CREATE TABLE t1(a REFERENCES t2)}} + do_test fkey2-14.2.1.3 { + test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 + } {{CREATE TABLE t1(a REFERENCES "t3")}} + + # Test ALTER TABLE RENAME TABLE a bit. + # + do_test fkey2-14.2.2.1 { + drop_all_tables + execsql { + CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1); + CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2); + CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1); + } + execsql { SELECT sql FROM sqlite_master WHERE type = 'table'} + } [list \ + {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1)} \ + {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)} \ + {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \ + ] + do_test fkey2-14.2.2.2 { + execsql { ALTER TABLE t1 RENAME TO t4 } + execsql { SELECT sql FROM sqlite_master WHERE type = 'table'} + } [list \ + {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4")} \ + {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2)} \ + {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \ + ] + do_test fkey2-14.2.2.3 { + catchsql { INSERT INTO t3 VALUES(1, 2, 3) } + } {1 {foreign key constraint failed}} + do_test fkey2-14.2.2.4 { + execsql { INSERT INTO t4 VALUES(1, NULL) } + } {} + do_test fkey2-14.2.2.5 { + catchsql { UPDATE t4 SET b = 5 } + } {1 {foreign key constraint failed}} + do_test fkey2-14.2.2.6 { + catchsql { UPDATE t4 SET b = 1 } + } {0 {}} + do_test fkey2-14.2.2.7 { + execsql { INSERT INTO t3 VALUES(1, NULL, 1) } + } {} +} + +do_test fkey-2.14.3.1 { + drop_all_tables + execsql { + CREATE TABLE t1(a, b REFERENCES nosuchtable); + DROP TABLE t1; + } +} {} +do_test fkey-2.14.3.2 { + execsql { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES('a', 1); + CREATE TABLE t2(x REFERENCES t1); + INSERT INTO t2 VALUES('a'); + } +} {} +do_test fkey-2.14.3.3 { + catchsql { DROP TABLE t1 } +} {1 {foreign key constraint failed}} +do_test fkey-2.14.3.4 { + execsql { + DELETE FROM t2; + DROP TABLE t1; + } +} {} +do_test fkey-2.14.3.4 { + catchsql { INSERT INTO t2 VALUES('x') } +} {1 {no such table: main.t1}} +do_test fkey-2.14.3.5 { + execsql { + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES('x'); + } + execsql { INSERT INTO t2 VALUES('x') } +} {} +do_test fkey-2.14.3.6 { + catchsql { DROP TABLE t1 } +} {1 {foreign key constraint failed}} +do_test fkey-2.14.3.7 { + execsql { + DROP TABLE t2; + DROP TABLE t1; + } +} {} +do_test fkey-2.14.3.8 { + execsql { + CREATE TABLE pp(x, y, PRIMARY KEY(x, y)); + CREATE TABLE cc(a, b, FOREIGN KEY(a, b) REFERENCES pp(x, z)); + } + catchsql { INSERT INTO cc VALUES(1, 2) } +} {1 {foreign key mismatch}} +do_test fkey-2.14.3.9 { + execsql { DROP TABLE cc } +} {} +do_test fkey-2.14.3.10 { + execsql { + CREATE TABLE cc(a, b, + FOREIGN KEY(a, b) REFERENCES pp DEFERRABLE INITIALLY DEFERRED + ); + } + execsql { + INSERT INTO pp VALUES('a', 'b'); + INSERT INTO cc VALUES('a', 'b'); + BEGIN; + DROP TABLE pp; + CREATE TABLE pp(a, b, c, PRIMARY KEY(b, c)); + INSERT INTO pp VALUES(1, 'a', 'b'); + COMMIT; + } +} {} +do_test fkey-2.14.3.11 { + execsql { + BEGIN; + DROP TABLE cc; + DROP TABLE pp; + COMMIT; + } +} {} +do_test fkey-2.14.3.12 { + execsql { + CREATE TABLE b1(a, b); + CREATE TABLE b2(a, b REFERENCES b1); + DROP TABLE b1; + } +} {} +do_test fkey-2.14.3.13 { + execsql { + CREATE TABLE b3(a, b REFERENCES b2 DEFERRABLE INITIALLY DEFERRED); + DROP TABLE b2; + } +} {} + +# Test that nothing goes wrong when dropping a table that refers to a view. +# Or dropping a view that an existing FK (incorrectly) refers to. Or either +# of the above scenarios with a virtual table. +drop_all_tables +do_test fkey-2.14.4.1 { + execsql { + CREATE TABLE t1(x REFERENCES v); + CREATE VIEW v AS SELECT * FROM t1; + } +} {} +do_test fkey-2.14.4.2 { + execsql { + DROP VIEW v; + } +} {} +ifcapable vtab { + register_echo_module db + do_test fkey-2.14.4.3 { + execsql { CREATE VIRTUAL TABLE v USING echo(t1) } + } {} + do_test fkey-2.14.4.2 { + execsql { + DROP TABLE v; + } + } {} +} + +#------------------------------------------------------------------------- +# The following tests, fkey2-15.*, test that unnecessary FK related scans +# and lookups are avoided when the constraint counters are zero. +# +drop_all_tables +proc execsqlS {zSql} { + set ::sqlite_search_count 0 + set ::sqlite_found_count 0 + set res [uplevel [list execsql $zSql]] + concat [expr $::sqlite_found_count + $::sqlite_search_count] $res +} +do_test fkey2-15.1.1 { + execsql { + CREATE TABLE pp(a PRIMARY KEY, b); + CREATE TABLE cc(x, y REFERENCES pp DEFERRABLE INITIALLY DEFERRED); + INSERT INTO pp VALUES(1, 'one'); + INSERT INTO pp VALUES(2, 'two'); + INSERT INTO cc VALUES('neung', 1); + INSERT INTO cc VALUES('song', 2); + } +} {} +do_test fkey2-15.1.2 { + execsqlS { INSERT INTO pp VALUES(3, 'three') } +} {0} +do_test fkey2-15.1.3 { + execsql { + BEGIN; + INSERT INTO cc VALUES('see', 4); -- Violates deferred constraint + } + execsqlS { INSERT INTO pp VALUES(5, 'five') } +} {2} +do_test fkey2-15.1.4 { + execsql { DELETE FROM cc WHERE x = 'see' } + execsqlS { INSERT INTO pp VALUES(6, 'six') } +} {0} +do_test fkey2-15.1.5 { + execsql COMMIT +} {} +do_test fkey2-15.1.6 { + execsql BEGIN + execsqlS { + DELETE FROM cc WHERE x = 'neung'; + ROLLBACK; + } +} {1} +do_test fkey2-15.1.7 { + execsql { + BEGIN; + DELETE FROM pp WHERE a = 2; + } + execsqlS { + DELETE FROM cc WHERE x = 'neung'; + ROLLBACK; + } +} {2} + +#------------------------------------------------------------------------- +# This next block of tests, fkey2-16.*, test that rows that refer to +# themselves may be inserted and deleted. +# +foreach {tn zSchema} { + 1 { CREATE TABLE self(a INTEGER PRIMARY KEY, b REFERENCES self(a)) } + 2 { CREATE TABLE self(a PRIMARY KEY, b REFERENCES self(a)) } + 3 { CREATE TABLE self(a UNIQUE, b INTEGER PRIMARY KEY REFERENCES self(a)) } +} { + drop_all_tables + do_test fkey2-16.1.$tn.1 { + execsql $zSchema + execsql { INSERT INTO self VALUES(13, 13) } + } {} + do_test fkey2-16.1.$tn.2 { + execsql { UPDATE self SET a = 14, b = 14 } + } {} + + do_test fkey2-16.1.$tn.3 { + catchsql { UPDATE self SET b = 15 } + } {1 {foreign key constraint failed}} + + do_test fkey2-16.1.$tn.4 { + catchsql { UPDATE self SET a = 15 } + } {1 {foreign key constraint failed}} + + do_test fkey2-16.1.$tn.5 { + catchsql { UPDATE self SET a = 15, b = 16 } + } {1 {foreign key constraint failed}} + + do_test fkey2-16.1.$tn.6 { + catchsql { UPDATE self SET a = 17, b = 17 } + } {0 {}} + + do_test fkey2-16.1.$tn.7 { + execsql { DELETE FROM self } + } {} + do_test fkey2-16.1.$tn.8 { + catchsql { INSERT INTO self VALUES(20, 21) } + } {1 {foreign key constraint failed}} +} + +#------------------------------------------------------------------------- +# This next block of tests, fkey2-17.*, tests that if "PRAGMA count_changes" +# is turned on statements that violate immediate FK constraints return +# SQLITE_CONSTRAINT immediately, not after returning a number of rows. +# Whereas statements that violate deferred FK constraints return the number +# of rows before failing. +# +# Also test that rows modified by FK actions are not counted in either the +# returned row count or the values returned by sqlite3_changes(). Like +# trigger related changes, they are included in sqlite3_total_changes() though. +# +drop_all_tables +do_test fkey2-17.1.1 { + execsql { PRAGMA count_changes = 1 } + execsql { + CREATE TABLE one(a, b, c, UNIQUE(b, c)); + CREATE TABLE two(d, e, f, FOREIGN KEY(e, f) REFERENCES one(b, c)); + INSERT INTO one VALUES(1, 2, 3); + } +} {1} +do_test fkey2-17.1.2 { + set STMT [sqlite3_prepare_v2 db "INSERT INTO two VALUES(4, 5, 6)" -1 dummy] + sqlite3_step $STMT +} {SQLITE_CONSTRAINT} +do_test fkey2-17.1.3 { + sqlite3_step $STMT +} {SQLITE_MISUSE} +do_test fkey2-17.1.4 { + sqlite3_finalize $STMT +} {SQLITE_CONSTRAINT} +do_test fkey2-17.1.5 { + execsql { + INSERT INTO one VALUES(2, 3, 4); + INSERT INTO one VALUES(3, 4, 5); + INSERT INTO two VALUES(1, 2, 3); + INSERT INTO two VALUES(2, 3, 4); + INSERT INTO two VALUES(3, 4, 5); + } +} {1 1 1 1 1} +do_test fkey2-17.1.6 { + catchsql { + BEGIN; + INSERT INTO one VALUES(0, 0, 0); + UPDATE two SET e=e+1, f=f+1; + } +} {1 {foreign key constraint failed}} +do_test fkey2-17.1.7 { + execsql { SELECT * FROM one } +} {1 2 3 2 3 4 3 4 5 0 0 0} +do_test fkey2-17.1.8 { + execsql { SELECT * FROM two } +} {1 2 3 2 3 4 3 4 5} +do_test fkey2-17.1.9 { + execsql COMMIT +} {} +do_test fkey2-17.1.10 { + execsql { + CREATE TABLE three( + g, h, i, + FOREIGN KEY(h, i) REFERENCES one(b, c) DEFERRABLE INITIALLY DEFERRED + ); + } +} {} +do_test fkey2-17.1.11 { + set STMT [sqlite3_prepare_v2 db "INSERT INTO three VALUES(7, 8, 9)" -1 dummy] + sqlite3_step $STMT +} {SQLITE_ROW} +do_test fkey2-17.1.12 { + sqlite3_column_text $STMT 0 +} {1} +do_test fkey2-17.1.13 { + sqlite3_step $STMT +} {SQLITE_CONSTRAINT} +do_test fkey2-17.1.14 { + sqlite3_finalize $STMT +} {SQLITE_CONSTRAINT} + +drop_all_tables +do_test fkey2-17.2.1 { + execsql { + CREATE TABLE high("a'b!" PRIMARY KEY, b); + CREATE TABLE low( + c, + "d&6" REFERENCES high ON UPDATE CASCADE ON DELETE CASCADE + ); + } +} {} +do_test fkey2-17.2.2 { + execsql { + INSERT INTO high VALUES('a', 'b'); + INSERT INTO low VALUES('b', 'a'); + } + db changes +} {1} +set nTotal [db total_changes] +do_test fkey2-17.2.3 { + execsql { UPDATE high SET "a'b!" = 'c' } +} {1} +do_test fkey2-17.2.4 { + db changes +} {1} +do_test fkey2-17.2.5 { + expr [db total_changes] - $nTotal +} {2} +do_test fkey2-17.2.6 { + execsql { SELECT * FROM high ; SELECT * FROM low } +} {c b b c} +do_test fkey2-17.2.7 { + execsql { DELETE FROM high } +} {1} +do_test fkey2-17.2.8 { + db changes +} {1} +do_test fkey2-17.2.9 { + expr [db total_changes] - $nTotal +} {4} +do_test fkey2-17.2.10 { + execsql { SELECT * FROM high ; SELECT * FROM low } +} {} +execsql { PRAGMA count_changes = 0 } + +#------------------------------------------------------------------------- +# Test that the authorization callback works. +# + +ifcapable auth { + do_test fkey2-18.1 { + execsql { + CREATE TABLE long(a, b PRIMARY KEY, c); + CREATE TABLE short(d, e, f REFERENCES long); + CREATE TABLE mid(g, h, i REFERENCES long DEFERRABLE INITIALLY DEFERRED); + } + } {} + + proc auth {args} {eval lappend ::authargs $args ; return SQLITE_OK} + db auth auth + + # An insert on the parent table must read the child key of any deferred + # foreign key constraints. But not the child key of immediate constraints. + set authargs {} + do_test fkey2-18.2 { + execsql { INSERT INTO long VALUES(1, 2, 3) } + set authargs + } {SQLITE_INSERT long {} main {} SQLITE_READ mid i main {}} + + # An insert on the child table of an immediate constraint must read the + # parent key columns (to see if it is a violation or not). + set authargs {} + do_test fkey2-18.3 { + execsql { INSERT INTO short VALUES(1, 3, 2) } + set authargs + } {SQLITE_INSERT short {} main {} SQLITE_READ long b main {}} + + # As must an insert on the child table of a deferred constraint. + set authargs {} + do_test fkey2-18.4 { + execsql { INSERT INTO mid VALUES(1, 3, 2) } + set authargs + } {SQLITE_INSERT mid {} main {} SQLITE_READ long b main {}} + + do_test fkey2-18.5 { + execsql { + CREATE TABLE nought(a, b PRIMARY KEY, c); + CREATE TABLE cross(d, e, f, + FOREIGN KEY(e) REFERENCES nought(b) ON UPDATE CASCADE + ); + } + execsql { INSERT INTO nought VALUES(2, 1, 2) } + execsql { INSERT INTO cross VALUES(0, 1, 0) } + set authargs [list] + execsql { UPDATE nought SET b = 5 } + set authargs + } {SQLITE_UPDATE nought b main {} SQLITE_READ cross e main {} SQLITE_READ cross e main {} SQLITE_READ nought b main {} SQLITE_READ nought b main {} SQLITE_READ nought b main {} SQLITE_UPDATE cross e main {} SQLITE_READ nought b main {} SQLITE_READ cross e main {} SQLITE_READ nought b main {} SQLITE_READ nought b main {}} + + do_test fkey2-18.6 { + execsql {SELECT * FROM cross} + } {0 5 0} + + do_test fkey2-18.7 { + execsql { + CREATE TABLE one(a INTEGER PRIMARY KEY, b); + CREATE TABLE two(b, c REFERENCES one); + INSERT INTO one VALUES(101, 102); + } + set authargs [list] + execsql { INSERT INTO two VALUES(100, 101); } + set authargs + } {SQLITE_INSERT two {} main {} SQLITE_READ one a main {}} + + # Return SQLITE_IGNORE to requests to read from the parent table. This + # causes inserts of non-NULL keys into the child table to fail. + # + rename auth {} + proc auth {args} { + if {[lindex $args 1] == "long"} {return SQLITE_IGNORE} + return SQLITE_OK + } + do_test fkey2-18.8 { + catchsql { INSERT INTO short VALUES(1, 3, 2) } + } {1 {foreign key constraint failed}} + do_test fkey2-18.9 { + execsql { INSERT INTO short VALUES(1, 3, NULL) } + } {} + do_test fkey2-18.10 { + execsql { SELECT * FROM short } + } {1 3 2 1 3 {}} + do_test fkey2-18.11 { + catchsql { UPDATE short SET f = 2 WHERE f IS NULL } + } {1 {foreign key constraint failed}} + + db auth {} + unset authargs +} + +#------------------------------------------------------------------------- +# The following block of tests, those prefixed with "fkey2-genfkey.", are +# the same tests that were used to test the ".genfkey" command provided +# by the shell tool. So these tests show that the built-in foreign key +# implementation is more or less compatible with the triggers generated +# by genfkey. +# +drop_all_tables +do_test fkey2-genfkey.1.1 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c)); + CREATE TABLE t2(e REFERENCES t1, f); + CREATE TABLE t3(g, h, i, FOREIGN KEY (h, i) REFERENCES t1(b, c)); + } +} {} +do_test fkey2-genfkey.1.2 { + catchsql { INSERT INTO t2 VALUES(1, 2) } +} {1 {foreign key constraint failed}} +do_test fkey2-genfkey.1.3 { + execsql { + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(1, 2); + } +} {} +do_test fkey2-genfkey.1.4 { + execsql { INSERT INTO t2 VALUES(NULL, 3) } +} {} +do_test fkey2-genfkey.1.5 { + catchsql { UPDATE t2 SET e = 5 WHERE e IS NULL } +} {1 {foreign key constraint failed}} +do_test fkey2-genfkey.1.6 { + execsql { UPDATE t2 SET e = 1 WHERE e IS NULL } +} {} +do_test fkey2-genfkey.1.7 { + execsql { UPDATE t2 SET e = NULL WHERE f = 3 } +} {} +do_test fkey2-genfkey.1.8 { + catchsql { UPDATE t1 SET a = 10 } +} {1 {foreign key constraint failed}} +do_test fkey2-genfkey.1.9 { + catchsql { UPDATE t1 SET a = NULL } +} {1 {datatype mismatch}} +do_test fkey2-genfkey.1.10 { + catchsql { DELETE FROM t1 } +} {1 {foreign key constraint failed}} +do_test fkey2-genfkey.1.11 { + execsql { UPDATE t2 SET e = NULL } +} {} +do_test fkey2-genfkey.1.12 { + execsql { + UPDATE t1 SET a = 10; + DELETE FROM t1; + DELETE FROM t2; + } +} {} +do_test fkey2-genfkey.1.13 { + execsql { + INSERT INTO t3 VALUES(1, NULL, NULL); + INSERT INTO t3 VALUES(1, 2, NULL); + INSERT INTO t3 VALUES(1, NULL, 3); + } +} {} +do_test fkey2-genfkey.1.14 { + catchsql { INSERT INTO t3 VALUES(3, 1, 4) } +} {1 {foreign key constraint failed}} +do_test fkey2-genfkey.1.15 { + execsql { + INSERT INTO t1 VALUES(1, 1, 4); + INSERT INTO t3 VALUES(3, 1, 4); + } +} {} +do_test fkey2-genfkey.1.16 { + catchsql { DELETE FROM t1 } +} {1 {foreign key constraint failed}} +do_test fkey2-genfkey.1.17 { + catchsql { UPDATE t1 SET b = 10} +} {1 {foreign key constraint failed}} +do_test fkey2-genfkey.1.18 { + execsql { UPDATE t1 SET a = 10} +} {} +do_test fkey2-genfkey.1.19 { + catchsql { UPDATE t3 SET h = 'hello' WHERE i = 3} +} {1 {foreign key constraint failed}} + +drop_all_tables +do_test fkey2-genfkey.2.1 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c)); + CREATE TABLE t2(e REFERENCES t1 ON UPDATE CASCADE ON DELETE CASCADE, f); + CREATE TABLE t3(g, h, i, + FOREIGN KEY (h, i) + REFERENCES t1(b, c) ON UPDATE CASCADE ON DELETE CASCADE + ); + } +} {} +do_test fkey2-genfkey.2.2 { + execsql { + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(1, 'one'); + INSERT INTO t2 VALUES(4, 'four'); + } +} {} +do_test fkey2-genfkey.2.3 { + execsql { + UPDATE t1 SET a = 2 WHERE a = 1; + SELECT * FROM t2; + } +} {2 one 4 four} +do_test fkey2-genfkey.2.4 { + execsql { + DELETE FROM t1 WHERE a = 4; + SELECT * FROM t2; + } +} {2 one} + +do_test fkey2-genfkey.2.5 { + execsql { + INSERT INTO t3 VALUES('hello', 2, 3); + UPDATE t1 SET c = 2; + SELECT * FROM t3; + } +} {hello 2 2} +do_test fkey2-genfkey.2.6 { + execsql { + DELETE FROM t1; + SELECT * FROM t3; + } +} {} + +drop_all_tables +do_test fkey2-genfkey.3.1 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(c, b)); + CREATE TABLE t2(e REFERENCES t1 ON UPDATE SET NULL ON DELETE SET NULL, f); + CREATE TABLE t3(g, h, i, + FOREIGN KEY (h, i) + REFERENCES t1(b, c) ON UPDATE SET NULL ON DELETE SET NULL + ); + } +} {} +do_test fkey2-genfkey.3.2 { + execsql { + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(1, 'one'); + INSERT INTO t2 VALUES(4, 'four'); + } +} {} +do_test fkey2-genfkey.3.3 { + execsql { + UPDATE t1 SET a = 2 WHERE a = 1; + SELECT * FROM t2; + } +} {{} one 4 four} +do_test fkey2-genfkey.3.4 { + execsql { + DELETE FROM t1 WHERE a = 4; + SELECT * FROM t2; + } +} {{} one {} four} +do_test fkey2-genfkey.3.5 { + execsql { + INSERT INTO t3 VALUES('hello', 2, 3); + UPDATE t1 SET c = 2; + SELECT * FROM t3; + } +} {hello {} {}} +do_test fkey2-genfkey.3.6 { + execsql { + UPDATE t3 SET h = 2, i = 2; + DELETE FROM t1; + SELECT * FROM t3; + } +} {hello {} {}} + +finish_test ADDED test/fkey3.test Index: test/fkey3.test ================================================================== --- /dev/null +++ test/fkey3.test @@ -0,0 +1,80 @@ +# 2009 September 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests for foreign keys. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable {!foreignkey||!trigger} { + finish_test + return +} + +# Create a table and some data to work with. +# +do_test fkey3-1.1 { + execsql { + PRAGMA foreign_keys=ON; + CREATE TABLE t1(x INTEGER PRIMARY KEY); + INSERT INTO t1 VALUES(100); + INSERT INTO t1 VALUES(101); + CREATE TABLE t2(y INTEGER REFERENCES t1 (x)); + INSERT INTO t2 VALUES(100); + INSERT INTO t2 VALUES(101); + SELECT 1, x FROM t1; + SELECT 2, y FROM t2; + } +} {1 100 1 101 2 100 2 101} + +do_test fkey3-1.2 { + catchsql { + DELETE FROM t1 WHERE x=100; + } +} {1 {foreign key constraint failed}} + +do_test fkey3-1.3 { + catchsql { + DROP TABLE t1; + } +} {1 {foreign key constraint failed}} + +do_test fkey3-1.4 { + execsql { + DROP TABLE t2; + } +} {} + +do_test fkey3-1.5 { + execsql { + DROP TABLE t1; + } +} {} + +do_test fkey3-2.1 { + execsql { + PRAGMA foreign_keys=ON; + CREATE TABLE t1(x INTEGER PRIMARY KEY); + INSERT INTO t1 VALUES(100); + INSERT INTO t1 VALUES(101); + CREATE TABLE t2(y INTEGER PRIMARY KEY REFERENCES t1 (x) ON UPDATE SET NULL); + } + execsql { + INSERT INTO t2 VALUES(100); + INSERT INTO t2 VALUES(101); + SELECT 1, x FROM t1; + SELECT 2, y FROM t2; + } +} {1 100 1 101 2 100 2 101} + +finish_test ADDED test/fkey_malloc.test Index: test/fkey_malloc.test ================================================================== --- /dev/null +++ test/fkey_malloc.test @@ -0,0 +1,132 @@ +# 2009 September 22 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !foreignkey||!trigger { + finish_test + return +} +source $testdir/malloc_common.tcl + +do_malloc_test fkey_malloc-1 -sqlprep { + PRAGMA foreign_keys = 1; + CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); + CREATE TABLE t2(x REFERENCES t1 ON UPDATE CASCADE ON DELETE CASCADE); +} -sqlbody { + INSERT INTO t1 VALUES('aaa', 1); + INSERT INTO t2 VALUES('aaa'); + UPDATE t1 SET a = 'bbb'; + DELETE FROM t1; +} + +do_malloc_test fkey_malloc-2 -sqlprep { + PRAGMA foreign_keys = 1; + CREATE TABLE t1(a, b, UNIQUE(a, b)); +} -sqlbody { + CREATE TABLE t2(x, y, + FOREIGN KEY(x, y) REFERENCES t1(a, b) DEFERRABLE INITIALLY DEFERRED + ); + BEGIN; + INSERT INTO t2 VALUES('a', 'b'); + INSERT INTO t1 VALUES('a', 'b'); + UPDATE t1 SET a = 'c'; + DELETE FROM t2; + INSERT INTO t2 VALUES('d', 'b'); + UPDATE t2 SET x = 'c'; + COMMIT; +} + +do_malloc_test fkey_malloc-3 -sqlprep { + PRAGMA foreign_keys = 1; + CREATE TABLE t1(x INTEGER PRIMARY KEY); + CREATE TABLE t2(y DEFAULT 14 REFERENCES t1(x) ON UPDATE SET DEFAULT); + CREATE TABLE t3(y REFERENCES t1 ON UPDATE SET NULL); + INSERT INTO t1 VALUES(13); + INSERT INTO t2 VALUES(13); + INSERT INTO t3 VALUES(13); +} -sqlbody { + UPDATE t1 SET x = 14; +} + +proc catch_fk_error {zSql} { + set rc [catch {db eval $zSql} msg] + if {$rc==0} { + return $msg + } + if {[string match {*foreign key*} $msg]} { + return "" + } + if {$msg eq "out of memory"} { + error 1 + } + error $msg +} + +do_malloc_test fkey_malloc-4 -sqlprep { + PRAGMA foreign_keys = 1; + CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE); + CREATE TABLE t2(z REFERENCES t1(x), a REFERENCES t1(y)); + CREATE TABLE t3(x); + CREATE TABLE t4(z REFERENCES t3); + CREATE TABLE t5(x, y); + CREATE TABLE t6(z REFERENCES t5(x)); + CREATE INDEX i51 ON t5(x); + CREATE INDEX i52 ON t5(y, x); + INSERT INTO t1 VALUES(1, 2); +} -tclbody { + catch_fk_error {INSERT INTO t2 VALUES(1, 3)} + catch_fk_error {INSERT INTO t4 VALUES(2)} + catch_fk_error {INSERT INTO t6 VALUES(2)} +} + +do_malloc_test fkey_malloc-5 -sqlprep { + PRAGMA foreign_keys = 1; + CREATE TABLE t1(x, y, PRIMARY KEY(x, y)); + CREATE TABLE t2(a, b, FOREIGN KEY(a, b) REFERENCES t1 ON UPDATE CASCADE); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t2 VALUES(1, 2); +} -sqlbody { + UPDATE t1 SET x = 5; +} + +do_malloc_test fkey_malloc-6 -sqlprep { + PRAGMA foreign_keys = 1; + CREATE TABLE t1( + x PRIMARY KEY, + y REFERENCES t1 ON DELETE RESTRICT ON UPDATE SET DEFAULT + ); + INSERT INTO t1 VALUES('abc', 'abc'); + INSERT INTO t1 VALUES('def', 'def'); +} -sqlbody { + INSERT INTO t1 VALUES('ghi', 'ghi'); + DELETE FROM t1 WHERE rowid>1; + UPDATE t1 SET x='jkl', y='jkl'; +} + +do_malloc_test fkey_malloc-7 -sqlprep { + PRAGMA foreign_keys = 1; + CREATE TABLE x(a, b, PRIMARY KEY(a, b)); + CREATE TABLE y(c, d, + FOREIGN KEY(d, c) REFERENCES x DEFERRABLE INITIALLY DEFERRED + ); + CREATE TABLE z(e, f, FOREIGN KEY(e, f) REFERENCES x); +} -sqlbody { + DROP TABLE y; + DROP TABLE x; +} + +finish_test + + Index: test/fts3expr.test ================================================================== --- test/fts3expr.test +++ test/fts3expr.test @@ -9,11 +9,11 @@ # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS3 module. # -# $Id: fts3expr.test,v 1.7 2009/03/12 15:43:48 danielk1977 Exp $ +# $Id: fts3expr.test,v 1.9 2009/07/28 16:44:26 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -120,13 +120,16 @@ } {AND {PHRASE 3 0 one} {PHRASE 3 0 two+}} do_test fts3expr-1.14 { test_fts3expr {a:one two} } {AND {PHRASE 0 0 one} {PHRASE 3 0 two}} -do_test fts3expr-1.15 { +do_test fts3expr-1.15.1 { test_fts3expr {one b:two} } {AND {PHRASE 3 0 one} {PHRASE 1 0 two}} +do_test fts3expr-1.15.2 { + test_fts3expr {one B:two} +} {AND {PHRASE 3 0 one} {PHRASE 1 0 two}} do_test fts3expr-1.16 { test_fts3expr {one AND two AND three AND four AND five} } [list AND \ [list AND \ @@ -461,20 +464,36 @@ 12 "(one two OR four five) NOT three" {3 11 19 24 25 26 27} 13 "((((((one two OR four five)))))) NOT three" {3 11 19 24 25 26 27} } { - do_test fts3expr-6.$id { + do_test fts3expr-6.1.$id { + execsql { SELECT rowid FROM t1 WHERE t1 MATCH $expr ORDER BY rowid } + } $res +} + +set sqlite_fts3_enable_parentheses 0 +foreach {id expr res} { + 1 "one -two three" {5 13 21 29} + 2 "-two one three" {5 13 21 29} + 3 "one three -two" {5 13 21 29} + 4 "-one -two three" {4 12 20 28} + 5 "three -one -two" {4 12 20 28} + 6 "-one three -two" {4 12 20 28} +} { + do_test fts3expr-6.2.$id { execsql { SELECT rowid FROM t1 WHERE t1 MATCH $expr ORDER BY rowid } } $res } +set sqlite_fts3_enable_parentheses 1 do_test fts3expr-7.1 { execsql { CREATE VIRTUAL TABLE test USING fts3 (keyword); INSERT INTO test VALUES ('abc'); SELECT * FROM test WHERE keyword MATCH '""'; } } {} + set sqlite_fts3_enable_parentheses 0 finish_test Index: test/func.test ================================================================== --- test/func.test +++ test/func.test @@ -9,11 +9,10 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing built-in functions. # -# $Id: func.test,v 1.93 2009/06/19 16:44:41 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a table to work with. @@ -1155,6 +1154,18 @@ } {1 {wrong number of arguments to function coalesce()}} do_test func-27.3 { catchsql {SELECT coalesce(1,2)} } {0 1} +# Ticket 2d401a94287b5 +# Unknown function in a DEFAULT expression causes a segfault. +# +do_test func-28.1 { + db eval { + CREATE TABLE t28(x, y DEFAULT(nosuchfunc(1))); + } + catchsql { + INSERT INTO t28(x) VALUES(1); + } +} {1 {unknown function: nosuchfunc()}} + finish_test Index: test/icu.test ================================================================== --- test/icu.test +++ test/icu.test @@ -24,16 +24,16 @@ # execsql {CREATE TABLE test1(i1 int, i2 int, r1 real, r2 real, t1 text, t2 text)} execsql {INSERT INTO test1 VALUES(1,2,1.1,2.2,'hello','world')} proc test_expr {name settings expr result} { do_test $name [format { - db one { + lindex [db eval { BEGIN; UPDATE test1 SET %s; SELECT %s FROM test1; ROLLBACK; - } + }] 0 } $settings $expr] $result } # Tests of the REGEXP operator. # Index: test/incrblob.test ================================================================== --- test/incrblob.test +++ test/incrblob.test @@ -307,19 +307,35 @@ set ::blob [db incrblob blobs i 2] } msg ] list $rc $msg } {1 {cannot open value of type null}} -do_test incrblob-4.8 { +do_test incrblob-4.8.1 { execsql { INSERT INTO blobs(k, v, i) VALUES(X'010203040506070809', 'hello', 'world'); } set rc [catch { set ::blob [db incrblob blobs k 3] } msg ] list $rc $msg } {1 {cannot open indexed column for writing}} +do_test incrblob-4.8.2 { + execsql { + CREATE TABLE t3(a INTEGER PRIMARY KEY, b); + INSERT INTO t3 VALUES(1, 2); + } + set rc [catch { + set ::blob [db incrblob -readonly t3 a 1] + } msg ] + list $rc $msg +} {1 {cannot open value of type null}} +do_test incrblob-4.8.3 { + set rc [catch { + set ::blob [db incrblob -readonly t3 rowid 1] + } msg ] + list $rc $msg +} {1 {no such column: "rowid"}} do_test incrblob-4.9.1 { set rc [catch { set ::blob [db incrblob -readonly blobs k 3] } msg] Index: test/incrblob2.test ================================================================== --- test/incrblob2.test +++ test/incrblob2.test @@ -10,11 +10,11 @@ #*********************************************************************** # # Test that it is possible to have two open blob handles on a single # blob object. # -# $Id: incrblob2.test,v 1.10 2009/03/16 13:19:36 danielk1977 Exp $ +# $Id: incrblob2.test,v 1.11 2009/06/29 06:00:37 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -272,18 +272,27 @@ execsql BEGIN db2 catchsql { INSERT INTO t1 VALUES(4, 'pqrst') } db2 } {0 {}} do_test incrblob2-5.5 { - set blob [db incrblob -readonly t1 data 1] - catchsql { INSERT INTO t1 VALUES(5, 'uvwxy') } db2 - } {1 {database table is locked}} + set rc [catch { db incrblob -readonly t1 data 1 } msg] + list $rc $msg + } {1 {database table is locked: t1}} do_test incrblob2-5.6 { - close $blob + execsql { PRAGMA read_uncommitted=1 } + set blob [db incrblob -readonly t1 data 4] + read $blob + } {pqrst} + + do_test incrblob2-5.7 { catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2 } {0 {}} + + do_test incrblob2-5.8 { + close $blob + } {} db2 close db close sqlite3_enable_shared_cache $::enable_shared_cache } Index: test/incrvacuum2.test ================================================================== --- test/incrvacuum2.test +++ test/incrvacuum2.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the incremental vacuum feature. # -# $Id: incrvacuum2.test,v 1.5 2008/05/07 07:13:16 danielk1977 Exp $ +# $Id: incrvacuum2.test,v 1.6 2009/07/25 13:42:50 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # If this build of the library does not support auto-vacuum, omit this @@ -21,20 +21,10 @@ ifcapable {!autovacuum || !pragma} { finish_test return } -# If the OMIT_INCRBLOB symbol was defined at compile time, there -# is no zeroblob() function available. So create a similar -# function here using Tcl. It doesn't return a blob, but it returns -# data of the required length, which is good enough for this -# test file. -ifcapable !incrblob { - proc zeroblob {n} { string repeat 0 $n } - db function zeroblob zeroblob -} - # Create a database in incremental vacuum mode that has many # pages on the freelist. # do_test incrvacuum2-1.1 { ADDED test/init.test Index: test/init.test ================================================================== --- /dev/null +++ test/init.test @@ -0,0 +1,79 @@ +# 2001 September 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements regression tests for SQLite library. The +# focus of this file is testing the effects of a failure in +# sqlite3_initialize(). +# +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +db close + +foreach {t failed rc started} { + 1.1 {} SQLITE_OK {mutex mem pcache} + 1.2 {mutex} SQLITE_ERROR {} + 1.3 {mem} SQLITE_ERROR {mutex} + 1.4 {pcache} SQLITE_ERROR {mutex mem} +} { + do_test init-$t.1 { + eval init_wrapper_install $failed + sqlite3_initialize + } $rc + do_test init-$t.2 { + init_wrapper_query + } $started + do_test init-$t.3 { + sqlite3_shutdown + init_wrapper_query + } {} + do_test init-$t.4 { + sqlite3_initialize + } $rc + do_test init-$t.5 { + init_wrapper_query + } $started + do_test init-$t.6 { + init_wrapper_clear + sqlite3_initialize + } SQLITE_OK + do_test init-$t.7 { + init_wrapper_query + } {mutex mem pcache} + do_test init-$t.8 { + init_wrapper_uninstall + } {} +} + +source $testdir/malloc_common.tcl +if {$MEMDEBUG} { + do_malloc_test init-2 -tclprep { + db close + init_wrapper_install + } -tclbody { + set rc [sqlite3_initialize] + if {[string match "SQLITE*NOMEM" $rc]} {error "out of memory"} + } -cleanup { + set zRepeat "transient" + if {$::iRepeat} {set zRepeat "persistent"} + do_test init-2.$zRepeat.$::n.x { + init_wrapper_clear + sqlite3_initialize + } SQLITE_OK + init_wrapper_uninstall + } +} + +autoinstall_test_functions +finish_test + ADDED test/intarray.test Index: test/intarray.test ================================================================== --- /dev/null +++ test/intarray.test @@ -0,0 +1,109 @@ +# 2009 November 10 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests for the "intarray" object implemented +# in test_intarray.c. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !vtab { + return +} + +do_test intarray-1.0 { + db eval { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + } + for {set i 1} {$i<=999} {incr i} { + set b [format {x%03d} $i] + db eval {INSERT INTO t1(a,b) VALUES($i,$b)} + } + db eval { + CREATE TABLE t2(x INTEGER PRIMARY KEY, y); + INSERT INTO t2 SELECT * FROM t1; + SELECT b FROM t1 WHERE a IN (12,34,56,78) ORDER BY a + } +} {x012 x034 x056 x078} + +do_test intarray-1.1 { + set ia1 [sqlite3_intarray_create db ia1] + set ia2 [sqlite3_intarray_create db ia2] + set ia3 [sqlite3_intarray_create db ia3] + set ia4 [sqlite3_intarray_create db ia4] + db eval { + SELECT type, name FROM sqlite_temp_master + ORDER BY name + } +} {table ia1 table ia2 table ia3 table ia4} + +do_test intarray-1.2 { + db eval { + SELECT b FROM t1 WHERE a IN ia3 ORDER BY a + } +} {} + +do_test intarray-1.3 { + sqlite3_intarray_bind $ia3 45 123 678 + db eval { + SELECT b FROM t1 WHERE a IN ia3 ORDER BY a + } +} {x045 x123 x678} + +do_test intarray-1.4 { + db eval { + SELECT count(b) FROM t1 WHERE a NOT IN ia3 ORDER BY a + } +} {996} + +#explain {SELECT b FROM t1 WHERE a NOT IN ia3} + +do_test intarray-1.5 { + set cmd sqlite3_intarray_bind + lappend cmd $ia1 + for {set i 1} {$i<=999} {incr i} { + lappend cmd $i + lappend cmd [expr {$i+1000}] + lappend cmd [expr {$i+2000}] + } + eval $cmd + db eval { + REPLACE INTO t1 SELECT * FROM t2; + DELETE FROM t1 WHERE a NOT IN ia1; + SELECT count(*) FROM t1; + } +} {999} + +do_test intarray-1.6 { + db eval { + DELETE FROM t1 WHERE a IN ia1; + SELECT count(*) FROM t1; + } +} {0} + +do_test intarray-2.1 { + db eval { + CREATE TEMP TABLE t3(p,q); + INSERT INTO t3 SELECT * FROM t2; + SELECT count(*) FROM t3 WHERE p IN ia1; + } +} {999} + +do_test intarray-2.2 { + set ia5 [sqlite3_intarray_create db ia5] + db eval { + SELECT count(*) FROM t3 WHERE p IN ia1; + } +} {999} + +finish_test Index: test/io.test ================================================================== --- test/io.test +++ test/io.test @@ -11,11 +11,10 @@ # # The focus of this file is testing some specific characteristics of the # IO traffic generated by SQLite (making sure SQLite is not writing out # more database pages than it has to, stuff like that). # -# $Id: io.test,v 1.21 2009/03/28 07:03:42 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl db close @@ -423,15 +422,17 @@ # 3) The database file. # # Normally, when the SAFE_APPEND flag is not set, there is another fsync() # on the journal file between steps (2) and (3) above. # +set expected_sync_count 2 if {$::tcl_platform(platform)=="unix"} { - set expected_sync_count 3 -} else { - set expected_sync_count 2 + ifcapable dirsync { + incr expected_sync_count + } } + do_test io-4.1 { execsql { DELETE FROM abc } nSync execsql { INSERT INTO abc VALUES('a', 'b') } nSync Index: test/join.test ================================================================== --- test/join.test +++ test/join.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for joins, including outer joins. # -# $Id: join.test,v 1.26 2008/12/05 00:00:07 drh Exp $ +# $Id: join.test,v 1.27 2009/07/01 16:12:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_test join-1.1 { @@ -307,14 +307,12 @@ catchsql { SELECT * FROM t1 JOIN t2 USING(d); } } {1 {cannot join using column d - column not present in both tables}} do_test join-3.5 { - catchsql { - SELECT * FROM t1 USING(a); - } -} {0 {1 2 3 2 3 4 3 4 5}} + catchsql { SELECT * FROM t1 USING(a) } +} {1 {a JOIN clause is required before USING}} do_test join-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}} @@ -575,7 +573,72 @@ SELECT * FROM t23 LEFT JOIN (SELECT * FROM t24); } } {1 2 3 {} {} {}} } ;# ifcapable subquery + +#------------------------------------------------------------------------- +# The following tests are to ensure that bug b73fb0bd64 is fixed. +# +do_test join-11.1 { + drop_all_tables + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); + CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT); + INSERT INTO t1 VALUES(1,'abc'); + INSERT INTO t1 VALUES(2,'def'); + INSERT INTO t2 VALUES(1,'abc'); + INSERT INTO t2 VALUES(2,'def'); + SELECT * FROM t1 NATURAL JOIN t2; + } +} {1 abc 2 def} + +do_test join-11.2 { + execsql { SELECT a FROM t1 JOIN t1 USING (a)} +} {1 2} +do_test join-11.3 { + execsql { SELECT a FROM t1 JOIN t1 AS t2 USING (a)} +} {1 2} +do_test join-11.3 { + execsql { SELECT * FROM t1 NATURAL JOIN t1 AS t2} +} {1 abc 2 def} +do_test join-11.4 { + execsql { SELECT * FROM t1 NATURAL JOIN t1 } +} {1 abc 2 def} + +do_test join-11.5 { + drop_all_tables + execsql { + CREATE TABLE t1(a COLLATE nocase, b); + CREATE TABLE t2(a, b); + INSERT INTO t1 VALUES('ONE', 1); + INSERT INTO t1 VALUES('two', 2); + INSERT INTO t2 VALUES('one', 1); + INSERT INTO t2 VALUES('two', 2); + } +} {} +do_test join-11.6 { + execsql { SELECT * FROM t1 NATURAL JOIN t2 } +} {ONE 1 two 2} +do_test join-11.7 { + execsql { SELECT * FROM t2 NATURAL JOIN t1 } +} {two 2} + +do_test join-11.8 { + drop_all_tables + execsql { + CREATE TABLE t1(a, b TEXT); + CREATE TABLE t2(b INTEGER, a); + INSERT INTO t1 VALUES('one', '1.0'); + INSERT INTO t1 VALUES('two', '2'); + INSERT INTO t2 VALUES(1, 'one'); + INSERT INTO t2 VALUES(2, 'two'); + } +} {} +do_test join-11.9 { + execsql { SELECT * FROM t1 NATURAL JOIN t2 } +} {one 1.0 two 2} +do_test join-11.10 { + execsql { SELECT * FROM t2 NATURAL JOIN t1 } +} {1 one 2 two} finish_test ADDED test/lock7.test Index: test/lock7.test ================================================================== --- /dev/null +++ test/lock7.test @@ -0,0 +1,61 @@ +# 2009 August 17 +# +# 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. +# +#*********************************************************************** +# +# Check that reading the database schema from within an active transaction +# does not establish a SHARED lock on the database file if one is not +# already held (or, more accurately, that the SHARED lock is released after +# reading the database schema). +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test lock7-1.1 { + execsql { CREATE TABLE t1(a, b) } + db close + + sqlite3 db1 test.db + sqlite3 db2 test.db + + db1 eval {BEGIN} + db2 eval {BEGIN} +} {} + +do_test lock7-1.2 { + execsql { PRAGMA lock_status } db1 +} {main unlocked temp closed} +do_test lock7-1.3 { + execsql { PRAGMA lock_status } db2 +} {main unlocked temp closed} + +do_test lock7-1.4 { + catchsql { INSERT INTO t1 VALUES(1, 1) } db1 +} {0 {}} +do_test lock7-1.5 { + catchsql { INSERT INTO t1 VALUES(2, 2) } db2 +} {1 {database is locked}} + +do_test lock7-1.6 { + execsql { PRAGMA lock_status } db1 +} {main reserved temp closed} +do_test lock7-1.7 { + execsql { PRAGMA lock_status } db2 +} {main unlocked temp closed} + +do_test lock7-1.8 { + execsql { COMMIT } db1 +} {} + +db1 close +db2 close + +finish_test + Index: test/malloc.test ================================================================== --- test/malloc.test +++ test/malloc.test @@ -863,10 +863,37 @@ } {} db2 close } catch { db2 close } } + +ifcapable stat2 { + do_malloc_test 38 -tclprep { + add_test_collate db 0 0 1 + execsql { + ANALYZE; + CREATE TABLE t4(x COLLATE test_collate); + CREATE INDEX t4x ON t4(x); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 0, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 1, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 2, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 3, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 4, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 5, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 6, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 7, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 8, 'aaa'); + INSERT INTO sqlite_stat2 VALUES('t4', 't4x', 9, 'aaa'); + } + db close + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 0 0 + add_test_collate db 0 0 1 + } -sqlbody { + SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc' + } +} # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close} set sqlite_open_file_count Index: test/mallocI.test ================================================================== --- test/mallocI.test +++ test/mallocI.test @@ -9,11 +9,11 @@ # #*********************************************************************** # # This test script checks malloc failures in various obscure operations. # -# $Id: mallocI.test,v 1.1 2008/08/02 03:50:39 drh Exp $ +# $Id: mallocI.test,v 1.3 2009/08/10 04:26:39 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl @@ -37,7 +37,27 @@ do_malloc_test mallocI-3 -sqlprep { CREATE TABLE t1(a,b,c); } -sqlbody { CREATE TABLE t2 AS SELECT b,c FROM t1; } + +# This tests that a malloc failure that occurs while passing the schema +# does not result in a SHARED lock being left on the database file. +# +do_malloc_test mallocI-4 -tclprep { + sqlite3 db2 test.db + db2 eval { + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(a, b, c); + } +} -sqlbody { + SELECT * FROM t1 +} -cleanup { + do_test mallocI-4.$::n.2 { + # If this INSERT is possible then [db] does not hold a shared lock + # on the database file. + catchsql { INSERT INTO t1 VALUES(1, 2, 3) } db2 + } {0 {}} +} +catch { db2 close } finish_test Index: test/memsubsys1.test ================================================================== --- test/memsubsys1.test +++ test/memsubsys1.test @@ -9,11 +9,11 @@ # #*********************************************************************** # # This file contains tests of the memory allocation subsystem # -# $Id: memsubsys1.test,v 1.15 2009/04/11 14:46:43 drh Exp $ +# $Id: memsubsys1.test,v 1.17 2009/07/18 14:36:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl sqlite3_reset_auto_extension Index: test/misc2.test ================================================================== --- test/misc2.test +++ test/misc2.test @@ -16,10 +16,15 @@ # $Id: misc2.test,v 1.28 2007/09/12 17:01:45 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl +# The tests in this file were written before SQLite supported recursive +# trigger invocation, and some tests depend on that to pass. So disable +# recursive triggers for this file. +catchsql { pragma recursive_triggers = off } + ifcapable {trigger} { # Test for ticket #360 # do_test misc2-1.1 { catchsql { @@ -356,10 +361,11 @@ } db close file delete -force test.db sqlite3 db test.db +catchsql { pragma recursive_triggers = off } # Ticket #453. If the SQL ended with "-", the tokenizer was calling that # an incomplete token, which caused problem. The solution was to just call # it a minus sign. # Index: test/misc7.test ================================================================== --- test/misc7.test +++ test/misc7.test @@ -8,11 +8,11 @@ # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # -# $Id: misc7.test,v 1.28 2009/02/10 05:45:42 danielk1977 Exp $ +# $Id: misc7.test,v 1.29 2009/07/16 18:21:18 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_test misc7-1-misuse { @@ -364,20 +364,10 @@ execsql { SELECT count(*) FROM t3; } } {32} -set sqlite_pager_n_sort_bucket 4 -do_test misc7-17 { - execsql { - PRAGMA integrity_check; - VACUUM; - PRAGMA integrity_check; - } -} {ok ok} -set sqlite_pager_n_sort_bucket 0 - #---------------------------------------------------------------------- # Test the situation where a hot-journal is discovered but write-access # to it is denied. This should return SQLITE_BUSY. # # These tests do not work on windows due to restrictions in the Index: test/nan.test ================================================================== --- test/nan.test +++ test/nan.test @@ -311,10 +311,10 @@ do_test nan-4.20 { db eval {DELETE FROM t1} set big [string repeat 9 10000].0e-9000 db eval "INSERT INTO t1 VALUES($big)" db eval {SELECT x, typeof(x) FROM t1} -} {{} null} +} {inf real} finish_test Index: test/notnull.test ================================================================== --- test/notnull.test +++ test/notnull.test @@ -499,7 +499,41 @@ INSERT INTO t1 VALUES(1,2,3,4,5); UPDATE t1 SET e=null, a=b, b=a; SELECT * FROM t1 ORDER BY a; } } {1 {t1.e may not be NULL}} + +# Test that bug 29ab7be99f is fixed. +# +do_test notnull-5.1 { + execsql { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a, b NOT NULL); + CREATE TABLE t2(c, d); + INSERT INTO t2 VALUES(3, 4); + INSERT INTO t2 VALUES(5, NULL); + } +} {} +do_test notnull-5.2 { + catchsql { + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 SELECT * FROM t2; + } +} {1 {t1.b may not be NULL}} +do_test notnull-5.3 { + execsql { SELECT * FROM t1 } +} {1 2} +do_test notnull-5.4 { + catchsql { + DELETE FROM t1; + BEGIN; + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 SELECT * FROM t2; + COMMIT; + } +} {1 {t1.b may not be NULL}} +do_test notnull-5.5 { + execsql { SELECT * FROM t1 } +} {1 2} finish_test + DELETED test/pager.test Index: test/pager.test ================================================================== --- test/pager.test +++ /dev/null @@ -1,582 +0,0 @@ -# 2001 September 15 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this script is page cache subsystem. -# -# $Id: pager.test,v 1.35 2009/06/05 17:09:12 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -if {[info commands pager_open]!=""} { -db close - -# Basic sanity check. Open and close a pager. -# -do_test pager-1.0 { - catch {file delete -force ptf1.db} - catch {file delete -force ptf1.db-journal} - set v [catch { - set ::p1 [pager_open ptf1.db 10] - } msg] -} {0} -do_test pager-1.1 { - pager_stats $::p1 -} {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0} -do_test pager-1.2 { - pager_pagecount $::p1 -} {0} -do_test pager-1.3 { - pager_stats $::p1 -} {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0} -do_test pager-1.4 { - pager_close $::p1 -} {} - -# Try to write a few pages. -# -do_test pager-2.1 { - set v [catch { - set ::p1 [pager_open ptf1.db 10] - } msg] -} {0} -#do_test pager-2.2 { -# set v [catch { -# set ::g1 [page_get $::p1 0] -# } msg] -# lappend v $msg -#} {1 SQLITE_ERROR} -do_test pager-2.3.1 { - set ::gx [page_lookup $::p1 1] -} {} -do_test pager-2.3.2 { - pager_stats $::p1 -} {ref 0 page 0 max 10 size -1 state 0 err 0 hit 0 miss 0 ovfl 0} -do_test pager-2.3.3 { - set v [catch { - set ::g1 [page_get $::p1 1] - } msg] - if {$v} {lappend v $msg} - set v -} {0} -do_test pager-2.3.3 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0} -do_test pager-2.3.4 { - set ::gx [page_lookup $::p1 1] - expr {$::gx!=""} -} {1} -do_test pager-2.3.5 { - page_unref $::gx - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0} -do_test pager-2.3.6 { - expr {$::g1==$::gx} -} {1} -do_test pager-2.3.7 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0} -do_test pager-2.4 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0} -do_test pager-2.5 { - pager_pagecount $::p1 -} {0} -do_test pager-2.6 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 0 miss 1 ovfl 0} -do_test pager-2.7 { - page_number $::g1 -} {1} -do_test pager-2.8 { - page_read $::g1 -} {} -do_test pager-2.9 { - page_unref $::g1 -} {} - -# Update 24/03/2007: Even though the ref-count has dropped to zero, the -# pager-cache still contains some pages. Previously, it was always true -# that if there were no references to a pager it was empty. -do_test pager-2.10 { - pager_stats $::p1 -} {ref 0 page 1 max 10 size -1 state 0 err 0 hit 0 miss 1 ovfl 0} -do_test pager-2.11 { - set ::g1 [page_get $::p1 1] - expr {$::g1!=0} -} {1} -do_test pager-2.12 { - page_number $::g1 -} {1} -do_test pager-2.13 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 1 err 0 hit 1 miss 1 ovfl 0} -do_test pager-2.14 { - set v [catch { - page_write $::g1 "Page-One" - } msg] - lappend v $msg -} {0 {}} -do_test pager-2.15 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 1 state 2 err 0 hit 1 miss 1 ovfl 0} -do_test pager-2.16 { - page_read $::g1 -} {Page-One} -do_test pager-2.17 { - set v [catch { - pager_commit $::p1 - } msg] - lappend v $msg -} {0 {}} -do_test pager-2.20 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size -1 state 1 err 0 hit 2 miss 1 ovfl 0} -do_test pager-2.19 { - pager_pagecount $::p1 -} {1} -do_test pager-2.21 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 1 state 1 err 0 hit 2 miss 1 ovfl 0} -do_test pager-2.22 { - page_unref $::g1 -} {} -do_test pager-2.23 { - pager_stats $::p1 -} {ref 0 page 1 max 10 size -1 state 0 err 0 hit 2 miss 1 ovfl 0} -do_test pager-2.24 { - set v [catch { - page_get $::p1 1 - } ::g1] - if {$v} {lappend v $::g1} - set v -} {0} -do_test pager-2.25 { - page_read $::g1 -} {Page-One} -do_test pager-2.26 { - set v [catch { - page_write $::g1 {page-one} - } msg] - lappend v $msg -} {0 {}} -do_test pager-2.27 { - page_read $::g1 -} {page-one} -do_test pager-2.28 { - set v [catch { - pager_rollback $::p1 - } msg] - lappend v $msg -} {0 {}} -do_test pager-2.29 { - page_unref $::g1 - set ::g1 [page_get $::p1 1] - page_read $::g1 -} {Page-One} -do_test pager-2.99 { - page_unref $::g1 - pager_close $::p1 -} {} - -do_test pager-3.1 { - set v [catch { - set ::p1 [pager_open ptf1.db 15] - } msg] - if {$v} {lappend v $msg} - set v -} {0} -do_test pager-3.2 { - pager_pagecount $::p1 -} {1} -do_test pager-3.3 { - set v [catch { - set ::g(1) [page_get $::p1 1] - } msg] - if {$v} {lappend v $msg} - set v -} {0} -do_test pager-3.4 { - page_read $::g(1) -} {Page-One} -do_test pager-3.5 { - for {set i 2} {$i<=20} {incr i} { - set gx [page_get $::p1 $i] - page_write $gx "Page-$i" - page_unref $gx - } - pager_commit $::p1 - page_unref $::g(1) -} {} -for {set i 2} {$i<=20} {incr i} { - do_test pager-3.6.[expr {$i-1}] [subst { - set gx \[page_get $::p1 $i\] - set v \[page_read \$gx\] - page_unref \$gx - set v - }] "Page-$i" -} -for {set i 1} {$i<=20} {incr i} { - regsub -all CNT { - set ::g1 [page_get $::p1 CNT] - set ::g2 [page_get $::p1 CNT] - set ::vx [page_read $::g2] - expr {$::g1==$::g2} - } $i body; - do_test pager-3.7.$i.1 $body {1} - regsub -all CNT { - page_unref $::g2 - set vy [page_read $::g1] - expr {$vy==$::vx} - } $i body; - do_test pager-3.7.$i.2 $body {1} - regsub -all CNT { - page_unref $::g1 - set gx [page_get $::p1 CNT] - set vy [page_read $gx] - page_unref $gx - expr {$vy==$::vx} - } $i body; - do_test pager-3.7.$i.3 $body {1} -} -do_test pager-3.99 { - pager_close $::p1 -} {} - -# tests of the checkpoint mechanism and api -# -do_test pager-4.0 { - set v [catch { - file delete -force ptf1.db - set ::p1 [pager_open ptf1.db 15] - } msg] - if {$v} {lappend v $msg} - set v -} {0} -do_test pager-4.1 { - set g1 [page_get $::p1 1] - page_write $g1 "Page-1 v0" - for {set i 2} {$i<=20} {incr i} { - set gx [page_get $::p1 $i] - page_write $gx "Page-$i v0" - page_unref $gx - } - pager_commit $::p1 -} {} -for {set i 1} {$i<=20} {incr i} { - do_test pager-4.2.$i { - set gx [page_get $p1 $i] - set v [page_read $gx] - page_unref $gx - set v - } "Page-$i v0" -} -do_test pager-4.3 { - lrange [pager_stats $::p1] 0 1 -} {ref 1} -do_test pager-4.4 { - lrange [pager_stats $::p1] 8 9 -} {state 1} - -for {set i 1} {$i<20} {incr i} { - do_test pager-4.5.$i.0 { - set res {} - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v[expr {$i-1}]" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager-4.5.$i.1 { - page_write $g1 "Page-1 v$i" - lrange [pager_stats $p1] 8 9 - } {state 2} - do_test pager-4.5.$i.2 { - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - page_write $gx "Page-$j v$i" - page_unref $gx - if {$j==$i} { - pager_stmt_begin $p1 - } - } - } {} - do_test pager-4.5.$i.3 { - set res {} - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v$i" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager-4.5.$i.4 { - pager_rollback $p1 - set res {} - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v[expr {$i-1}]" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager-4.5.$i.5 { - page_write $g1 "Page-1 v$i" - lrange [pager_stats $p1] 8 9 - } {state 2} - do_test pager-4.5.$i.6 { - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - page_write $gx "Page-$j v$i" - page_unref $gx - if {$j==$i} { - pager_stmt_begin $p1 - } - } - } {} - do_test pager-4.5.$i.7 { - pager_stmt_rollback $p1 - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - if {$j<=$i || $i==1} { - set shouldbe "Page-$j v$i" - } else { - set shouldbe "Page-$j v[expr {$i-1}]" - } - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager-4.5.$i.8 { - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - page_write $gx "Page-$j v$i" - page_unref $gx - if {$j==$i} { - pager_stmt_begin $p1 - } - } - } {} - do_test pager-4.5.$i.9 { - pager_stmt_commit $p1 - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v$i" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager-4.5.$i.10 { - pager_commit $p1 - lrange [pager_stats $p1] 8 9 - } {state 1} -} - -# Test that nothing bad happens when sqlite3pager_set_cachesize() is -# called with a negative argument. -do_test pager-4.6.1 { - pager_close [pager_open ptf2.db -15] -} {} - -# Test truncate on an in-memory database is Ok. -ifcapable memorydb { - do_test pager-4.6.2 { - set ::p2 [pager_open :memory: 10] - pager_truncate $::p2 0 - } {} - do_test pager-4.6.3 { - set page1 [page_get $::p2 1] - for {set i 1} {$i<5} {incr i} { - set p [page_get $::p2 $i] - page_write $p "Page $i" - pager_commit $::p2 - page_unref $p - } - page_unref $page1 - pager_truncate $::p2 3 - } {} - do_test pager-4.6.4 { - pager_close $::p2 - } {} -} - -do_test pager-4.99 { - page_unref $::g1 - pager_close $::p1 -} {} - - - - file delete -force ptf1.db - -} ;# end if( not mem: and has pager_open command ); - -if 0 { -# Ticket #615: an assertion fault inside the pager. It is a benign -# fault, but we might as well test for it. -# -do_test pager-5.1 { - sqlite3 db test.db - execsql { - BEGIN; - CREATE TABLE t1(x); - PRAGMA synchronous=off; - COMMIT; - } -} {} -} - -# The following tests cover rolling back hot journal files. -# They can't be run on windows because the windows version of -# SQLite holds a mandatory exclusive lock on journal files it has open. -# -# They cannot be run during the journaltest permutation because -# "PRAGMA synchronous = 0" is used. -# -if {$tcl_platform(platform)!="windows" && ( - 0 == [info exists ::permutations_test_prefix] - || $::permutations_test_prefix ne "journaltest" -)} { -do_test pager-6.1 { - file delete -force test2.db - file delete -force test2.db-journal - sqlite3 db2 test2.db - execsql { - PRAGMA synchronous = 0; - CREATE TABLE abc(a, b, c); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - INSERT INTO abc VALUES(1, 2, randstr(200,200)); - BEGIN; - UPDATE abc SET c = randstr(200,200); - } db2 - copy_file test2.db test.db - copy_file test2.db-journal test.db-journal - - set f [open test.db-journal a] - fconfigure $f -encoding binary - seek $f [expr [file size test.db-journal] - 1032] start - puts -nonewline $f "\00\00\00\00" - close $f - - sqlite3 db test.db - execsql { - SELECT sql FROM sqlite_master - } -} {{CREATE TABLE abc(a, b, c)}} - -do_test pager-6.2 { - copy_file test2.db test.db - copy_file test2.db-journal test.db-journal - - set f [open test.db-journal a] - fconfigure $f -encoding binary - seek $f [expr [file size test.db-journal] - 1032] start - puts -nonewline $f "\00\00\00\FF" - close $f - - sqlite3 db test.db - execsql { - SELECT sql FROM sqlite_master - } -} {{CREATE TABLE abc(a, b, c)}} - -do_test pager-6.3 { - copy_file test2.db test.db - copy_file test2.db-journal test.db-journal - - set f [open test.db-journal a] - fconfigure $f -encoding binary - seek $f [expr [file size test.db-journal] - 4] start - puts -nonewline $f "\00\00\00\00" - close $f - - sqlite3 db test.db - execsql { - SELECT sql FROM sqlite_master - } -} {{CREATE TABLE abc(a, b, c)}} - -do_test pager-6.4.1 { - execsql { - BEGIN; - SELECT sql FROM sqlite_master; - } - copy_file test2.db-journal test.db-journal; - sqlite3 db3 test.db - catchsql { - BEGIN; - SELECT sql FROM sqlite_master; - } db3; -} {1 {database is locked}} -do_test pager-6.4.2 { - file delete -force test.db-journal - catchsql { - SELECT sql FROM sqlite_master; - } db3; -} {0 {{CREATE TABLE abc(a, b, c)}}} -do_test pager-6.4.3 { - db3 close - execsql { - COMMIT; - } -} {} - -do_test pager-6.5 { - copy_file test2.db test.db - copy_file test2.db-journal test.db-journal - - set f [open test.db-journal a] - fconfigure $f -encoding binary - puts -nonewline $f "hello" - puts -nonewline $f "\x00\x00\x00\x05\x01\x02\x03\x04" - puts -nonewline $f "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7" - close $f - - sqlite3 db test.db - execsql { - SELECT sql FROM sqlite_master - } -} {{CREATE TABLE abc(a, b, c)}} - -do_test pager-6.5 { - db2 close -} {} -} -finish_test DELETED test/pager2.test Index: test/pager2.test ================================================================== --- test/pager2.test +++ /dev/null @@ -1,414 +0,0 @@ -# 2001 September 15 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this script is page cache subsystem. -# -# $Id: pager2.test,v 1.9 2008/12/30 17:55:00 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -# Don't run this test file if the pager test interface [pager_open] is not -# available, or the library was compiled without in-memory database support. -# -if {[info commands pager_open]!=""} { -ifcapable memorydb { -db close - -# Basic sanity check. Open and close a pager. -# -do_test pager2-1.0 { - set v [catch { - set ::p1 [pager_open :memory: 10] - } msg] -} {0} -do_test pager2-1.1 { - pager_stats $::p1 -} {ref 0 page 0 max 10 size 0 state 4 err 0 hit 0 miss 0 ovfl 0} -do_test pager2-1.2 { - pager_pagecount $::p1 -} {0} -do_test pager2-1.3 { - pager_stats $::p1 -} {ref 0 page 0 max 10 size 0 state 4 err 0 hit 0 miss 0 ovfl 0} -do_test pager2-1.4 { - pager_close $::p1 -} {} - -# Try to write a few pages. -# -do_test pager2-2.1 { - set v [catch { - set ::p1 [pager_open :memory: 10] - } msg] -} {0} -#do_test pager2-2.2 { -# set v [catch { -# set ::g1 [page_get $::p1 0] -# } msg] -# lappend v $msg -#} {1 SQLITE_ERROR} -do_test pager2-2.3.1 { - set ::gx [page_lookup $::p1 1] -} {} -do_test pager2-2.3.2 { - pager_stats $::p1 -} {ref 0 page 0 max 10 size 0 state 4 err 0 hit 0 miss 0 ovfl 0} -do_test pager2-2.3.3 { - set v [catch { - set ::g1 [page_get $::p1 1] - } msg] - if {$v} {lappend v $msg} - set v -} {0} -do_test pager2-2.3.3 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 4 err 0 hit 0 miss 1 ovfl 0} -do_test pager2-2.3.4 { - set ::gx [page_lookup $::p1 1] - page_unref $::gx - expr {$::gx!=""} -} {1} -do_test pager2-2.3.5 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 4 err 0 hit 0 miss 1 ovfl 0} -do_test pager2-2.3.6 { - expr {$::g1==$::gx} -} {1} -do_test pager2-2.3.7 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 4 err 0 hit 0 miss 1 ovfl 0} -do_test pager2-2.4 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 4 err 0 hit 0 miss 1 ovfl 0} -do_test pager2-2.5 { - pager_pagecount $::p1 -} {0} -do_test pager2-2.6 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 4 err 0 hit 0 miss 1 ovfl 0} -do_test pager2-2.7 { - page_number $::g1 -} {1} -do_test pager2-2.8 { - page_read $::g1 -} {} -do_test pager2-2.9 { - page_unref $::g1 -} {} -do_test pager2-2.10 { - pager_stats $::p1 -} {ref 0 page 1 max 10 size 0 state 4 err 0 hit 0 miss 1 ovfl 0} -do_test pager2-2.11 { - set ::g1 [page_get $::p1 1] - expr {$::g1!=0} -} {1} -do_test pager2-2.12 { - page_number $::g1 -} {1} -do_test pager2-2.13 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 0 state 4 err 0 hit 1 miss 1 ovfl 0} -do_test pager2-2.14 { - set v [catch { - page_write $::g1 "Page-One" - } msg] - lappend v $msg -} {0 {}} -do_test pager2-2.15 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 1 state 4 err 0 hit 1 miss 1 ovfl 0} -do_test pager2-2.16 { - page_read $::g1 -} {Page-One} -do_test pager2-2.17 { - set v [catch { - pager_commit $::p1 - } msg] - lappend v $msg -} {0 {}} -do_test pager2-2.20 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 1 state 4 err 0 hit 1 miss 1 ovfl 0} -do_test pager2-2.19 { - pager_pagecount $::p1 -} {1} -do_test pager2-2.21 { - pager_stats $::p1 -} {ref 1 page 1 max 10 size 1 state 4 err 0 hit 1 miss 1 ovfl 0} -do_test pager2-2.22 { - page_unref $::g1 -} {} -do_test pager2-2.23 { - pager_stats $::p1 -} {ref 0 page 1 max 10 size 1 state 4 err 0 hit 1 miss 1 ovfl 0} -do_test pager2-2.24 { - set v [catch { - page_get $::p1 1 - } ::g1] - if {$v} {lappend v $::g1} - set v -} {0} -do_test pager2-2.25 { - page_read $::g1 -} {Page-One} -do_test pager2-2.26 { - set v [catch { - page_write $::g1 {page-one} - } msg] - lappend v $msg -} {0 {}} -do_test pager2-2.27 { - page_read $::g1 -} {page-one} -do_test pager2-2.28 { - set v [catch { - pager_rollback $::p1 - } msg] - lappend v $msg -} {0 {}} -do_test pager2-2.29 { - page_unref $::g1 - set ::g1 [page_get $::p1 1] - page_read $::g1 -} {Page-One} -do_test pager2-2.99 { - page_unref $::g1 -} {} - -#do_test pager2-3.1 { -# set v [catch { -# set ::p1 [pager_open :memory: 15] -# } msg] -# if {$v} {lappend v $msg} -# set v -#} {0} -do_test pager2-3.2 { - pager_pagecount $::p1 -} {1} -do_test pager2-3.3 { - set v [catch { - set ::g(1) [page_get $::p1 1] - } msg] - if {$v} {lappend v $msg} - set v -} {0} -do_test pager2-3.4 { - page_read $::g(1) -} {Page-One} -do_test pager2-3.5 { - for {set i 2} {$i<=20} {incr i} { - set gx [page_get $::p1 $i] - page_write $gx "Page-$i" - page_unref $gx - } - pager_commit $::p1 - page_unref $::g(1) -} {} -for {set i 2} {$i<=20} {incr i} { - set page1 [page_get $::p1 1] - do_test pager2-3.6.[expr {$i-1}] [subst { - set gx \[page_get $::p1 $i\] - set v \[page_read \$gx\] - page_unref \$gx - set v - }] "Page-$i" - page_unref $page1 -} -for {set i 1} {$i<=20} {incr i} { - set page1 [page_get $::p1 1] - regsub -all CNT { - set ::g1 [page_get $::p1 CNT] - set ::g2 [page_get $::p1 CNT] - set ::vx [page_read $::g2] - expr {$::g1==$::g2} - } $i body; - do_test pager2-3.7.$i.1 $body {1} - regsub -all CNT { - page_unref $::g2 - set vy [page_read $::g1] - expr {$vy==$::vx} - } $i body; - do_test pager2-3.7.$i.2 $body {1} - regsub -all CNT { - page_unref $::g1 - set gx [page_get $::p1 CNT] - set vy [page_read $gx] - page_unref $gx - expr {$vy==$::vx} - } $i body; - do_test pager2-3.7.$i.3 $body {1} - page_unref $page1 -} -do_test pager2-3.99 { - pager_close $::p1 -} {} - -# tests of the checkpoint mechanism and api -# -do_test pager2-4.0 { - set v [catch { - set ::p1 [pager_open :memory: 15] - } msg] - if {$v} {lappend v $msg} - set v -} {0} -do_test pager2-4.1 { - set g1 [page_get $::p1 1] - page_write $g1 "Page-1 v0" - for {set i 2} {$i<=20} {incr i} { - set gx [page_get $::p1 $i] - page_write $gx "Page-$i v0" - page_unref $gx - } - pager_commit $::p1 -} {} -for {set i 1} {$i<=20} {incr i} { - do_test pager2-4.2.$i { - set gx [page_get $p1 $i] - set v [page_read $gx] - page_unref $gx - set v - } "Page-$i v0" -} -do_test pager2-4.3 { - lrange [pager_stats $::p1] 0 1 -} {ref 1} -do_test pager2-4.4 { - lrange [pager_stats $::p1] 8 9 -} {state 4} - -for {set i 1} {$i<20} {incr i} { - do_test pager2-4.5.$i.0 { - set res {} - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v[expr {$i-1}]" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager2-4.5.$i.1 { - page_write $g1 "Page-1 v$i" - lrange [pager_stats $p1] 8 9 - } {state 4} - do_test pager2-4.5.$i.2 { - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - page_write $gx "Page-$j v$i" - page_unref $gx - if {$j==$i} { - pager_stmt_begin $p1 - } - } - } {} - do_test pager2-4.5.$i.3 { - set res {} - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v$i" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager2-4.5.$i.4 { - pager_rollback $p1 - set res {} - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v[expr {$i-1}]" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager2-4.5.$i.5 { - page_write $g1 "Page-1 v$i" - lrange [pager_stats $p1] 8 9 - } {state 4} - do_test pager2-4.5.$i.6 { - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - page_write $gx "Page-$j v$i" - page_unref $gx - if {$j==$i} { - pager_stmt_begin $p1 - } - } - } {} - do_test pager2-4.5.$i.7 { - pager_stmt_rollback $p1 - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - if {$j<=$i || $i==1} { - set shouldbe "Page-$j v$i" - } else { - set shouldbe "Page-$j v[expr {$i-1}]" - } - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager2-4.5.$i.8 { - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - page_write $gx "Page-$j v$i" - page_unref $gx - if {$j==$i} { - pager_stmt_begin $p1 - } - } - } {} - do_test pager2-4.5.$i.9 { - pager_stmt_commit $p1 - for {set j 2} {$j<=20} {incr j} { - set gx [page_get $p1 $j] - set value [page_read $gx] - page_unref $gx - set shouldbe "Page-$j v$i" - if {$value!=$shouldbe} { - lappend res $value $shouldbe - } - } - set res - } {} - do_test pager2-4.5.$i.10 { - pager_commit $p1 - lrange [pager_stats $p1] 8 9 - } {state 4} -} - -do_test pager2-4.99 { - page_unref $::g1 - pager_close $::p1 -} {} - -} ;# ifcapable inmemory -} ;# end if( has pager_open command ); - - -finish_test DELETED test/pager3.test Index: test/pager3.test ================================================================== --- test/pager3.test +++ /dev/null @@ -1,73 +0,0 @@ -# 2001 September 15 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# This file implements regression tests for SQLite library. The -# focus of this script is page cache subsystem. -# -# $Id: pager3.test,v 1.3 2005/03/29 03:11:00 danielk1977 Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -# This test makes sure the database file is truncated back to the correct -# length on a rollback. -# -# After some preliminary setup, a transaction is start at NOTE (1). -# The create table on the following line allocates an additional page -# at the end of the database file. But that page is not written because -# the database still has a RESERVED lock, not an EXCLUSIVE lock. The -# new page is held in memory and the size of the file is unchanged. -# The insert at NOTE (2) begins adding additional pages. Then it hits -# a constraint error and aborts. The abort causes sqlite3OsTruncate() -# to be called to restore the file to the same length as it was after -# the create table. But the create table results had not yet been -# written so the file is actually lengthened by this truncate. Finally, -# the rollback at NOTE (3) is called to undo all the changes since the -# begin. This rollback should truncate the database again. -# -# This test was added because the second truncate at NOTE (3) was not -# occurring on early versions of SQLite 3.0. -# -ifcapable tempdb { - do_test pager3-1.1 { - execsql { - create table t1(a unique, b); - insert into t1 values(1, 'abcdefghijklmnopqrstuvwxyz'); - insert into t1 values(2, 'abcdefghijklmnopqrstuvwxyz'); - update t1 set b=b||a||b; - update t1 set b=b||a||b; - update t1 set b=b||a||b; - update t1 set b=b||a||b; - update t1 set b=b||a||b; - update t1 set b=b||a||b; - create temp table t2 as select * from t1; - begin; ------- NOTE (1) - create table t3(x); - } - catchsql { - insert into t1 select 4-a, b from t2; ----- NOTE (2) - } - execsql { - rollback; ------- NOTE (3) - } - db close - sqlite3 db test.db - set r ok - ifcapable {integrityck} { - set r [execsql { - pragma integrity_check; - }] - } - set r - } ok -} - -finish_test Index: test/pcache2.test ================================================================== --- test/pcache2.test +++ test/pcache2.test @@ -9,11 +9,11 @@ # #*********************************************************************** # # This file is focused on testing the pcache module. # -# $Id: pcache2.test,v 1.3 2008/11/13 16:21:50 danielk1977 Exp $ +# $Id: pcache2.test,v 1.5 2009/07/18 14:36:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -43,10 +43,11 @@ file delete -force test2.db test2.db-journal sqlite3 db2 test2.db db2 eval {PRAGMA cache_size=50} lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 1 } {4} + # Make lots of changes on the first connection. Verify that the # page cache usage does not grow to consume the page space set aside # for the second connection. # Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -7,11 +7,11 @@ # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # -# $Id: permutations.test,v 1.50 2009/05/13 14:46:10 danielk1977 Exp $ +# $Id: permutations.test,v 1.51 2009/07/01 18:09:02 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Argument processing. @@ -481,17 +481,25 @@ autovacuum.test delete3.test manydb.test bigrow.test incrblob2.test memdb.test bitvec.test index2.test memsubsys1.test capi3c.test ioerr.test memsubsys2.test capi3.test join3.test pagesize.test - collate5.test limit.test + collate5.test limit.test backup_ioerr.test + backup_malloc.test } -initialize { catch {db close} sqlite3_reset_auto_extension sqlite3_shutdown sqlite3_config_heap 25000000 0 sqlite3_config_lookaside 0 0 + ifcapable mem5 { + # If both memsys3 and memsys5 are enabled in the build, the call to + # [sqlite3_config_heap] will initialize the system to use memsys5. + # The following overrides this preference and installs the memsys3 + # allocator. + sqlite3_install_memsys3 + } install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} Index: test/pragma.test ================================================================== --- test/pragma.test +++ test/pragma.test @@ -531,11 +531,11 @@ do_test pragma-6.3.1 { execsql { CREATE TABLE t3(a int references t2(b), b UNIQUE); pragma foreign_key_list(t3); } - } {0 0 t2 a b RESTRICT RESTRICT NONE} + } {0 0 t2 a b {NO ACTION} {NO ACTION} NONE} do_test pragma-6.3.2 { execsql { pragma foreign_key_list; } } {} Index: test/quick.test ================================================================== --- test/quick.test +++ test/quick.test @@ -56,10 +56,11 @@ crash5.test crash6.test crash7.test delete3.test fts3.test + fkey_malloc.test fuzz.test fuzz3.test fuzz_malloc.test in2.test loadext.test Index: test/savepoint.test ================================================================== --- test/savepoint.test +++ test/savepoint.test @@ -7,11 +7,11 @@ # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # -# $Id: savepoint.test,v 1.12 2009/02/04 10:09:04 danielk1977 Exp $ +# $Id: savepoint.test,v 1.13 2009/07/18 08:30:45 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -267,70 +267,72 @@ #------------------------------------------------------------------------ # Test some logic errors to do with the savepoint feature. # -do_test savepoint-5.1.1 { - execsql { - CREATE TABLE blobs(x); - INSERT INTO blobs VALUES('a twentyeight character blob'); - } - set fd [db incrblob blobs x 1] - puts -nonewline $fd "hello" - catchsql {SAVEPOINT abc} -} {1 {cannot open savepoint - SQL statements in progress}} -do_test savepoint-5.1.2 { - close $fd - catchsql {SAVEPOINT abc} -} {0 {}} - -do_test savepoint-5.2 { - execsql {RELEASE abc} - catchsql {RELEASE abc} -} {1 {no such savepoint: abc}} - -do_test savepoint-5.3.1 { - execsql {SAVEPOINT abc} - catchsql {ROLLBACK TO def} -} {1 {no such savepoint: def}} -do_test savepoint-5.3.2 { - execsql {SAVEPOINT def} - set fd [db incrblob -readonly blobs x 1] - catchsql {ROLLBACK TO def} -} {1 {cannot rollback savepoint - SQL statements in progress}} -do_test savepoint-5.3.3 { - catchsql {RELEASE def} -} {0 {}} -do_test savepoint-5.3.4 { - close $fd - execsql {savepoint def} - set fd [db incrblob blobs x 1] - catchsql {release def} -} {1 {cannot release savepoint - SQL statements in progress}} -do_test savepoint-5.3.5 { - close $fd - execsql {release abc} -} {} - -do_test savepoint-5.4.1 { - execsql { - SAVEPOINT main; - INSERT INTO blobs VALUES('another blob'); - } -} {} -do_test savepoint-5.4.2 { - sqlite3 db2 test.db - execsql { BEGIN ; SELECT * FROM blobs } db2 - catchsql { RELEASE main } -} {1 {database is locked}} -do_test savepoint-5.4.3 { - db2 close - catchsql { RELEASE main } -} {0 {}} -do_test savepoint-5.4.4 { - execsql { SELECT x FROM blobs WHERE rowid = 2 } -} {{another blob}} +ifcapable incrblob { + do_test savepoint-5.1.1 { + execsql { + CREATE TABLE blobs(x); + INSERT INTO blobs VALUES('a twentyeight character blob'); + } + set fd [db incrblob blobs x 1] + puts -nonewline $fd "hello" + catchsql {SAVEPOINT abc} + } {1 {cannot open savepoint - SQL statements in progress}} + do_test savepoint-5.1.2 { + close $fd + catchsql {SAVEPOINT abc} + } {0 {}} + + do_test savepoint-5.2 { + execsql {RELEASE abc} + catchsql {RELEASE abc} + } {1 {no such savepoint: abc}} + + do_test savepoint-5.3.1 { + execsql {SAVEPOINT abc} + catchsql {ROLLBACK TO def} + } {1 {no such savepoint: def}} + do_test savepoint-5.3.2 { + execsql {SAVEPOINT def} + set fd [db incrblob -readonly blobs x 1] + catchsql {ROLLBACK TO def} + } {1 {cannot rollback savepoint - SQL statements in progress}} + do_test savepoint-5.3.3 { + catchsql {RELEASE def} + } {0 {}} + do_test savepoint-5.3.4 { + close $fd + execsql {savepoint def} + set fd [db incrblob blobs x 1] + catchsql {release def} + } {1 {cannot release savepoint - SQL statements in progress}} + do_test savepoint-5.3.5 { + close $fd + execsql {release abc} + } {} + + do_test savepoint-5.4.1 { + execsql { + SAVEPOINT main; + INSERT INTO blobs VALUES('another blob'); + } + } {} + do_test savepoint-5.4.2 { + sqlite3 db2 test.db + execsql { BEGIN ; SELECT * FROM blobs } db2 + catchsql { RELEASE main } + } {1 {database is locked}} + do_test savepoint-5.4.3 { + db2 close + catchsql { RELEASE main } + } {0 {}} + do_test savepoint-5.4.4 { + execsql { SELECT x FROM blobs WHERE rowid = 2 } + } {{another blob}} +} #------------------------------------------------------------------------- # The following tests, savepoint-6.*, test an incr-vacuum inside of a # couple of nested savepoints. # Index: test/schema.test ================================================================== --- test/schema.test +++ test/schema.test @@ -361,7 +361,27 @@ # $::STMT was prepared. So unless it has been expired, it would be # possible to run the "CREATE TABLE t4" statement and create a # duplicate table. list [sqlite3_step $::STMT] [sqlite3_finalize $::STMT] } {SQLITE_ERROR SQLITE_SCHEMA} + +do_test schema-13.1 { + set S [sqlite3_prepare_v2 db "SELECT * FROM sqlite_master" -1 dummy] + db function hello hello + db function hello {} + db auth auth + proc auth {args} { + if {[lindex $args 0] == "SQLITE_READ"} {return SQLITE_DENY} + return SQLITE_OK + } + sqlite3_step $S +} {SQLITE_SCHEMA} + +do_test schema-13.2 { + sqlite3_step $S +} {SQLITE_SCHEMA} + +do_test schema-13.3 { + sqlite3_finalize $S +} {SQLITE_SCHEMA} finish_test Index: test/selectC.test ================================================================== --- test/selectC.test +++ test/selectC.test @@ -149,19 +149,21 @@ } } {CCC AAA AAA} # The following query used to leak memory. Verify that has been fixed. # -do_test selectC-2.1 { - catchsql { - CREATE TABLE t21a(a,b); - INSERT INTO t21a VALUES(1,2); - CREATE TABLE t21b(n); - CREATE TRIGGER r21 AFTER INSERT ON t21b BEGIN - SELECT a FROM t21a WHERE a>new.x UNION ALL - SELECT b FROM t21a WHERE b>new.x ORDER BY 1 LIMIT 2; - END; - INSERT INTO t21b VALUES(6); - } -} {1 {no such column: new.x}} +ifcapable trigger { + do_test selectC-2.1 { + catchsql { + CREATE TABLE t21a(a,b); + INSERT INTO t21a VALUES(1,2); + CREATE TABLE t21b(n); + CREATE TRIGGER r21 AFTER INSERT ON t21b BEGIN + SELECT a FROM t21a WHERE a>new.x UNION ALL + SELECT b FROM t21a WHERE b>new.x ORDER BY 1 LIMIT 2; + END; + INSERT INTO t21b VALUES(6); + } + } {1 {no such column: new.x}} +} finish_test ADDED test/sharedlock.test Index: test/sharedlock.test ================================================================== --- /dev/null +++ test/sharedlock.test @@ -0,0 +1,55 @@ +# 2009 July 2 +# +# 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. +# +#*********************************************************************** +# +# $Id: sharedlock.test,v 1.1 2009/07/02 17:21:58 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +db close + +ifcapable !shared_cache { + finish_test + return +} + +set ::enable_shared_cache [sqlite3_enable_shared_cache 1] +sqlite3 db test.db +sqlite3 db2 test.db + +do_test sharedlock-1.1 { + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + } +} {} + +do_test sharedlock-1.2 { + set res [list] + db eval { SELECT * FROM t1 ORDER BY rowid } { + lappend res $a $b + if {$a == 1} { catch { db eval "INSERT INTO t1 VALUES(3, 'three')" } } + + # This should fail. Connection [db] has a read-lock on t1, which should + # prevent connection [db2] from obtaining the write-lock it needs to + # modify t1. At one point there was a bug causing the previous INSERT + # to drop the read-lock belonging to [db]. + if {$a == 2} { catch { db2 eval "INSERT INTO t1 VALUES(4, 'four')" } } + } + set res +} {1 one 2 two 3 three} + +db close +db2 close + +sqlite3_enable_shared_cache $::enable_shared_cache +finish_test + Index: test/speed3.test ================================================================== --- test/speed3.test +++ test/speed3.test @@ -10,11 +10,11 @@ #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing that the overflow-page related # enhancements added after version 3.3.17 speed things up. # -# $Id: speed3.test,v 1.5 2007/10/09 08:29:33 danielk1977 Exp $ +# $Id: speed3.test,v 1.6 2009/07/09 02:48:24 shane Exp $ # #--------------------------------------------------------------------- # Test plan: # @@ -103,22 +103,10 @@ # puts "2: [array get stats2]" puts "Incrvacuum: Read $stats1(read), wrote $stats1(write)" puts "Normal : Read $stats2(read), wrote $stats2(write)" } -proc overflow_report {db} { - set bt [btree_from_db db] - set csr [btree_cursor $bt 3 0] - - for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} { - puts "[btree_ovfl_info $bt $csr]" - } - - btree_close_cursor $csr - -} - proc reset_db {} { db close sqlite3 db test.db db eval { PRAGMA main.cache_size = 200000; @@ -164,11 +152,10 @@ } {2 0} # Delete all content in a table, one row at a time. # #io_log db -#overflow_report db reset_db speed_trial speed3-1.incrvacuum $::NROW row {DELETE FROM main.t1 WHERE 1} speed_trial speed3-1.normal $::NROW row {DELETE FROM aux.t1 WHERE 1} io_log db @@ -175,12 +162,11 @@ # Select the "C" column (located at the far end of the overflow # chain) from each table row. # #db eval {PRAGMA incremental_vacuum(500000)} populate_t1 db -#overflow_report db reset_db speed_trial speed3-2.incrvacuum $::NROW row {SELECT c FROM main.t1} speed_trial speed3-2.normal $::NROW row {SELECT c FROM aux.t1} io_log db finish_test Index: test/tableapi.test ================================================================== --- test/tableapi.test +++ test/tableapi.test @@ -10,14 +10,19 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the sqlite_exec_printf() and # sqlite_get_table_printf() APIs. # -# $Id: tableapi.test,v 1.20 2008/07/31 02:05:05 shane Exp $ +# $Id: tableapi.test,v 1.21 2009/07/17 14:37:25 shane Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl + +ifcapable !gettable { + finish_test + return +} ifcapable memdebug { source $testdir/malloc_common.tcl } @@ -33,12 +38,10 @@ } {0 {}} do_test tableapi-1.2 { sqlite3_exec_printf $::dbx {SELECT * FROM xyz} {} } {0 {a b 1 {Hi Y'all}}} -ifcapable gettable { - do_test tableapi-2.1 { sqlite3_get_table_printf $::dbx { BEGIN TRANSACTION; SELECT * FROM xyz WHERE b='%q' } {Hi Y'all} @@ -112,13 +115,10 @@ do_test tableapi-2.7 { sqlite3_get_table_printf $::dbx { SELECT * FROM xyz WHERE a>1000 } {} } {0 0 0} - -}; # end ifcapable gettable - # Repeat all tests with the empty_result_callbacks pragma turned on # do_test tableapi-3.1 { sqlite3_get_table_printf $::dbx { Index: test/tclsqlite.test ================================================================== --- test/tclsqlite.test +++ test/tclsqlite.test @@ -567,10 +567,13 @@ db exists {SELECT 0 FROM t4 WHERE x==6} } {1} do_test tcl-11.3 { db exists {SELECT 1 FROM t4 WHERE x==8} } {0} +do_test tcl-11.3.1 { + tcl_objproc db exists {SELECT 1 FROM t4 WHERE x==8} +} {0} do_test tcl-12.1 { unset -nocomplain a b c version set version [db version] scan $version "%d.%d.%d" a b c Index: test/tester.tcl ================================================================== --- test/tester.tcl +++ test/tester.tcl @@ -955,10 +955,24 @@ puts -nonewline $t [read $f [file size $from]] close $t close $f } } + +# Drop all tables in database [db] +proc drop_all_tables {{db db}} { + set pk [$db one "PRAGMA foreign_keys"] + $db eval "PRAGMA foreign_keys = OFF" + foreach {t type} [$db eval { + SELECT name, type FROM sqlite_master + WHERE type IN('table', 'view') AND name NOT like 'sqlite_%' + }] { + $db eval "DROP $type $t" + } + $db eval " PRAGMA foreign_keys = $pk " +} + # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1. set AUTOVACUUM $sqlite_options(default_autovacuum) Index: test/thread2.test ================================================================== --- test/thread2.test +++ test/thread2.test @@ -190,27 +190,26 @@ thread_create A test.db thread_compile A {SELECT a FROM t1 LIMIT 3} thread_step A set STMT [thread_stmt_get A] set DB [thread_db_get A] - thread_halt A -} {} -do_test thread2-3.21 { sqlite3_step $STMT } SQLITE_ROW do_test thread2-3.22 { sqlite3_column_int $STMT 0 } 2 do_test thread2-3.23 { # The unlock fails here. But because we never check the return # code from sqlite3OsUnlock (because we cannot do anything about it # if it fails) we do not realize that an error has occurred. + breakpoint sqlite3_finalize $STMT } SQLITE_OK do_test thread2-3.25 { - sqlite3_close $DB -} SQLITE_OK + thread_db_put A $DB + thread_halt A +} {} do_test thread2-3.30 { thread_create A test.db thread_compile A {BEGIN} thread_step A @@ -217,22 +216,20 @@ thread_finalize A thread_compile A {SELECT a FROM t1 LIMIT 1} thread_step A thread_finalize A set DB [thread_db_get A] - thread_halt A -} {} -do_test thread2-3.31 { set STMT [sqlite3_prepare $DB {INSERT INTO t1 VALUES(99,'error')} -1 TAIL] sqlite3_step $STMT } SQLITE_ERROR do_test thread2-3.32 { sqlite3_finalize $STMT } SQLITE_MISUSE do_test thread2-3.33 { - sqlite3_close $DB -} SQLITE_OK + thread_db_put A $DB + thread_halt A +} {} # VERY important to set the override flag back to its true value. # set threadsOverrideEachOthersLocks $orig_threadOverride ADDED test/tkt-2ea2425d34.test Index: test/tkt-2ea2425d34.test ================================================================== --- /dev/null +++ test/tkt-2ea2425d34.test @@ -0,0 +1,31 @@ +# 2009 September 2 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [2ea2425d34be] has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-2ea24-1.1 { + db eval { + PRAGMA encoding=UTF16; + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,'abc'); + INSERT INTO t1 VALUES(2,'def'); + INSERT INTO t1 VALUES(3,'ghi'); + SELECT a FROM t1 WHERE length(b)<10 AND b<>'def' ORDER BY a; + } +} {1 3} + +finish_test ADDED test/tkt-3fe897352e.test Index: test/tkt-3fe897352e.test ================================================================== --- /dev/null +++ test/tkt-3fe897352e.test @@ -0,0 +1,53 @@ +# 2009 October 23 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [3fe897352e8d8] has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-3fe89-1.1 { + db close + sqlite3 db :memory: + db eval { + PRAGMA encoding=UTF8; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(hex_to_utf16be('D800')); + SELECT hex(x) FROM t1; + } +} {EDA080} +do_test tkt-3fe89-1.2 { + db eval { + DELETE FROM t1; + INSERT INTO t1 VALUES(hex_to_utf16le('00D8')); + SELECT hex(x) FROM t1; + } +} {EDA080} +do_test tkt-3fe89-1.3 { + db eval { + DELETE FROM t1; + INSERT INTO t1 VALUES(hex_to_utf16be('DFFF')); + SELECT hex(x) FROM t1; + } +} {EDBFBF} +do_test tkt-3fe89-1.4 { + db eval { + DELETE FROM t1; + INSERT INTO t1 VALUES(hex_to_utf16le('FFDF')); + SELECT hex(x) FROM t1; + } +} {EDBFBF} + + +finish_test ADDED test/tkt-4a03edc4c8.test Index: test/tkt-4a03edc4c8.test ================================================================== --- /dev/null +++ test/tkt-4a03edc4c8.test @@ -0,0 +1,46 @@ +# 2009 September 23 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that +# ticket [4a03edc4c8c028c93e9269f64fc5e97f632c1166] has been fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-4a03ed-1.1 { + db eval { + CREATE TABLE t1( + a INTEGER PRIMARY KEY ON CONFLICT REPLACE, + b UNIQUE ON CONFLICT FAIL + ); + INSERT INTO t1 VALUES(1, 1); + INSERT INTO t1 VALUES(2, 2); + } + catchsql { + BEGIN; + INSERT INTO t1 VALUES(1, 2); + COMMIT; + } +} {1 {column b is not unique}} +do_test tkt-4a03ed-1.2 { + db eval { + PRAGMA integrity_check; + } +} {ok} +do_test tkt-4a03ed-1.3 { + db eval { + SELECT * FROM t1 ORDER BY a; + } +} {1 1 2 2} + +finish_test ADDED test/tkt-5ee23731f.test Index: test/tkt-5ee23731f.test ================================================================== --- /dev/null +++ test/tkt-5ee23731f.test @@ -0,0 +1,42 @@ +# 2009 October 13 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [5ee23731f15] has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-5ee237-1.1 { + db close + file delete -force test.db + sqlite3 db test.db + db eval { + CREATE TABLE t1(x UNIQUE); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t1 SELECT x+2 FROM t1; + INSERT INTO t1 SELECT x+4 FROM t1; + INSERT INTO t1 SELECT x+8 FROM t1; + } + db close + sqlite3 db test.db -readonly 1 + set rc [catch { + db eval {SELECT rowid, x FROM t1 ORDER BY x} { + db eval {UPDATE t1 SET x=x+1 WHERE rowid=:rowid} + } + } msg] + lappend rc $msg +} {1 {attempt to write a readonly database}} + +finish_test ADDED test/tkt-94c04eaadb.test Index: test/tkt-94c04eaadb.test ================================================================== --- /dev/null +++ test/tkt-94c04eaadb.test @@ -0,0 +1,72 @@ +# 2009 October 19 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +if {[info commands sqlite3async_initialize] eq ""} { + # The async logic is not built into this system + finish_test + return +} + +# Create a database. +do_test tkt-94c94-1.1 { + execsql { CREATE TABLE t1(a, b) } +} {} + +# Grow the file to larger than 4096MB (2^32 bytes) +db close +if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} { + puts "**** Unable to create a file larger than 4096 MB. *****" + finish_test + return +} + +# Switch to async mode. +sqlite3async_initialize "" 1 +sqlite3 db test.db +sqlite3 db2 test.db + +# Read from and write to the db just past the 4096MB mark. +# +do_test tkt-94c94-2.1 { + execsql { CREATE TABLE t2(x, y) } db +} {} +do_test tkt-94c94-2.2 { +breakpoint + execsql { INSERT INTO t2 VALUES(1, 2) } db2 +} {} +do_test tkt-94c94-2.3 { + execsql { SELECT * FROM t2 } db +} {1 2} +do_test tkt-94c94-2.4 { + sqlite3async_control halt idle + sqlite3async_start + sqlite3async_wait +} {} +do_test tkt-94c94-2.5 { + execsql { SELECT * FROM t2 } db +} {1 2} +do_test tkt-94c94-2.6 { + sqlite3async_start + sqlite3async_wait +} {} + +db close +db2 close +sqlite3async_start +sqlite3async_wait +sqlite3async_shutdown + +finish_test ADDED test/tkt-d82e3f3721.txt Index: test/tkt-d82e3f3721.txt ================================================================== --- /dev/null +++ test/tkt-d82e3f3721.txt @@ -0,0 +1,79 @@ +# 2009 September 2 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [d82e3f3721] has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-d82e3-1.1 { + db eval { + CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b); + INSERT INTO t1 VALUES(null,'abc'); + INSERT INTO t1 VALUES(null,'def'); + DELETE FROM t1; + INSERT INTO t1 VALUES(null,'ghi'); + SELECT * FROM t1; + } +} {3 ghi} +do_test tkt-d82e3-1.2 { + db eval { + CREATE TEMP TABLE t2(a INTEGER PRIMARY KEY AUTOINCREMENT, b); + INSERT INTO t2 VALUES(null,'jkl'); + INSERT INTO t2 VALUES(null,'mno'); + DELETE FROM t2; + INSERT INTO t2 VALUES(null,'pqr'); + SELECT * FROM t2; + } +} {3 pqr} +do_test tkt-d82e3-1.3 { + db eval { + SELECT 'main', * FROM main.sqlite_sequence + UNION ALL + SELECT 'temp', * FROM temp.sqlite_sequence + ORDER BY 2 + } +} {main t1 3 temp t2 3} +do_test tkt-d82e3-1.4 { + db eval { + VACUUM; + SELECT 'main', * FROM main.sqlite_sequence + UNION ALL + SELECT 'temp', * FROM temp.sqlite_sequence + ORDER BY 2 + } +} {main t1 3 temp t2 3} + +sqlite3 db2 test.db +do_test tkt-d82e3-2.1 { + db eval { + CREATE TEMP TABLE t3(x); + INSERT INTO t3 VALUES(1); + } + db2 eval { + CREATE TABLE t3(y,z); + INSERT INTO t3 VALUES(8,9); + } + db eval { + SELECT * FROM temp.t3 JOIN main.t3; + } +} {1 8 9} +do_test tkt-d82e3-2.2 { + db eval { + VACUUM; + SELECT * FROM temp.t3 JOIN main.t3; + } +} {1 8 9} + +finish_test ADDED test/tkt-f777251dc7a.test Index: test/tkt-f777251dc7a.test ================================================================== --- /dev/null +++ test/tkt-f777251dc7a.test @@ -0,0 +1,99 @@ +# 2009 October 16 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket [f777251dc7a] has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-f7772-1.1 { + execsql { + CREATE TEMP TABLE t1(x UNIQUE); + INSERT INTO t1 VALUES(1); + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES(1, 2); + CREATE TEMP TABLE t3(w, z); + } +} {} + +proc force_rollback {} { + catch {db eval {INSERT OR ROLLBACK INTO t1 VALUES(1)}} +} +db function force_rollback force_rollback + +do_test tkt-f7772-1.2 { + catchsql { + BEGIN IMMEDIATE; + SELECT x, force_rollback(), EXISTS(SELECT 1 FROM t3 WHERE w=x) FROM t2; + } +} {1 {callback requested query abort}} +do_test tkt-f7772-1.3 { + sqlite3_get_autocommit db +} {1} + +do_test tkt-f7772-2.1 { + execsql { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + DROP TABLE IF EXISTS t3; + + CREATE TEMP TABLE t1(x UNIQUE); + INSERT INTO t1 VALUES(1); + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES(1, 2); + } +} {} +do_test tkt-f7772-2.2 { + execsql { + BEGIN IMMEDIATE; + CREATE TEMP TABLE t3(w, z); + } + catchsql { + SELECT x, force_rollback(), EXISTS(SELECT 1 FROM t3 WHERE w=x) FROM t2 + } +} {1 {callback requested query abort}} +do_test tkt-f7772-2.3 { + sqlite3_get_autocommit db +} {1} + +do_test tkt-f7772-3.1 { + execsql { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + DROP TABLE IF EXISTS t3; + + CREATE TEMP TABLE t1(x); + CREATE TABLE t2(x); + CREATE TABLE t3(x); + + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t2 VALUES(1); + INSERT INTO t2 VALUES(2); + } +} {} + +proc ins {} { db eval {INSERT INTO t3 VALUES('hello')} } +db function ins ins + +do_test tkt-f7772-3.2 { + execsql { + SELECT ins() AS x FROM t2 UNION ALL SELECT ins() AS x FROM t1 + } +} {{} {} {} {}} +do_test tkt-f7772-3.3 { + execsql { SELECT * FROM t3 } +} {hello hello hello hello} + +finish_test Index: test/tkt3201.test ================================================================== --- test/tkt3201.test +++ test/tkt3201.test @@ -67,8 +67,39 @@ } {1 one 2 two} do_test tkt3201-7 { execsql { SELECT a, b, c, d FROM t1, t3 WHERE a < c } } {1 one 2 two} + +# Ticket [efc02f977919] +# +ifcapable trigger { + do_test tkt3201-4.0 { + db eval { + CREATE TABLE t4(x); + CREATE TABLE t4_log(x); + CREATE TRIGGER r4_1 AFTER INSERT ON t4 WHEN new.x=1 BEGIN + INSERT INTO t4_log(x) VALUES(new.x); + END; + CREATE TRIGGER r4_2 AFTER INSERT ON t4 WHEN new.x=2 BEGIN + INSERT INTO t4_log(x) VALUES(new.x); + END; + CREATE TRIGGER r4_3 AFTER INSERT ON t4 WHEN new.x=3 BEGIN + INSERT INTO t4_log(x) VALUES(new.x); + END; + CREATE TRIGGER r4_4 AFTER INSERT ON t4 WHEN new.x=4 BEGIN + INSERT INTO t4_log(x) VALUES(new.x); + END; + INSERT INTO t4 VALUES(1); + INSERT INTO t4 VALUES(2); + INSERT INTO t4 VALUES(3); + INSERT INTO t4 VALUES(4); + SELECT * FROM t4_log; + } + } {1 2 3 4} +} + + + finish_test Index: test/tkt3731.test ================================================================== --- test/tkt3731.test +++ test/tkt3731.test @@ -15,10 +15,15 @@ source $testdir/tester.tcl ifcapable {!trigger} { finish_test return } + +# The tests in this file were written before SQLite supported recursive +# trigger invocation, and some tests depend on that to pass. So disable +# recursive triggers for this file. +catchsql { pragma recursive_triggers = off } do_test tkt3731-1.1 { execsql { CREATE TABLE t1(a PRIMARY KEY, b); CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN ADDED test/tkt3810.test Index: test/tkt3810.test ================================================================== --- /dev/null +++ test/tkt3810.test @@ -0,0 +1,87 @@ +# 2009 August 1 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Tests to make sure #3810 is fixed. +# +# $Id: tkt3810.test,v 1.4 2009/08/06 17:43:31 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable {!trigger} { + finish_test + return +} + +# Create a table using the first database connection. +# +do_test tkt3810-1.1 { + execsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(123); + SELECT * FROM t1; + CREATE TABLE t2(y); + CREATE TABLE t3(z); + } +} 123 + +# Create a second connection to the same database. Make sure the +# schema of the database has been parsed by the second connection. +# +do_test tkt3810-2 { + sqlite3 db2 test.db + execsql { + SELECT * FROM t1; + } db2 +} 123 + +# DROP the table using the second connection. The table no longer exists +# but the first connection does not yet know this. Then try to create a TEMP +# trigger in the first connection that references the table that was dropped. +# +do_test tkt3810-3 { + execsql {DROP TABLE t1} db2 + execsql { + CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN + INSERT INTO t2 VALUES(new.rowid); + END; + } + catchsql { + SELECT * FROM t3; + } +} {0 {}} + +# Trigger still exists in the sqlite_temp_master table, but now it is +# an orphan. +# +do_test tkt3810-4 { + execsql {SELECT name FROM sqlite_temp_master ORDER BY name} +} {r1} + +# Because it is an orphan, it cannot be dropped. +# +do_test tkt3810-5 { + catchsql {DROP TRIGGER r1} +} {1 {no such trigger: r1}} + +# Create a table t1 then drop the table in order to drop the orphaned +# trigger. +# +do_test tkt3810-6 { + execsql {CREATE TABLE t1(x)} db2 + execsql {DROP TABLE t1} + execsql { + SELECT name FROM sqlite_temp_master; + } +} {} + +db2 close + +finish_test Index: test/tkt3832.test ================================================================== --- test/tkt3832.test +++ test/tkt3832.test @@ -16,10 +16,14 @@ # # $Id: tkt3832.test,v 1.1 2009/05/01 02:08:04 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable {!trigger} { + finish_test + return +} do_test tkt3832-1.1 { db eval { CREATE TABLE t1(a INT, b INTEGER PRIMARY KEY); Index: test/tkt3838.test ================================================================== --- test/tkt3838.test +++ test/tkt3838.test @@ -18,10 +18,14 @@ # $Id: tkt3838.test,v 1.1 2009/05/05 12:54:50 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable !altertable { + finish_test + return +} do_test tkt3838-1.1 { db eval { PRAGMA encoding=UTF16; CREATE TABLE t1(x); Index: test/tkt3929.test ================================================================== --- test/tkt3929.test +++ test/tkt3929.test @@ -13,10 +13,14 @@ # # $Id: tkt3929.test,v 1.1 2009/06/23 11:53:09 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl +ifcapable {!trigger} { + finish_test + return +} do_test tkt3929-1.0 { execsql { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); ADDED test/tkt3935.test Index: test/tkt3935.test ================================================================== --- /dev/null +++ test/tkt3935.test @@ -0,0 +1,57 @@ +# 2009 July 1 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket #3935 has been fixed. +# +# $Id: tkt3935.test,v 1.2 2009/07/01 16:12:08 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt3935.1 { + execsql { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + } +} {} + +do_test tkt3935.2 { + execsql { SELECT j1.b FROM ( SELECT * FROM t1 INNER JOIN t2 ON a=c ) AS j1 } +} {} +do_test tkt3935.3 { + execsql { SELECT j1.b FROM (t1 INNER JOIN t2 ON a=c) AS j1 } +} {} + + +do_test tkt3935.4 { + catchsql { SELECT a FROM (t1) AS t ON b USING(a) } +} {1 {a JOIN clause is required before ON}} +do_test tkt3935.5 { + catchsql { SELECT a FROM (t1) AS t ON b } +} {1 {a JOIN clause is required before ON}} +do_test tkt3935.6 { + catchsql { SELECT a FROM (SELECT * FROM t1) AS t ON b USING(a) } +} {1 {a JOIN clause is required before ON}} +do_test tkt3935.7 { + catchsql { SELECT a FROM (SELECT * FROM t1) AS t ON b } +} {1 {a JOIN clause is required before ON}} +do_test tkt3935.8 { + catchsql { SELECT a FROM t1 AS t ON b } +} {1 {a JOIN clause is required before ON}} +do_test tkt3935.9 { + catchsql { SELECT a FROM t1 AS t ON b USING(a) } +} {1 {a JOIN clause is required before ON}} +do_test tkt3935.10 { + catchsql { SELECT a FROM t1 AS t USING(a) } +} {1 {a JOIN clause is required before USING}} + +finish_test ADDED test/tkt3992.test Index: test/tkt3992.test ================================================================== --- /dev/null +++ test/tkt3992.test @@ -0,0 +1,81 @@ +# 2001 September 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# $Id: tkt3992.test,v 1.1 2009/07/27 10:05:06 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt3992-1.1 { + execsql { + CREATE TABLE parameters1( + mountcnt INT NOT NULL CHECK (typeof(mountcnt) == 'integer'), + version REAL NOT NULL + ); + INSERT INTO parameters1(mountcnt, version) VALUES(1, 1.0); + + CREATE TABLE parameters2( + mountcnt INT NOT NULL CHECK (typeof(mountcnt) == 'integer'), + version REAL CHECK (typeof(version) == 'real') + ); + INSERT INTO parameters2(mountcnt, version) VALUES(1, 1.0); + } +} {} + +do_test tkt3992-1.2 { + execsql { + UPDATE parameters1 SET mountcnt = mountcnt + 1; + SELECT * FROM parameters1; + } +} {2 1.0} + +do_test tkt3992-1.3 { + execsql { + UPDATE parameters2 SET mountcnt = mountcnt + 1; + SELECT * FROM parameters2; + } +} {2 1.0} + +ifcapable altertable { + do_test tkt3992-2.1 { + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + ALTER TABLE t1 ADD COLUMN c DEFAULT 3; + SELECT * FROM t1; + } + } {1 2 3} + do_test tkt3992-2.2 { + execsql { + UPDATE t1 SET a = 'one'; + SELECT * FROM t1; + } + } {one 2 3} +} + +ifcapable trigger { + db function tcl eval + do_test tkt3992-2.3 { + execsql { + CREATE TABLE t2(a REAL, b REAL, c REAL); + INSERT INTO t2 VALUES(1, 2, 3); + CREATE TRIGGER tr2 BEFORE UPDATE ON t2 BEGIN + SELECT tcl('set res', typeof(new.c)); + END; + + UPDATE t2 SET a = 'I'; + } + set res + } {real} +} + + +finish_test ADDED test/tkt3997.test Index: test/tkt3997.test ================================================================== --- /dev/null +++ test/tkt3997.test @@ -0,0 +1,73 @@ +# 2001 September 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Tests to make sure #3997 is fixed. +# +# $Id: tkt3997.test,v 1.1 2009/07/28 13:30:31 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +proc reverse {lhs rhs} { + return [string compare $rhs $lhs] +} +proc usual {lhs rhs} { + return [string compare $lhs $rhs] +} + +db collate reverse reverse +db collate usual usual + +do_test tkt3997-1.1 { + execsql { + create table mytext(name BLOB); + INSERT INTO mytext VALUES('abc'); + INSERT INTO mytext VALUES('acd'); + INSERT INTO mytext VALUES('afe'); + } +} {} +do_test tkt3997-1.2 { + execsql { + SELECT name + FROM mytext + ORDER BY name COLLATE reverse + } +} {afe acd abc} +do_test tkt3997-1.3 { + execsql { + SELECT name + FROM (SELECT name FROM mytext) + ORDER BY name COLLATE reverse + } +} {afe acd abc} + +do_test tkt3997-2.1 { + execsql { + CREATE TABLE mytext2(name COLLATE reverse); + INSERT INTO mytext2 SELECT name FROM mytext; + } +} {} +do_test tkt3997-2.2 { + execsql { + SELECT name + FROM (SELECT name FROM mytext2) + ORDER BY name + } +} {afe acd abc} +do_test tkt3997-2.3 { + execsql { + SELECT name + FROM (SELECT name FROM mytext2) + ORDER BY name COLLATE usual + } +} {abc acd afe} + +finish_test ADDED test/tkt4018.test Index: test/tkt4018.test ================================================================== --- /dev/null +++ test/tkt4018.test @@ -0,0 +1,89 @@ +# 2009 August 20 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests to verify that ticket #4018 has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +proc testsql {sql} { + set fd [open tf_main.tcl w] + puts $fd [subst -nocommands { + sqlite3_test_control_pending_byte 0x0010000 + sqlite3 db test.db + set rc [catch { db eval {$sql} } msg] + puts -nonewline "[set rc] {[set msg]}" + flush stdout + exit + }] + close $fd + set fd [open "| [info nameofexec] ./tf_main.tcl" r] + set res [read $fd] + close $fd + return $res +} + +do_test tkt4018-1.1 { + execsql { + CREATE TABLE t1(a, b); + BEGIN; + SELECT * FROM t1; + } +} {} + +# The database is locked by connection [db]. Open and close a second +# connection to test.db 10000 times. If file-descriptors are not being +# reused, then the process will quickly exceed its maximum number of +# file descriptors (1024 by default on linux). +do_test tkt4018-1.2 { + for {set i 0} {$i < 10000} {incr i} { + sqlite3 db2 test.db + db2 close + } +} {} + +# Now check that connection [db] is still holding a SHARED lock by +# having a second process try to write the db. +do_test tkt4018-1.3 { + testsql {INSERT INTO t1 VALUES(3, 4)} +} {1 {database is locked}} + +# Sanity checking. Have [db] release the lock and then retry the +# INSERT from the previous test case. +do_test tkt4018-1.4 { + db eval COMMIT + testsql {INSERT INTO t1 VALUES(3, 4)} +} {0 {}} + +# Check that reusing a file descriptor cannot change a read-only +# connection into a read-write connection. +do_test tkt4018-2.1 { + sqlite3 db2 test.db + execsql {INSERT INTO t1 VALUES(1, 2)} db2 +} {} +do_test tkt4018-2.2 { + execsql { + BEGIN; + SELECT * FROM t1 ORDER BY a; + } +} {1 2 3 4} +do_test tkt4018-2.3 { + db2 close + sqlite3 db2 test.db -readonly 1 + execsql COMMIT + catchsql {INSERT INTO t1 VALUES(5, 6)} db2 +} {1 {attempt to write a readonly database}} +db2 close + +finish_test Index: test/trigger1.test ================================================================== --- test/trigger1.test +++ test/trigger1.test @@ -61,101 +61,101 @@ SELECT * FROM sqlite_master; END; } } {1 {near "STATEMENT": syntax error}} execsql { - CREATE TRIGGER tr1 INSERT ON t1 BEGIN - INSERT INTO t1 values(1); - END; + CREATE TRIGGER tr1 INSERT ON t1 BEGIN + INSERT INTO t1 values(1); + END; } do_test trigger1-1.2.0 { catchsql { - CREATE TRIGGER IF NOT EXISTS tr1 DELETE ON t1 BEGIN - SELECT * FROM sqlite_master; - END + CREATE TRIGGER IF NOT EXISTS tr1 DELETE ON t1 BEGIN + SELECT * FROM sqlite_master; + END } } {0 {}} do_test trigger1-1.2.1 { catchsql { - CREATE TRIGGER tr1 DELETE ON t1 BEGIN - SELECT * FROM sqlite_master; - END + CREATE TRIGGER tr1 DELETE ON t1 BEGIN + SELECT * FROM sqlite_master; + END } } {1 {trigger tr1 already exists}} do_test trigger1-1.2.2 { catchsql { - CREATE TRIGGER "tr1" DELETE ON t1 BEGIN - SELECT * FROM sqlite_master; - END + CREATE TRIGGER "tr1" DELETE ON t1 BEGIN + SELECT * FROM sqlite_master; + END } } {1 {trigger "tr1" already exists}} do_test trigger1-1.2.3 { catchsql { - CREATE TRIGGER [tr1] DELETE ON t1 BEGIN - SELECT * FROM sqlite_master; - END + CREATE TRIGGER [tr1] DELETE ON t1 BEGIN + SELECT * FROM sqlite_master; + END } } {1 {trigger [tr1] already exists}} do_test trigger1-1.3 { catchsql { - BEGIN; - CREATE TRIGGER tr2 INSERT ON t1 BEGIN - SELECT * from sqlite_master; END; + BEGIN; + CREATE TRIGGER tr2 INSERT ON t1 BEGIN + SELECT * from sqlite_master; END; ROLLBACK; - CREATE TRIGGER tr2 INSERT ON t1 BEGIN - SELECT * from sqlite_master; END; + CREATE TRIGGER tr2 INSERT ON t1 BEGIN + SELECT * from sqlite_master; END; } } {0 {}} do_test trigger1-1.4 { catchsql { - DROP TRIGGER IF EXISTS tr1; - CREATE TRIGGER tr1 DELETE ON t1 BEGIN - SELECT * FROM sqlite_master; - END + DROP TRIGGER IF EXISTS tr1; + CREATE TRIGGER tr1 DELETE ON t1 BEGIN + SELECT * FROM sqlite_master; + END } } {0 {}} do_test trigger1-1.5 { execsql { - BEGIN; - DROP TRIGGER tr2; - ROLLBACK; - DROP TRIGGER tr2; + BEGIN; + DROP TRIGGER tr2; + ROLLBACK; + DROP TRIGGER tr2; } } {} do_test trigger1-1.6.1 { catchsql { - DROP TRIGGER IF EXISTS biggles; + DROP TRIGGER IF EXISTS biggles; } } {0 {}} do_test trigger1-1.6.2 { catchsql { - DROP TRIGGER biggles; + DROP TRIGGER biggles; } } {1 {no such trigger: biggles}} do_test trigger1-1.7 { catchsql { - DROP TABLE t1; - DROP TRIGGER tr1; + DROP TABLE t1; + DROP TRIGGER tr1; } } {1 {no such trigger: tr1}} ifcapable tempdb { execsql { CREATE TEMP TABLE temp_table(a); } do_test trigger1-1.8 { execsql { - CREATE TRIGGER temp_trig UPDATE ON temp_table BEGIN - SELECT * from sqlite_master; - END; - SELECT count(*) FROM sqlite_master WHERE name = 'temp_trig'; + CREATE TRIGGER temp_trig UPDATE ON temp_table BEGIN + SELECT * from sqlite_master; + END; + SELECT count(*) FROM sqlite_master WHERE name = 'temp_trig'; } } {0} } do_test trigger1-1.9 { @@ -401,18 +401,18 @@ execsql {SELECT type, name FROM sqlite_master} } [concat $view_v1 {table t2}] do_test trigger1-6.2 { execsql { CREATE TRIGGER t2 BEFORE DELETE ON t2 BEGIN - SELECT RAISE(ABORT,'deletes are not allows'); + SELECT RAISE(ABORT,'deletes are not permitted'); END; SELECT type, name FROM sqlite_master; } } [concat $view_v1 {table t2 trigger t2}] do_test trigger1-6.3 { catchsql {DELETE FROM t2} -} {1 {deletes are not allows}} +} {1 {deletes are not permitted}} do_test trigger1-6.4 { execsql {SELECT * FROM t2} } {3 4 7 8} do_test trigger1-6.5 { db close @@ -637,6 +637,66 @@ } {1 {datatype mismatch}} do_test trigger1-15.2 { catchsql { INSERT INTO tA VALUES('abc', 2, 3) } } {1 {datatype mismatch}} +# Ticket #3947: Do not allow qualified table names on INSERT, UPDATE, and +# DELETE statements within triggers. Actually, this has never been allowed +# by the grammar. But the error message is confusing: one simply gets a +# "syntax error". That has now been changed to give a full error message. +# +do_test trigger1-16.1 { + db eval { + CREATE TABLE t16(a,b,c); + CREATE INDEX t16a ON t16(a); + CREATE INDEX t16b ON t16(b); + } + catchsql { + CREATE TRIGGER main.t16err1 AFTER INSERT ON tA BEGIN + INSERT INTO main.t16 VALUES(1,2,3); + END; + } +} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}} +do_test trigger1-16.2 { + catchsql { + CREATE TRIGGER main.t16err2 AFTER INSERT ON tA BEGIN + UPDATE main.t16 SET rowid=rowid+1; + END; + } +} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}} +do_test trigger1-16.3 { + catchsql { + CREATE TRIGGER main.t16err3 AFTER INSERT ON tA BEGIN + DELETE FROM main.t16; + END; + } +} {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}} +do_test trigger1-16.4 { + catchsql { + CREATE TRIGGER main.t16err4 AFTER INSERT ON tA BEGIN + UPDATE t16 NOT INDEXED SET rowid=rowid+1; + END; + } +} {1 {the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers}} +do_test trigger1-16.5 { + catchsql { + CREATE TRIGGER main.t16err5 AFTER INSERT ON tA BEGIN + UPDATE t16 INDEXED BY t16a SET rowid=rowid+1 WHERE a=1; + END; + } +} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}} +do_test trigger1-16.6 { + catchsql { + CREATE TRIGGER main.t16err6 AFTER INSERT ON tA BEGIN + DELETE FROM t16 NOT INDEXED WHERE a=123; + END; + } +} {1 {the NOT INDEXED clause is not allowed on UPDATE or DELETE statements within triggers}} +do_test trigger1-16.7 { + catchsql { + CREATE TRIGGER main.t16err7 AFTER INSERT ON tA BEGIN + DELETE FROM t16 INDEXED BY t16a WHERE a=123; + END; + } +} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}} + finish_test Index: test/trigger2.test ================================================================== --- test/trigger2.test +++ test/trigger2.test @@ -51,10 +51,15 @@ source $testdir/tester.tcl ifcapable {!trigger} { finish_test return } + +# The tests in this file were written before SQLite supported recursive +# trigger invocation, and some tests depend on that to pass. So disable +# recursive triggers for this file. +catchsql { pragma recursive_triggers = off } # 1. ifcapable subquery { set ii 0 set tbl_definitions [list \ Index: test/trigger3.test ================================================================== --- test/trigger3.test +++ test/trigger3.test @@ -15,69 +15,73 @@ source $testdir/tester.tcl ifcapable {!trigger} { finish_test return } + +# The tests in this file were written before SQLite supported recursive } +# trigger invocation, and some tests depend on that to pass. So disable +# recursive triggers for this file. +catchsql { pragma recursive_triggers = off } # Test that we can cause ROLLBACK, FAIL and ABORT correctly -# catchsql { DROP TABLE tbl; } -catchsql { CREATE TABLE tbl (a, b, c) } - +# +catchsql { CREATE TABLE tbl(a, b ,c) } execsql { CREATE TRIGGER before_tbl_insert BEFORE INSERT ON tbl BEGIN SELECT CASE - WHEN (new.a = 4) THEN RAISE(IGNORE) END; + WHEN (new.a = 4) THEN RAISE(IGNORE) END; END; CREATE TRIGGER after_tbl_insert AFTER INSERT ON tbl BEGIN SELECT CASE - WHEN (new.a = 1) THEN RAISE(ABORT, 'Trigger abort') - WHEN (new.a = 2) THEN RAISE(FAIL, 'Trigger fail') - WHEN (new.a = 3) THEN RAISE(ROLLBACK, 'Trigger rollback') END; + WHEN (new.a = 1) THEN RAISE(ABORT, 'Trigger abort') + WHEN (new.a = 2) THEN RAISE(FAIL, 'Trigger fail') + WHEN (new.a = 3) THEN RAISE(ROLLBACK, 'Trigger rollback') END; END; } # ABORT do_test trigger3-1.1 { catchsql { - BEGIN; + BEGIN; INSERT INTO tbl VALUES (5, 5, 6); INSERT INTO tbl VALUES (1, 5, 6); } } {1 {Trigger abort}} do_test trigger3-1.2 { execsql { - SELECT * FROM tbl; - ROLLBACK; + SELECT * FROM tbl; + ROLLBACK; } } {5 5 6} do_test trigger3-1.3 { execsql {SELECT * FROM tbl} } {} # FAIL do_test trigger3-2.1 { catchsql { - BEGIN; + BEGIN; INSERT INTO tbl VALUES (5, 5, 6); INSERT INTO tbl VALUES (2, 5, 6); } } {1 {Trigger fail}} do_test trigger3-2.2 { execsql { - SELECT * FROM tbl; - ROLLBACK; + SELECT * FROM tbl; + ROLLBACK; } } {5 5 6 2 5 6} # ROLLBACK do_test trigger3-3.1 { catchsql { - BEGIN; + BEGIN; INSERT INTO tbl VALUES (5, 5, 6); INSERT INTO tbl VALUES (3, 5, 6); } } {1 {Trigger rollback}} do_test trigger3-3.2 { execsql { - SELECT * FROM tbl; + SELECT * FROM tbl; } } {} # Verify that a ROLLBACK trigger works like a FAIL trigger if # we are not within a transaction. Ticket #3035. @@ -93,19 +97,19 @@ } {} # IGNORE do_test trigger3-4.1 { catchsql { - BEGIN; + BEGIN; INSERT INTO tbl VALUES (5, 5, 6); INSERT INTO tbl VALUES (4, 5, 6); } } {0 {}} do_test trigger3-4.2 { execsql { - SELECT * FROM tbl; - ROLLBACK; + SELECT * FROM tbl; + ROLLBACK; } } {5 5 6} # Check that we can also do RAISE(IGNORE) for UPDATE and DELETE execsql {DROP TABLE tbl;} @@ -112,43 +116,43 @@ execsql {CREATE TABLE tbl (a, b, c);} execsql {INSERT INTO tbl VALUES(1, 2, 3);} execsql {INSERT INTO tbl VALUES(4, 5, 6);} execsql { CREATE TRIGGER before_tbl_update BEFORE UPDATE ON tbl BEGIN - SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END; + SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END; END; CREATE TRIGGER before_tbl_delete BEFORE DELETE ON tbl BEGIN - SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END; + SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END; END; } do_test trigger3-5.1 { execsql { - UPDATE tbl SET c = 10; - SELECT * FROM tbl; + UPDATE tbl SET c = 10; + SELECT * FROM tbl; } } {1 2 3 4 5 10} do_test trigger3-5.2 { execsql { - DELETE FROM tbl; - SELECT * FROM tbl; + DELETE FROM tbl; + SELECT * FROM tbl; } } {1 2 3} # Check that RAISE(IGNORE) works correctly for nested triggers: execsql {CREATE TABLE tbl2(a, b, c)} execsql { CREATE TRIGGER after_tbl2_insert AFTER INSERT ON tbl2 BEGIN - UPDATE tbl SET c = 10; + UPDATE tbl SET c = 10; INSERT INTO tbl2 VALUES (new.a, new.b, new.c); END; } do_test trigger3-6 { execsql { - INSERT INTO tbl2 VALUES (1, 2, 3); - SELECT * FROM tbl2; - SELECT * FROM tbl; + INSERT INTO tbl2 VALUES (1, 2, 3); + SELECT * FROM tbl2; + SELECT * FROM tbl; } } {1 2 3 1 2 3 1 2 3} # Check that things also work for view-triggers @@ -155,29 +159,29 @@ ifcapable view { execsql {CREATE VIEW tbl_view AS SELECT * FROM tbl} execsql { CREATE TRIGGER tbl_view_insert INSTEAD OF INSERT ON tbl_view BEGIN - SELECT CASE WHEN (new.a = 1) THEN RAISE(ROLLBACK, 'View rollback') - WHEN (new.a = 2) THEN RAISE(IGNORE) - WHEN (new.a = 3) THEN RAISE(ABORT, 'View abort') END; + SELECT CASE WHEN (new.a = 1) THEN RAISE(ROLLBACK, 'View rollback') + WHEN (new.a = 2) THEN RAISE(IGNORE) + WHEN (new.a = 3) THEN RAISE(ABORT, 'View abort') END; END; } do_test trigger3-7.1 { catchsql { - INSERT INTO tbl_view VALUES(1, 2, 3); + INSERT INTO tbl_view VALUES(1, 2, 3); } } {1 {View rollback}} do_test trigger3-7.2 { catchsql { - INSERT INTO tbl_view VALUES(2, 2, 3); + INSERT INTO tbl_view VALUES(2, 2, 3); } } {0 {}} do_test trigger3-7.3 { catchsql { - INSERT INTO tbl_view VALUES(3, 2, 3); + INSERT INTO tbl_view VALUES(3, 2, 3); } } {1 {View abort}} } ;# ifcapable view Index: test/trigger8.test ================================================================== --- test/trigger8.test +++ test/trigger8.test @@ -30,10 +30,11 @@ set nStatement 10000 if {$tcl_platform(platform) == "symbian"} { set nStatement 1000 } +set nStatement 5 do_test trigger8-1.1 { execsql { CREATE TABLE t1(x); CREATE TABLE t2(y); } Index: test/trigger9.test ================================================================== --- test/trigger9.test +++ test/trigger9.test @@ -73,11 +73,11 @@ SELECT * FROM t2; } } {1 2 3} do_test trigger9-1.3.2 { has_rowdata {DELETE FROM t1} -} 1 +} 0 do_test trigger9-1.3.3 { execsql { ROLLBACK } } {} do_test trigger9-1.4.1 { execsql { BEGIN; @@ -88,11 +88,11 @@ SELECT * FROM t2; } } {1} do_test trigger9-1.4.2 { has_rowdata {DELETE FROM t1} -} 1 +} 0 do_test trigger9-1.4.3 { execsql { ROLLBACK } } {} do_test trigger9-1.5.1 { execsql { BEGIN; @@ -118,11 +118,11 @@ SELECT * FROM t2; } } {1 2 3} do_test trigger9-1.6.2 { has_rowdata {UPDATE t1 SET y = ''} -} 1 +} 0 do_test trigger9-1.6.3 { execsql { ROLLBACK } } {} do_test trigger9-1.7.1 { execsql { BEGIN; @@ -133,11 +133,11 @@ SELECT * FROM t2; } } {2 3} do_test trigger9-1.7.2 { has_rowdata {UPDATE t1 SET y = ''} -} 1 +} 0 do_test trigger9-1.7.3 { execsql { ROLLBACK } } {} do_test trigger9-3.1 { execsql { CREATE TABLE t3(a, b); Index: test/triggerA.test ================================================================== --- test/triggerA.test +++ test/triggerA.test @@ -77,11 +77,11 @@ do_test triggerA-1.6 { db eval { CREATE VIEW v5 AS SELECT x, b FROM t1, t2 WHERE y=c; SELECT * FROM v5; } -} {1 103 2 203 3 305 4 404 5 504 6 603 7 705 8 805 9 904 10 1003} +} {10 1003 9 904 8 805 7 705 6 603 5 504 4 404 3 305 2 203 1 103} # Create INSTEAD OF triggers on the views. Run UPDATE and DELETE statements # using those triggers. Verify correct operation. # do_test triggerA-2.1 { ADDED test/triggerC.test Index: test/triggerC.test ================================================================== --- /dev/null +++ test/triggerC.test @@ -0,0 +1,791 @@ +# 2009 August 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice', here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable {!trigger} { + finish_test + return +} + +#------------------------------------------------------------------------- +# Test organization: +# +# triggerC-1.*: Haphazardly designed trigger related tests that were useful +# during an upgrade of the triggers sub-system. +# +# triggerC-2.*: +# +# triggerC-3.*: +# +# triggerC-4.*: +# +# triggerC-5.*: Test that when recursive triggers are enabled DELETE +# triggers are fired when rows are deleted as part of OR +# REPLACE conflict resolution. And that they are not fired +# if recursive triggers are not enabled. +# +# triggerC-6.*: Test that the recursive_triggers pragma returns correct +# results when invoked without an argument. +# + +# Enable recursive triggers for this file. +# +execsql { PRAGMA recursive_triggers = on } + +#sqlite3_db_config_lookaside db 0 0 0 + +#------------------------------------------------------------------------- +# This block of tests, triggerC-1.*, are not aimed at any specific +# property of the triggers sub-system. They were created to debug +# specific problems while modifying SQLite to support recursive +# triggers. They are left here in case they can help debug the +# same problems again. +# +do_test triggerC-1.1 { + execsql { + CREATE TABLE t1(a, b, c); + CREATE TABLE log(t, a1, b1, c1, a2, b2, c2); + CREATE TRIGGER trig1 BEFORE INSERT ON t1 BEGIN + INSERT INTO log VALUES('before', NULL, NULL, NULL, new.a, new.b, new.c); + END; + CREATE TRIGGER trig2 AFTER INSERT ON t1 BEGIN + INSERT INTO log VALUES('after', NULL, NULL, NULL, new.a, new.b, new.c); + END; + CREATE TRIGGER trig3 BEFORE UPDATE ON t1 BEGIN + INSERT INTO log VALUES('before', old.a,old.b,old.c, new.a,new.b,new.c); + END; + CREATE TRIGGER trig4 AFTER UPDATE ON t1 BEGIN + INSERT INTO log VALUES('after', old.a,old.b,old.c, new.a,new.b,new.c); + END; + + CREATE TRIGGER trig5 BEFORE DELETE ON t1 BEGIN + INSERT INTO log VALUES('before', old.a,old.b,old.c, NULL,NULL,NULL); + END; + CREATE TRIGGER trig6 AFTER DELETE ON t1 BEGIN + INSERT INTO log VALUES('after', old.a,old.b,old.c, NULL,NULL,NULL); + END; + } +} {} +do_test triggerC-1.2 { + execsql { + INSERT INTO t1 VALUES('A', 'B', 'C'); + SELECT * FROM log; + } +} {before {} {} {} A B C after {} {} {} A B C} +do_test triggerC-1.3 { + execsql { SELECT * FROM t1 } +} {A B C} +do_test triggerC-1.4 { + execsql { + DELETE FROM log; + UPDATE t1 SET a = 'a'; + SELECT * FROM log; + } +} {before A B C a B C after A B C a B C} +do_test triggerC-1.5 { + execsql { SELECT * FROM t1 } +} {a B C} +do_test triggerC-1.6 { + execsql { + DELETE FROM log; + DELETE FROM t1; + SELECT * FROM log; + } +} {before a B C {} {} {} after a B C {} {} {}} +do_test triggerC-1.7 { + execsql { SELECT * FROM t1 } +} {} +do_test triggerC-1.8 { + execsql { + CREATE TABLE t4(a, b); + CREATE TRIGGER t4t AFTER DELETE ON t4 BEGIN + SELECT RAISE(ABORT, 'delete is not supported'); + END; + } +} {} +do_test triggerC-1.9 { + execsql { INSERT INTO t4 VALUES(1, 2) } + catchsql { DELETE FROM t4 } +} {1 {delete is not supported}} +do_test triggerC-1.10 { + execsql { SELECT * FROM t4 } +} {1 2} +do_test triggerC-1.11 { + execsql { + CREATE TABLE t5 (a primary key, b, c); + INSERT INTO t5 values (1, 2, 3); + CREATE TRIGGER au_tbl AFTER UPDATE ON t5 BEGIN + UPDATE OR IGNORE t5 SET a = new.a, c = 10; + END; + } +} {} +do_test triggerC-1.12 { + catchsql { UPDATE OR REPLACE t5 SET a = 4 WHERE a = 1 } +} {1 {too many levels of trigger recursion}} +do_test triggerC-1.13 { + execsql { + CREATE TABLE t6(a INTEGER PRIMARY KEY, b); + INSERT INTO t6 VALUES(1, 2); + create trigger r1 after update on t6 for each row begin + SELECT 1; + end; + UPDATE t6 SET a=a; + } +} {} +do_test triggerC-1.14 { + execsql { + DROP TABLE t1; + CREATE TABLE cnt(n); + INSERT INTO cnt VALUES(0); + CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE, c, d, e); + CREATE INDEX t1cd ON t1(c,d); + CREATE TRIGGER t1r1 AFTER UPDATE ON t1 BEGIN UPDATE cnt SET n=n+1; END; + INSERT INTO t1 VALUES(1,2,3,4,5); + INSERT INTO t1 VALUES(6,7,8,9,10); + INSERT INTO t1 VALUES(11,12,13,14,15); + } +} {} +do_test triggerC-1.15 { + catchsql { UPDATE OR ROLLBACK t1 SET a=100 } +} {1 {PRIMARY KEY must be unique}} + + +#------------------------------------------------------------------------- +# This block of tests, triggerC-2.*, tests that recursive trigger +# programs (triggers that fire themselves) work. More specifically, +# this block focuses on recursive INSERT triggers. +# +do_test triggerC-2.1.0 { + execsql { + CREATE TABLE t2(a PRIMARY KEY); + } +} {} + +foreach {n tdefn rc} { + 1 { + CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN + INSERT INTO t2 VALUES(new.a - 1); + END; + } {0 {10 9 8 7 6 5 4 3 2 1 0}} + + 2 { + CREATE TRIGGER t2_trig AFTER INSERT ON t2 BEGIN + SELECT CASE WHEN new.a==2 THEN RAISE(IGNORE) ELSE NULL END; + INSERT INTO t2 VALUES(new.a - 1); + END; + } {0 {10 9 8 7 6 5 4 3 2}} + + 3 { + CREATE TRIGGER t2_trig BEFORE INSERT ON t2 WHEN (new.a>0) BEGIN + INSERT INTO t2 VALUES(new.a - 1); + END; + } {0 {0 1 2 3 4 5 6 7 8 9 10}} + + 4 { + CREATE TRIGGER t2_trig BEFORE INSERT ON t2 BEGIN + SELECT CASE WHEN new.a==2 THEN RAISE(IGNORE) ELSE NULL END; + INSERT INTO t2 VALUES(new.a - 1); + END; + } {0 {3 4 5 6 7 8 9 10}} + + 5 { + CREATE TRIGGER t2_trig BEFORE INSERT ON t2 BEGIN + INSERT INTO t2 VALUES(new.a - 1); + END; + } {1 {too many levels of trigger recursion}} + + 6 { + CREATE TRIGGER t2_trig AFTER INSERT ON t2 WHEN (new.a>0) BEGIN + INSERT OR IGNORE INTO t2 VALUES(new.a); + END; + } {0 10} + + 7 { + CREATE TRIGGER t2_trig BEFORE INSERT ON t2 WHEN (new.a>0) BEGIN + INSERT OR IGNORE INTO t2 VALUES(new.a); + END; + } {1 {too many levels of trigger recursion}} +} { + do_test triggerC-2.1.$n { + catchsql { DROP TRIGGER t2_trig } + execsql { DELETE FROM t2 } + execsql $tdefn + catchsql { + INSERT INTO t2 VALUES(10); + SELECT * FROM t2; + } + } $rc +} + +do_test triggerC-2.2 { + execsql { + CREATE TABLE t22(x); + + CREATE TRIGGER t22a AFTER INSERT ON t22 BEGIN + INSERT INTO t22 SELECT x + (SELECT max(x) FROM t22) FROM t22; + END; + CREATE TRIGGER t22b BEFORE INSERT ON t22 BEGIN + SELECT CASE WHEN (SELECT count(*) FROM t22) >= 100 + THEN RAISE(IGNORE) + ELSE NULL END; + END; + + INSERT INTO t22 VALUES(1); + SELECT count(*) FROM t22; + } +} {100} + +do_test triggerC-2.3 { + execsql { + CREATE TABLE t23(x PRIMARY KEY); + + CREATE TRIGGER t23a AFTER INSERT ON t23 BEGIN + INSERT INTO t23 VALUES(new.x + 1); + END; + + CREATE TRIGGER t23b BEFORE INSERT ON t23 BEGIN + SELECT CASE WHEN new.x>500 + THEN RAISE(IGNORE) + ELSE NULL END; + END; + + INSERT INTO t23 VALUES(1); + SELECT count(*) FROM t23; + } +} {500} + + +#----------------------------------------------------------------------- +# This block of tests, triggerC-3.*, test that SQLite throws an exception +# when it detects excessive recursion. +# +do_test triggerC-3.1.1 { + execsql { + CREATE TABLE t3(a, b); + CREATE TRIGGER t3i AFTER INSERT ON t3 BEGIN + DELETE FROM t3 WHERE rowid = new.rowid; + END; + CREATE TRIGGER t3d AFTER DELETE ON t3 BEGIN + INSERT INTO t3 VALUES(old.a, old.b); + END; + } +} {} +do_test triggerC-3.1.2 { + catchsql { INSERT INTO t3 VALUES(0,0) } +} {1 {too many levels of trigger recursion}} +do_test triggerC-3.1.3 { + execsql { SELECT * FROM t3 } +} {} + +do_test triggerC-3.2.1 { + execsql { + CREATE TABLE t3b(x); + CREATE TRIGGER t3bi AFTER INSERT ON t3b WHEN new.x<2000 BEGIN + INSERT INTO t3b VALUES(new.x+1); + END; + } + catchsql { + INSERT INTO t3b VALUES(1); + } +} {1 {too many levels of trigger recursion}} +do_test triggerC-3.2.2 { + db eval {SELECT * FROM t3b} +} {} + +do_test triggerC-3.3.1 { + catchsql { + INSERT INTO t3b VALUES(1001); + } +} {0 {}} +do_test triggerC-3.3.2 { + db eval {SELECT count(*), max(x), min(x) FROM t3b} +} {1000 2000 1001} + +do_test triggerC-3.4.1 { + catchsql { + DELETE FROM t3b; + INSERT INTO t3b VALUES(999); + } +} {1 {too many levels of trigger recursion}} +do_test triggerC-3.4.2 { + db eval {SELECT count(*), max(x), min(x) FROM t3b} +} {0 {} {}} + +do_test triggerC-3.5.1 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 100 + catchsql { + INSERT INTO t3b VALUES(1901); + } +} {0 {}} +do_test triggerC-3.5.2 { + db eval {SELECT count(*), max(x), min(x) FROM t3b} +} {100 2000 1901} + +do_test triggerC-3.5.3 { + catchsql { + DELETE FROM t3b; + INSERT INTO t3b VALUES(1900); + } +} {1 {too many levels of trigger recursion}} +do_test triggerC-3.5.4 { + db eval {SELECT count(*), max(x), min(x) FROM t3b} +} {0 {} {}} + +do_test triggerC-3.6.1 { + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1 + catchsql { + INSERT INTO t3b VALUES(2000); + } +} {0 {}} +do_test triggerC-3.6.2 { + db eval {SELECT count(*), max(x), min(x) FROM t3b} +} {1 2000 2000} + +do_test triggerC-3.6.3 { + catchsql { + DELETE FROM t3b; + INSERT INTO t3b VALUES(1999); + } +} {1 {too many levels of trigger recursion}} +do_test triggerC-3.6.4 { + db eval {SELECT count(*), max(x), min(x) FROM t3b} +} {0 {} {}} +sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000 + + +#----------------------------------------------------------------------- +# This next block of tests, triggerC-4.*, checks that affinity +# transformations and constraint processing is performed at the correct +# times relative to BEFORE and AFTER triggers. +# +# For an INSERT statement, for each row to be inserted: +# +# 1. Apply affinities to non-rowid values to be inserted. +# 2. Fire BEFORE triggers. +# 3. Process constraints. +# 4. Insert new record. +# 5. Fire AFTER triggers. +# +# If the value of the rowid field is to be automatically assigned, it is +# set to -1 in the new.* record. Even if it is explicitly set to NULL +# by the INSERT statement. +# +# For an UPDATE statement, for each row to be deleted: +# +# 1. Apply affinities to non-rowid values to be inserted. +# 2. Fire BEFORE triggers. +# 3. Process constraints. +# 4. Insert new record. +# 5. Fire AFTER triggers. +# +# For a DELETE statement, for each row to be deleted: +# +# 1. Fire BEFORE triggers. +# 2. Remove database record. +# 3. Fire AFTER triggers. +# +# When a numeric value that as an exact integer representation is stored +# in a column with REAL affinity, it is actually stored as an integer. +# These tests check that the typeof() such values is always 'real', +# not 'integer'. +# +# triggerC-4.1.*: Check that affinity transformations are made before +# triggers are invoked. +# +do_test triggerC-4.1.1 { + catchsql { DROP TABLE log } + catchsql { DROP TABLE t4 } + execsql { + CREATE TABLE log(t); + CREATE TABLE t4(a TEXT,b INTEGER,c REAL); + CREATE TRIGGER t4bi BEFORE INSERT ON t4 BEGIN + INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || + new.a || ' ' || typeof(new.a) || ' ' || + new.b || ' ' || typeof(new.b) || ' ' || + new.c || ' ' || typeof(new.c) + ); + END; + CREATE TRIGGER t4ai AFTER INSERT ON t4 BEGIN + INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || + new.a || ' ' || typeof(new.a) || ' ' || + new.b || ' ' || typeof(new.b) || ' ' || + new.c || ' ' || typeof(new.c) + ); + END; + CREATE TRIGGER t4bd BEFORE DELETE ON t4 BEGIN + INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || + old.a || ' ' || typeof(old.a) || ' ' || + old.b || ' ' || typeof(old.b) || ' ' || + old.c || ' ' || typeof(old.c) + ); + END; + CREATE TRIGGER t4ad AFTER DELETE ON t4 BEGIN + INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || + old.a || ' ' || typeof(old.a) || ' ' || + old.b || ' ' || typeof(old.b) || ' ' || + old.c || ' ' || typeof(old.c) + ); + END; + CREATE TRIGGER t4bu BEFORE UPDATE ON t4 BEGIN + INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || + old.a || ' ' || typeof(old.a) || ' ' || + old.b || ' ' || typeof(old.b) || ' ' || + old.c || ' ' || typeof(old.c) + ); + INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || + new.a || ' ' || typeof(new.a) || ' ' || + new.b || ' ' || typeof(new.b) || ' ' || + new.c || ' ' || typeof(new.c) + ); + END; + CREATE TRIGGER t4au AFTER UPDATE ON t4 BEGIN + INSERT INTO log VALUES(old.rowid || ' ' || typeof(old.rowid) || ' ' || + old.a || ' ' || typeof(old.a) || ' ' || + old.b || ' ' || typeof(old.b) || ' ' || + old.c || ' ' || typeof(old.c) + ); + INSERT INTO log VALUES(new.rowid || ' ' || typeof(new.rowid) || ' ' || + new.a || ' ' || typeof(new.a) || ' ' || + new.b || ' ' || typeof(new.b) || ' ' || + new.c || ' ' || typeof(new.c) + ); + END; + } +} {} +foreach {n insert log} { + + 2 { + INSERT INTO t4 VALUES('1', '1', '1'); + DELETE FROM t4; + } { + -1 integer 1 text 1 integer 1.0 real + 1 integer 1 text 1 integer 1.0 real + 1 integer 1 text 1 integer 1.0 real + 1 integer 1 text 1 integer 1.0 real + } + + 3 { + INSERT INTO t4(rowid,a,b,c) VALUES(45, 45, 45, 45); + DELETE FROM t4; + } { + 45 integer 45 text 45 integer 45.0 real + 45 integer 45 text 45 integer 45.0 real + 45 integer 45 text 45 integer 45.0 real + 45 integer 45 text 45 integer 45.0 real + } + + 4 { + INSERT INTO t4(rowid,a,b,c) VALUES(-42.0, -42.0, -42.0, -42.0); + DELETE FROM t4; + } { + -42 integer -42.0 text -42 integer -42.0 real + -42 integer -42.0 text -42 integer -42.0 real + -42 integer -42.0 text -42 integer -42.0 real + -42 integer -42.0 text -42 integer -42.0 real + } + + 5 { + INSERT INTO t4(rowid,a,b,c) VALUES(NULL, -42.4, -42.4, -42.4); + DELETE FROM t4; + } { + -1 integer -42.4 text -42.4 real -42.4 real + 1 integer -42.4 text -42.4 real -42.4 real + 1 integer -42.4 text -42.4 real -42.4 real + 1 integer -42.4 text -42.4 real -42.4 real + } + + 6 { + INSERT INTO t4 VALUES(7, 7, 7); + UPDATE t4 SET a=8, b=8, c=8; + } { + -1 integer 7 text 7 integer 7.0 real + 1 integer 7 text 7 integer 7.0 real + 1 integer 7 text 7 integer 7.0 real + 1 integer 8 text 8 integer 8.0 real + 1 integer 7 text 7 integer 7.0 real + 1 integer 8 text 8 integer 8.0 real + } + + 7 { + UPDATE t4 SET rowid=2; + } { + 1 integer 8 text 8 integer 8.0 real + 2 integer 8 text 8 integer 8.0 real + 1 integer 8 text 8 integer 8.0 real + 2 integer 8 text 8 integer 8.0 real + } + + 8 { + UPDATE t4 SET a='9', b='9', c='9'; + } { + 2 integer 8 text 8 integer 8.0 real + 2 integer 9 text 9 integer 9.0 real + 2 integer 8 text 8 integer 8.0 real + 2 integer 9 text 9 integer 9.0 real + } + + 9 { + UPDATE t4 SET a='9.1', b='9.1', c='9.1'; + } { + 2 integer 9 text 9 integer 9.0 real + 2 integer 9.1 text 9.1 real 9.1 real + 2 integer 9 text 9 integer 9.0 real + 2 integer 9.1 text 9.1 real 9.1 real + } +} { + do_test triggerC-4.1.$n { + eval concat [execsql " + DELETE FROM log; + $insert ; + SELECT * FROM log; + "] + } [join $log " "] +} + +#------------------------------------------------------------------------- +# This block of tests, triggerC-5.*, test that DELETE triggers are fired +# if a row is deleted as a result of OR REPLACE conflict resolution. +# +do_test triggerC-5.1.0 { + execsql { + DROP TABLE IF EXISTS t5; + CREATE TABLE t5(a INTEGER PRIMARY KEY, b); + CREATE UNIQUE INDEX t5i ON t5(b); + INSERT INTO t5 VALUES(1, 'a'); + INSERT INTO t5 VALUES(2, 'b'); + INSERT INTO t5 VALUES(3, 'c'); + + CREATE TABLE t5g(a, b, c); + CREATE TRIGGER t5t BEFORE DELETE ON t5 BEGIN + INSERT INTO t5g VALUES(old.a, old.b, (SELECT count(*) FROM t5)); + END; + } +} {} +foreach {n dml t5g t5} { + 1 "DELETE FROM t5 WHERE a=2" {2 b 3} {1 a 3 c} + 2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')" {2 b 3} {1 a 2 d 3 c} + 3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3" {2 b 3} {1 a 2 c} + 4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')" {2 b 3} {1 a 3 c 4 b} + 5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'" {2 b 3} {1 a 3 b} + 6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')" {2 b 3 3 c 2} {1 a 2 c} + 7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {1 a 3 2 b 2} {1 b} +} { + do_test triggerC-5.1.$n { + execsql " + BEGIN; + $dml ; + SELECT * FROM t5g; + SELECT * FROM t5; + ROLLBACK; + " + } [concat $t5g $t5] +} +do_test triggerC-5.2.0 { + execsql { + DROP TRIGGER t5t; + CREATE TRIGGER t5t AFTER DELETE ON t5 BEGIN + INSERT INTO t5g VALUES(old.a, old.b, (SELECT count(*) FROM t5)); + END; + } +} {} +foreach {n dml t5g t5} { + 1 "DELETE FROM t5 WHERE a=2" {2 b 2} {1 a 3 c} + 2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')" {2 b 2} {1 a 2 d 3 c} + 3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3" {2 b 2} {1 a 2 c} + 4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')" {2 b 2} {1 a 3 c 4 b} + 5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'" {2 b 2} {1 a 3 b} + 6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')" {2 b 2 3 c 1} {1 a 2 c} + 7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {1 a 2 2 b 1} {1 b} +} { + do_test triggerC-5.2.$n { + execsql " + BEGIN; + $dml ; + SELECT * FROM t5g; + SELECT * FROM t5; + ROLLBACK; + " + } [concat $t5g $t5] +} +do_test triggerC-5.3.0 { + execsql { PRAGMA recursive_triggers = off } +} {} +foreach {n dml t5g t5} { + 1 "DELETE FROM t5 WHERE a=2" {2 b 2} {1 a 3 c} + 2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')" {} {1 a 2 d 3 c} + 3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3" {} {1 a 2 c} + 4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')" {} {1 a 3 c 4 b} + 5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'" {} {1 a 3 b} + 6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')" {} {1 a 2 c} + 7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {} {1 b} +} { + do_test triggerC-5.3.$n { + execsql " + BEGIN; + $dml ; + SELECT * FROM t5g; + SELECT * FROM t5; + ROLLBACK; + " + } [concat $t5g $t5] +} +do_test triggerC-5.3.8 { + execsql { PRAGMA recursive_triggers = on } +} {} + +#------------------------------------------------------------------------- +# This block of tests, triggerC-6.*, tests that "PRAGMA recursive_triggers" +# statements return the current value of the recursive triggers flag. +# +do_test triggerC-6.1 { + execsql { PRAGMA recursive_triggers } +} {1} +do_test triggerC-6.2 { + execsql { + PRAGMA recursive_triggers = off; + PRAGMA recursive_triggers; + } +} {0} +do_test triggerC-6.3 { + execsql { + PRAGMA recursive_triggers = on; + PRAGMA recursive_triggers; + } +} {1} + +#------------------------------------------------------------------------- +# Test some of the "undefined behaviour" associated with triggers. The +# undefined behaviour occurs when a row being updated or deleted is +# manipulated by a BEFORE trigger. +# +do_test triggerC-7.1 { + execsql { + CREATE TABLE t8(x); + CREATE TABLE t7(a, b); + INSERT INTO t7 VALUES(1, 2); + INSERT INTO t7 VALUES(3, 4); + INSERT INTO t7 VALUES(5, 6); + CREATE TRIGGER t7t BEFORE UPDATE ON t7 BEGIN + DELETE FROM t7 WHERE a = 1; + END; + CREATE TRIGGER t7ta AFTER UPDATE ON t7 BEGIN + INSERT INTO t8 VALUES('after fired ' || old.rowid || '->' || new.rowid); + END; + } +} {} +do_test triggerC-7.2 { + execsql { + BEGIN; + UPDATE t7 SET b=7 WHERE a = 5; + SELECT * FROM t7; + SELECT * FROM t8; + ROLLBACK; + } +} {3 4 5 7 {after fired 3->3}} +do_test triggerC-7.3 { + execsql { + BEGIN; + UPDATE t7 SET b=7 WHERE a = 1; + SELECT * FROM t7; + SELECT * FROM t8; + ROLLBACK; + } +} {3 4 5 6} + +do_test triggerC-7.4 { + execsql { + DROP TRIGGER t7t; + CREATE TRIGGER t7t BEFORE UPDATE ON t7 WHEN (old.rowid!=1 OR new.rowid!=8) + BEGIN + UPDATE t7 set rowid = 8 WHERE rowid=1; + END; + } +} {} +do_test triggerC-7.5 { + execsql { + BEGIN; + UPDATE t7 SET b=7 WHERE a = 5; + SELECT rowid, * FROM t7; + SELECT * FROM t8; + ROLLBACK; + } +} {2 3 4 3 5 7 8 1 2 {after fired 1->8} {after fired 3->3}} +do_test triggerC-7.6 { + execsql { + BEGIN; + UPDATE t7 SET b=7 WHERE a = 1; + SELECT rowid, * FROM t7; + SELECT * FROM t8; + ROLLBACK; + } +} {2 3 4 3 5 6 8 1 2 {after fired 1->8}} + +do_test triggerC-7.7 { + execsql { + DROP TRIGGER t7t; + DROP TRIGGER t7ta; + CREATE TRIGGER t7t BEFORE DELETE ON t7 BEGIN + UPDATE t7 set rowid = 8 WHERE rowid=1; + END; + CREATE TRIGGER t7ta AFTER DELETE ON t7 BEGIN + INSERT INTO t8 VALUES('after fired ' || old.rowid); + END; + } +} {} +do_test triggerC-7.8 { + execsql { + BEGIN; + DELETE FROM t7 WHERE a = 3; + SELECT rowid, * FROM t7; + SELECT * FROM t8; + ROLLBACK; + } +} {3 5 6 8 1 2 {after fired 2}} +do_test triggerC-7.9 { + execsql { + BEGIN; + DELETE FROM t7 WHERE a = 1; + SELECT rowid, * FROM t7; + SELECT * FROM t8; + ROLLBACK; + } +} {2 3 4 3 5 6 8 1 2} + +# Ticket [e25d9ea771febc9c311928c1c01c3163dcb26643] +# +do_test triggerC-9.1 { + execsql { + CREATE TABLE t9(a,b); + CREATE INDEX t9b ON t9(b); + INSERT INTO t9 VALUES(1,0); + INSERT INTO t9 VALUES(2,1); + INSERT INTO t9 VALUES(3,2); + INSERT INTO t9 SELECT a+3, a+2 FROM t9; + INSERT INTO t9 SELECT a+6, a+5 FROM t9; + SELECT a FROM t9 ORDER BY a; + } +} {1 2 3 4 5 6 7 8 9 10 11 12} +do_test triggerC-9.2 { + execsql { + CREATE TRIGGER t9r1 AFTER DELETE ON t9 BEGIN + DELETE FROM t9 WHERE b=old.a; + END; + DELETE FROM t9 WHERE b=4; + SELECT a FROM t9 ORDER BY a; + } +} {1 2 3 4} + + + + +finish_test Index: test/types.test ================================================================== --- test/types.test +++ test/types.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. Specfically # it tests that the different storage classes (integer, real, text etc.) # all work correctly. # -# $Id: types.test,v 1.19 2006/06/27 12:51:13 drh Exp $ +# $Id: types.test,v 1.20 2009/06/29 06:00:37 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Tests in this file are organized roughly as follows: @@ -134,10 +134,11 @@ # Open the table with root-page $rootpage at the btree # level. Return a list that is the length of each record # in the table, in the tables default scanning order. proc record_sizes {rootpage} { set bt [btree_open test.db 10 0] + btree_begin_transaction $bt set c [btree_cursor $bt $rootpage 0] btree_first $c while 1 { lappend res [btree_payload_size $c] if {[btree_next $c]} break Index: test/vtab1.test ================================================================== --- test/vtab1.test +++ test/vtab1.test @@ -1152,14 +1152,16 @@ } "1 {echo-vtab-error: the $method method has failed}" unset echo_module_fail($method,t2) incr tn } -do_test vtab1-16.$tn { - set echo_module_fail(xRename,t2) "the xRename method has failed" - catchsql { ALTER TABLE echo_t2 RENAME TO another_name } -} "1 {echo-vtab-error: the xRename method has failed}" -unset echo_module_fail(xRename,t2) -incr tn +ifcapable altertable { + do_test vtab1-16.$tn { + set echo_module_fail(xRename,t2) "the xRename method has failed" + catchsql { ALTER TABLE echo_t2 RENAME TO another_name } + } "1 {echo-vtab-error: the xRename method has failed}" + unset echo_module_fail(xRename,t2) + incr tn +} unset -nocomplain echo_module_begin_fail finish_test Index: test/vtab5.test ================================================================== --- test/vtab5.test +++ test/vtab5.test @@ -132,15 +132,17 @@ } } {1 {virtual tables may not be indexed}} # Test that it is impossible to add a column to a virtual table. # -do_test vtab5.4.2 { - catchsql { - ALTER TABLE echo_strings ADD COLUMN col2; - } -} {1 {virtual tables may not be altered}} +ifcapable altertable { + do_test vtab5.4.2 { + catchsql { + ALTER TABLE echo_strings ADD COLUMN col2; + } + } {1 {virtual tables may not be altered}} +} # Test that it is impossible to rename a virtual table. # UPDATE: It is now possible. # # do_test vtab5.4.3 { Index: test/vtab6.test ================================================================== --- test/vtab6.test +++ test/vtab6.test @@ -12,11 +12,11 @@ # # This file implements tests for joins, including outer joins involving # virtual tables. The test cases in this file are copied from the file # join.test, and some of the comments still reflect that. # -# $Id: vtab6.test,v 1.4 2008/07/12 14:52:21 drh Exp $ +# $Id: vtab6.test,v 1.5 2009/07/01 16:12:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab { @@ -263,14 +263,12 @@ catchsql { SELECT * FROM t1 JOIN t2 USING(a); } } {1 {cannot join using column a - column not present in both tables}} do_test vtab6-3.5 { - catchsql { - SELECT * FROM t1 USING(a); - } -} {0 {1 2 3 2 3 4 3 4 5}} + catchsql { SELECT * FROM t1 USING(a) } +} {1 {a JOIN clause is required before USING}} do_test vtab6-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}} @@ -492,10 +490,11 @@ do_test vtab6-11.2.0 { execsql { CREATE INDEX ab_i ON ab_r(b); + CREATE INDEX bc_i ON bc_r(b); } } {} unset ::echo_module_cost @@ -560,15 +559,15 @@ do_test vtab6-11.4.1 { catchsql { SELECT a, b, c FROM ab NATURAL JOIN bc; } -} {1 {table ab: xBestIndex returned an invalid plan}} +} {1 {table bc: xBestIndex returned an invalid plan}} do_test vtab6-11.4.2 { catchsql { SELECT a, b, c FROM bc NATURAL JOIN ab; } } {1 {table ab: xBestIndex returned an invalid plan}} unset ::echo_module_ignore_usable finish_test Index: test/vtab_alter.test ================================================================== --- test/vtab_alter.test +++ test/vtab_alter.test @@ -15,11 +15,11 @@ # $Id: vtab_alter.test,v 1.3 2007/12/13 21:54:11 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !vtab { +ifcapable !vtab||!altertable { finish_test return } # Register the echo module. Index: test/vtab_shared.test ================================================================== --- test/vtab_shared.test +++ test/vtab_shared.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file tests interactions between the virtual table and # shared-schema functionality. # -# $Id: vtab_shared.test,v 1.2 2008/03/19 13:03:34 drh Exp $ +# $Id: vtab_shared.test,v 1.3 2009/07/24 17:58:53 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab||!shared_cache { @@ -21,41 +21,211 @@ return } db close sqlite3_enable_shared_cache 1 -sqlite3 db test.db - -do_test vtab_shared-1.0 { - register_echo_module [sqlite3_connection_pointer db] - catchsql { - CREATE TABLE t0(a, b, c); - CREATE VIRTUAL TABLE t1 USING echo(t0); - } -} {1 {Cannot use virtual tables in shared-cache mode}} - -db close -sqlite3_enable_shared_cache 0 -sqlite3 db test.db +sqlite3 db test.db +sqlite3 db2 test.db do_test vtab_shared-1.1 { register_echo_module [sqlite3_connection_pointer db] - catchsql { + execsql { + CREATE TABLE t0(a, b, c); + INSERT INTO t0 VALUES(1, 2, 3); CREATE VIRTUAL TABLE t1 USING echo(t0); } -} {0 {}} - -db close -sqlite3_enable_shared_cache 1 -sqlite3 db test.db +} {} do_test vtab_shared-1.2 { - register_echo_module [sqlite3_connection_pointer db] - catchsql { + execsql { SELECT * FROM t1 } db +} {1 2 3} + +# Fails because the 'echo' module has not been registered with connection db2 +do_test vtab_shared-1.3 { + catchsql { SELECT * FROM t1 } db2 +} {1 {no such module: echo}} + +do_test vtab_shared-1.4 { + execsql { SELECT * FROM t0 } db2 +} {1 2 3} + +do_test vtab_shared-1.5 { + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t1 } db +} {1 2 3} + +# Works after the module is registered with db2 +do_test vtab_shared-1.6 { + execsql { SELECT * FROM t1 } db2 +} {1 2 3} + +# Set a write-lock on table t0 using connection [db]. Then try to read from +# virtual table t1 using [db2]. That this returns an SQLITE_LOCKED error +# shows that the correct sqlite3_vtab is being used. +# +do_test vtab_shared-1.8.1 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(4, 5, 6); SELECT * FROM t1; } -} [list 1 \ - {malformed database schema (t1) - Cannot use virtual tables in shared-cache mode}] +} {1 2 3 4 5 6} +do_test vtab_shared-1.8.2 { + catchsql { SELECT * FROM t1 } db2 +} {1 {database table is locked}} +do_test vtab_shared-1.8.3 { + catchsql { SELECT * FROM t0 } db2 +} {1 {database table is locked: t0}} +do_test vtab_shared-1.8.4 { + execsql { SELECT * FROM t0 } db +} {1 2 3 4 5 6} +do_test vtab_shared-1.8.5 { + execsql { COMMIT } db + execsql { SELECT * FROM t1 } db2 +} {1 2 3 4 5 6} + +# While a SELECT is active on virtual table t1 via connection [db], close +# [db2]. This causes the schema to be reset internally. Verify that this +# does not cause a problem. +# +foreach {iTest dbSelect dbClose} { + 1 db db2 + 2 db db2 + 3 db2 db +} { + do_test vtab_shared-1.9.$iTest { + set res [list] + $dbSelect eval { SELECT * FROM t1 } { + if {$a == 1} {$dbClose close} + lappend res $a $b $c + } + sqlite3 $dbClose test.db + register_echo_module [sqlite3_connection_pointer $dbClose] + set res + } {1 2 3 4 5 6} +} + +# Ensure that it is not possible for one connection to DROP a virtual +# table while a second connection is reading from the database. +# +do_test vtab_shared-1.10 { + db eval { SELECT * FROM t1 } { + set error [catchsql { DROP TABLE t1 } db2] + break + } + set error +} {1 {database table is locked: sqlite_master}} + +do_test vtab_shared-1.11 { +breakpoint + execsql { + CREATE VIRTUAL TABLE t2 USING echo(t0); + CREATE VIRTUAL TABLE t3 USING echo(t0); + } + execsql { SELECT * FROM t3 } db2 +} {1 2 3 4 5 6} + +do_test vtab_shared-1.12.1 { + db close + execsql { + SELECT * FROM t1 UNION ALL + SELECT * FROM t2 UNION ALL + SELECT * FROM t3 + } db2 +} {1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6} +do_test vtab_shared-1.12.2 { + sqlite3 db test.db + register_echo_module [sqlite3_connection_pointer db] + execsql { + SELECT * FROM t1 UNION ALL + SELECT * FROM t2 UNION ALL + SELECT * FROM t3 + } db +} {1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6} + +# Try a rename or two. +# +ifcapable altertable { + do_test vtab_shared-1.13.1 { + execsql { ALTER TABLE t1 RENAME TO t4 } + execsql { SELECT * FROM t4 } db + } {1 2 3 4 5 6} + do_test vtab_shared-1.13.2 { + execsql { SELECT * FROM t4 } db2 + } {1 2 3 4 5 6} + do_test vtab_shared-1.13.3 { + execsql { ALTER TABLE t2 RENAME TO t5 } + execsql { SELECT * FROM t4 } db2 + } {1 2 3 4 5 6} +} + +# Try an UPDATE/INSERT/DELETE on a shared vtab as the first statement after a +# schema is loaded. +do_test vtab_shared_1.14.1 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t3 } +} {1 2 3 4 5 6} +do_test vtab_shared_1.14.2 { + execsql { + UPDATE t3 SET c = 'six' WHERE c = 6; + SELECT * FROM t3; + } db2 +} {1 2 3 4 5 six} +do_test vtab_shared_1.14.3 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t3 } +} {1 2 3 4 5 six} +do_test vtab_shared_1.14.4 { + execsql { + DELETE FROM t3 WHERE c = 'six'; + SELECT * FROM t3; + } db2 +} {1 2 3} +do_test vtab_shared_1.14.5 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { SELECT * FROM t3 } +} {1 2 3} +do_test vtab_shared_1.14.6 { + execsql { + INSERT INTO t3 VALUES(4, 5, 6); + SELECT * FROM t3; + } db2 +} {1 2 3 4 5 6} + +do_test vtab_shared_1.15.1 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { + UPDATE t3 SET c = 'six' WHERE c = 6; + SELECT * FROM t3; + } db2 +} {1 2 3 4 5 six} +do_test vtab_shared_1.15.2 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { + DELETE FROM t3 WHERE c = 'six'; + SELECT * FROM t3; + } db2 +} {1 2 3} +do_test vtab_shared_1.15.3 { + db2 close + sqlite3 db2 test.db + register_echo_module [sqlite3_connection_pointer db2] + execsql { + INSERT INTO t3 VALUES(4, 5, 6); + SELECT * FROM t3; + } +} {1 2 3 4 5 6} db close +db2 close sqlite3_enable_shared_cache 0 finish_test Index: test/where7.test ================================================================== --- test/where7.test +++ test/where7.test @@ -88,14 +88,16 @@ count_steps { SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4) } } {2 4 5 scan 0 sort 0} do_test where7-1.10 { +breakpoint count_steps { SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4 OR b>10) } } {2 4 5 scan 0 sort 0} +breakpoint do_test where7-1.11 { count_steps { SELECT a FROM t1 WHERE (d=5 AND b=3) OR c==100 ORDER BY a; } } {2 5 scan 0 sort 1} @@ -5410,11 +5412,11 @@ OR 1000000=45.0 AND d<46.0 AND d NOT NULL) OR (f GLOB '?ghij*' AND f GLOB 'fghi*') OR ((a BETWEEN 53 AND 55) AND a!=54) } -} {5 7 18 20 23 25 31 33 37 39 45 53 54 55 56 57 58 59 72 74 83 85 95 scan 99 sort 0} +} {5 7 18 20 23 25 31 33 37 39 45 53 54 55 56 57 58 59 72 74 83 85 95 scan 0 sort 0} do_test where7-2.297.2 { count_steps_sort { SELECT a FROM t3 WHERE a=95 OR ((a BETWEEN 72 AND 74) AND a!=73) @@ -8950,11 +8952,11 @@ OR b=300 OR ((a BETWEEN 24 AND 26) AND a!=25) OR (d>=21.0 AND d<22.0 AND d NOT NULL) OR ((a BETWEEN 93 AND 95) AND a!=94) } -} {1 11 13 21 22 24 26 27 32 34 39 41 53 61 74 76 79 93 95 scan 99 sort 0} +} {1 11 13 21 22 24 26 27 32 34 39 41 53 61 74 76 79 93 95 scan 0 sort 0} do_test where7-2.385.2 { count_steps_sort { SELECT a FROM t3 WHERE (d>=61.0 AND d<62.0 AND d NOT NULL) OR ((a BETWEEN 11 AND 13) AND a!=12) Index: test/where8.test ================================================================== --- test/where8.test +++ test/where8.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The focus # is testing of where.c. More specifically, the focus is the optimization # of WHERE clauses that feature the OR operator. # -# $Id: where8.test,v 1.8 2009/06/07 23:45:11 drh Exp $ +# $Id: where8.test,v 1.9 2009/07/31 06:14:52 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Test organization: @@ -285,11 +285,11 @@ execsql_status { SELECT c FROM t1, t2 WHERE a BETWEEN 1 AND 2 OR a = ( SELECT sum(e IS NULL) FROM t2 AS inner WHERE t2.d>inner.d ) } -} {I I I I I I I I I I II II II II II II II II II II III III III III III 99 0} +} {I II I II I II I II I II I II III I II III I II III I II III I II III 9 0} #----------------------------------------------------------------------- # The following tests - where8-4.* - verify that adding or removing # indexes does not change the results returned by various queries. # @@ -651,11 +651,55 @@ do_test where8-4.$A.$B.2 { lsort $R } [lsort $results($B)] } incr A } - catch {unset results} catch {unset A} catch {unset B} + +# At one point the following tests provoked an invalid write error (writing +# to memory that had already been freed). It was not possible to demonstrate +# that this bug could cause a query to return bad data. +# +do_test where8-5.1 { + db close + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 0 0 + execsql { + CREATE TABLE tA( + a, b, c, d, e, f, g, h, + i, j, k, l, m, n, o, p + ); + } + execsql { + SELECT * FROM tA WHERE + a=1 AND b=2 AND c=3 AND d=4 AND e=5 AND f=6 AND g=7 AND h=8 AND + i=1 AND j=2 AND k=3 AND l=4 AND m=5 AND n=6 AND o=7 AND + (p = 1 OR p = 2 OR p = 3) + } +} {} +do_test where8-5.2 { + execsql { + SELECT * FROM tA WHERE + a=1 AND b=2 AND c=3 AND d=4 AND e=5 AND f=6 AND g=7 AND h=8 AND + i=1 AND j=2 AND k=3 AND l=4 AND m=5 AND + (p = 1 OR p = 2 OR p = 3) AND n=6 AND o=7 + } +} {} +do_test where8-5.3 { + execsql { + INSERT INTO tA VALUES(1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8); + CREATE UNIQUE INDEX tAI ON tA(p); + CREATE TABLE tB(x); + INSERT INTO tB VALUES('x'); + } + execsql { + SELECT a, x FROM tA LEFT JOIN tB ON ( + a=1 AND b=2 AND c=3 AND d=4 AND e=5 AND f=6 AND g=7 AND h=8 AND + i=1 AND j=2 AND k=3 AND l=4 AND m=5 AND n=6 AND o=7 AND + (p = 1 OR p = 2 OR p = 3) + ) + } +} {1 {}} finish_test ADDED test/whereB.test Index: test/whereB.test ================================================================== --- /dev/null +++ test/whereB.test @@ -0,0 +1,543 @@ +# 2009 August 13 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing WHERE clause conditions with +# subtle affinity issues. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# For this set of tests: +# +# * t1.y holds an integer value with affinity NONE +# * t2.b holds a text value with affinity TEXT +# +# These values are not equal and because neither affinity is NUMERIC +# no type conversion occurs. +# +do_test whereB-1.1 { + db eval { + CREATE TABLE t1(x,y); -- affinity of t1.y is NONE + INSERT INTO t1 VALUES(1,99); + + CREATE TABLE t2(a, b TEXT); -- affinity of t2.b is TEXT + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,99); + + SELECT x, a, y=b FROM t1, t2 ORDER BY +x, +a; + } +} {1 2 0} +do_test whereB-1.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {} +do_test whereB-1.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {} +do_test whereB-1.4 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-1.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {} +do_test whereB-1.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {} +do_test whereB-1.102 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + +# For this set of tests: +# +# * t1.y holds a text value with affinity TEXT +# * t2.b holds an integer value with affinity NONE +# +# These values are not equal and because neither affinity is NUMERIC +# no type conversion occurs. +# +do_test whereB-2.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y TEXT); -- affinity of t1.y is TEXT + INSERT INTO t1 VALUES(1,99); + + CREATE TABLE t2(a, b BLOB); -- affinity of t2.b is NONE + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,99); + + SELECT x, a, y=b FROM t1, t2 ORDER BY +x, +a; + } +} {1 2 0} +do_test whereB-2.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {} +do_test whereB-2.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {} +do_test whereB-2.4 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-2.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {} +do_test whereB-2.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {} +do_test whereB-2.102 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + +# For this set of tests: +# +# * t1.y holds a text value with affinity NONE +# * t2.b holds an integer value with affinity NONE +# +# These values are not equal and because neither affinity is NUMERIC +# no type conversion occurs. +# +do_test whereB-3.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y BLOB); -- affinity of t1.y is NONE + INSERT INTO t1 VALUES(1,99); + + CREATE TABLE t2(a, b BLOB); -- affinity of t2.b is NONE + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,'99'); + + SELECT x, a, y=b FROM t1, t2; + } +} {1 2 0} +do_test whereB-3.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {} +do_test whereB-3.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {} +do_test whereB-3.4 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-3.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {} +do_test whereB-3.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {} +do_test whereB-3.102 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + + +# For this set of tests: +# +# * t1.y holds a text value with affinity NONE +# * t2.b holds an integer value with affinity NUMERIC +# +# Because t2.b has a numeric affinity, type conversion should occur +# and the two fields should be equal. +# +do_test whereB-4.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y BLOB); -- affinity of t1.y is NONE + INSERT INTO t1 VALUES(1,'99'); + + CREATE TABLE t2(a, b NUMERIC); -- affinity of t2.b is NUMERIC + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,99); + + SELECT x, a, y=b FROM t1, t2; + } +} {1 2 1} +do_test whereB-4.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-4.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-4.4 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-4.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-4.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-4.102 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + + + +# For this set of tests: +# +# * t1.y holds a text value with affinity NONE +# * t2.b holds an integer value with affinity INTEGER +# +# Because t2.b has a numeric affinity, type conversion should occur +# and the two fields should be equal. +# +do_test whereB-5.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y BLOB); -- affinity of t1.y is NONE + INSERT INTO t1 VALUES(1,'99'); + + CREATE TABLE t2(a, b INT); -- affinity of t2.b is INTEGER + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,99); + + SELECT x, a, y=b FROM t1, t2; + } +} {1 2 1} +do_test whereB-5.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-5.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-5.4 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-5.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-5.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-5.102 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + + +# For this set of tests: +# +# * t1.y holds a text value with affinity NONE +# * t2.b holds an integer value with affinity REAL +# +# Because t2.b has a numeric affinity, type conversion should occur +# and the two fields should be equal. +# +do_test whereB-6.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y BLOB); -- affinity of t1.y is NONE + INSERT INTO t1 VALUES(1,'99'); + + CREATE TABLE t2(a, b REAL); -- affinity of t2.b is REAL + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,99.0); + + SELECT x, a, y=b FROM t1, t2; + } +} {1 2 1} +do_test whereB-6.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-6.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-6.4 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-6.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-6.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-6.102 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + + +# For this set of tests: +# +# * t1.y holds an integer value with affinity NUMERIC +# * t2.b holds a text value with affinity NONE +# +# Because t1.y has a numeric affinity, type conversion should occur +# and the two fields should be equal. +# +do_test whereB-7.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y NUMERIC); -- affinity of t1.y is NUMERIC + INSERT INTO t1 VALUES(1,99); + + CREATE TABLE t2(a, b BLOB); -- affinity of t2.b is NONE + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,'99'); + + SELECT x, a, y=b FROM t1, t2; + } +} {1 2 1} +do_test whereB-7.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-7.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-7.4 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-7.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-7.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-7.102 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + +# For this set of tests: +# +# * t1.y holds an integer value with affinity INTEGER +# * t2.b holds a text value with affinity NONE +# +# Because t1.y has a numeric affinity, type conversion should occur +# and the two fields should be equal. +# +do_test whereB-8.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y INT); -- affinity of t1.y is INTEGER + INSERT INTO t1 VALUES(1,99); + + CREATE TABLE t2(a, b BLOB); -- affinity of t2.b is NONE + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,'99'); + + SELECT x, a, y=b FROM t1, t2; + } +} {1 2 1} +do_test whereB-8.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-8.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-8.4 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-8.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-8.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-8.102 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + +# For this set of tests: +# +# * t1.y holds an integer value with affinity REAL +# * t2.b holds a text value with affinity NONE +# +# Because t1.y has a numeric affinity, type conversion should occur +# and the two fields should be equal. +# +do_test whereB-9.1 { + db eval { + DROP TABLE t1; + DROP TABLE t2; + + CREATE TABLE t1(x, y REAL); -- affinity of t1.y is REAL + INSERT INTO t1 VALUES(1,99.0); + + CREATE TABLE t2(a, b BLOB); -- affinity of t2.b is NONE + CREATE INDEX t2b ON t2(b); + INSERT INTO t2 VALUES(2,'99'); + + SELECT x, a, y=b FROM t1, t2; + } +} {1 2 1} +do_test whereB-9.2 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-9.3 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-9.4 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} +do_test whereB-9.100 { + db eval { + DROP INDEX t2b; + SELECT x, a, y=b FROM t1, t2 WHERE y=b; + } +} {1 2 1} +do_test whereB-9.101 { + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE b=y; + } +} {1 2 1} +do_test whereB-9.102 { + # In this case the unary "+" operator removes the column affinity so + # the columns compare false + db eval { + SELECT x, a, y=b FROM t1, t2 WHERE +y=+b; + } +} {} + + + + +finish_test Index: test/zeroblob.test ================================================================== --- test/zeroblob.test +++ test/zeroblob.test @@ -11,11 +11,11 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing of the zero-filled blob functionality # including the sqlite3_bind_zeroblob(), sqlite3_result_zeroblob(), # and the built-in zeroblob() SQL function. # -# $Id: zeroblob.test,v 1.13 2008/06/13 18:24:28 drh Exp $ +# $Id: zeroblob.test,v 1.14 2009/07/14 02:33:02 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !incrblob { @@ -224,7 +224,36 @@ llength [execsql { SELECT 'hello' AS a, zeroblob(10) as b from t1 ORDER BY a, b; }] } {8} + +# Ticket #3965 +# zeroblobs on either size of an IN operator +# +do_test zeroblob-9.1 { + db eval {SELECT x'0000' IN (x'000000')} +} {0} +do_test zeroblob-9.2 { + db eval {SELECT x'0000' IN (x'0000')} +} {1} +do_test zeroblob-9.3 { + db eval {SELECT zeroblob(2) IN (x'000000')} +} {0} +do_test zeroblob-9.4 { + db eval {SELECT zeroblob(2) IN (x'0000')} +} {1} +do_test zeroblob-9.5 { + db eval {SELECT x'0000' IN (zeroblob(3))} +} {0} +do_test zeroblob-9.6 { + db eval {SELECT x'0000' IN (zeroblob(2))} +} {1} +do_test zeroblob-9.7 { + db eval {SELECT zeroblob(2) IN (zeroblob(3))} +} {0} +do_test zeroblob-9.8 { + db eval {SELECT zeroblob(2) IN (zeroblob(2))} +} {1} + finish_test Index: tool/genfkey.test ================================================================== --- tool/genfkey.test +++ tool/genfkey.test @@ -289,6 +289,66 @@ catchsql { INSERT INTO "t.3" VALUES(1); INSERT INTO t13 VALUES(1); } } {0 {}} + +# Test also column names that require quoting. +do_test genfkey-6.1 { + execsql { + DROP TABLE "t.3"; + DROP TABLE t13; + CREATE TABLE p( + "a.1 first", "b.2 second", + UNIQUE("a.1 first", "b.2 second") + ); + CREATE TABLE c( + "c.1 I", "d.2 II", + FOREIGN KEY("c.1 I", "d.2 II") + REFERENCES p("a.1 first", "b.2 second") + ON UPDATE CASCADE ON DELETE CASCADE + ); + } +} {} +do_test genfkey-6.2 { + set rc [catch {exec ./sqlite3 test.db .genfkey} msg] +} {0} +do_test genfkey-6.3 { + execsql $msg + execsql { + INSERT INTO p VALUES('A', 'B'); + INSERT INTO p VALUES('C', 'D'); + INSERT INTO c VALUES('A', 'B'); + INSERT INTO c VALUES('C', 'D'); + UPDATE p SET "a.1 first" = 'X' WHERE rowid = 1; + DELETE FROM p WHERE rowid = 2; + } + execsql { SELECT * FROM c } +} {X B} + +do_test genfkey-6.4 { + execsql { + DROP TABLE p; + DROP TABLE c; + CREATE TABLE parent("a.1", PRIMARY KEY("a.1")); + CREATE TABLE child("b.2", FOREIGN KEY("b.2") REFERENCES parent("a.1")); + } + set rc [catch {exec ./sqlite3 test.db .genfkey} msg] +} {0} +do_test genfkey-6.5 { + execsql $msg + execsql { + INSERT INTO parent VALUES(1); + INSERT INTO child VALUES(1); + } + catchsql { UPDATE parent SET "a.1"=0 } +} {1 {constraint failed}} +do_test genfkey-6.6 { + catchsql { UPDATE child SET "b.2"=7 } +} {1 {constraint failed}} +do_test genfkey-6.7 { + execsql { + SELECT * FROM parent; + SELECT * FROM child; + } +} {1 1} Index: tool/lemon.c ================================================================== --- tool/lemon.c +++ tool/lemon.c @@ -367,10 +367,13 @@ rc = (int)ap1->type - (int)ap2->type; } if( rc==0 && ap1->type==REDUCE ){ rc = ap1->x.rp->index - ap2->x.rp->index; } + if( rc==0 ){ + rc = ap2 - ap1; + } return rc; } /* Sort parser actions */ static struct action *Action_sort( @@ -515,25 +518,13 @@ ** offset is found. In the worst case, we fall out of the loop when ** i reaches p->nAction, which means we append the new transaction set. ** ** i is the index in p->aAction[] where p->mnLookahead is inserted. */ - for(i=0; inAction+p->mnLookahead; i++){ - if( p->aAction[i].lookahead<0 ){ - for(j=0; jnLookahead; j++){ - k = p->aLookahead[j].lookahead - p->mnLookahead + i; - if( k<0 ) break; - if( p->aAction[k].lookahead>=0 ) break; - } - if( jnLookahead ) continue; - for(j=0; jnAction; j++){ - if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; - } - if( j==p->nAction ){ - break; /* Fits in empty slots */ - } - }else if( p->aAction[i].lookahead==p->mnLookahead ){ + for(i=p->nAction-1; i>=0; i--){ + /* First look for an existing action table entry that can be reused */ + if( p->aAction[i].lookahead==p->mnLookahead ){ if( p->aAction[i].action!=p->mnAction ) continue; for(j=0; jnLookahead; j++){ k = p->aLookahead[j].lookahead - p->mnLookahead + i; if( k<0 || k>=p->nAction ) break; if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; @@ -547,10 +538,29 @@ } if( n==p->nLookahead ){ break; /* Same as a prior transaction set */ } } + } + if( i<0 ){ + /* If no reusable entry is found, look for an empty slot */ + for(i=0; inAction; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( jnLookahead ) continue; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + } + } } /* Insert transaction set at index i. */ for(j=0; jnLookahead; j++){ k = p->aLookahead[j].lookahead - p->mnLookahead + i; p->aAction[k] = p->aLookahead[j]; @@ -1576,20 +1586,20 @@ if( a==0 ){ head = b; }else if( b==0 ){ head = a; }else{ - if( (*cmp)(a,b)<0 ){ + if( (*cmp)(a,b)<=0 ){ ptr = a; a = NEXT(a); }else{ ptr = b; b = NEXT(b); } head = ptr; while( a && b ){ - if( (*cmp)(a,b)<0 ){ + if( (*cmp)(a,b)<=0 ){ NEXT(ptr) = a; ptr = a; a = NEXT(a); }else{ NEXT(ptr) = b; @@ -1637,11 +1647,11 @@ set[i] = 0; } set[i] = ep; } ep = 0; - for(i=0; inAction - p1->nAction; + int c; + c = p2->nAction - p1->nAction; + if( c==0 ){ + c = p2->iOrder - p1->iOrder; + } + assert( c!=0 || p1==p2 ); + return c; } /* ** Write text on "out" that describes the rule "rp". */ @@ -3556,11 +3573,11 @@ int lineno; struct state *stp; struct action *ap; struct rule *rp; struct acttab *pActtab; - int i, j, n; + int i, j, k, n; char *name; int mnTknOfst, mxTknOfst; int mnNtOfst, mxNtOfst; struct axset *ax; @@ -3682,10 +3699,11 @@ /* Compute the action table. In order to try to keep the size of the ** action table to a minimum, the heuristic of placing the largest action ** sets first is used. */ + for(i=0; instate*2; i++) ax[i].iOrder = i; qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); pActtab = acttab_alloc(); for(i=0; instate*2 && ax[i].nAction>0; i++){ stp = ax[i].stp; if( ax[i].isTkn ){ @@ -3714,12 +3732,13 @@ } } free(ax); /* Output the yy_action table */ - fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; n = acttab_size(pActtab); + fprintf(out,"#define YY_ACTTAB_COUNT (%d)\n", n); lineno++; + fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; for(i=j=0; instate + lemp->nrule + 2; if( j==0 ) fprintf(out," /* %5d */ ", i); fprintf(out, " %4d,", action); @@ -3750,11 +3769,13 @@ /* Output the yy_shift_ofst[] table */ fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; n = lemp->nstate; while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; - fprintf(out, "#define YY_SHIFT_MAX %d\n", n-1); lineno++; + fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++; + fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++; + fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++; fprintf(out, "static const %s yy_shift_ofst[] = {\n", minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; for(i=j=0; isorted[i]; @@ -3773,11 +3794,13 @@ /* Output the yy_reduce_ofst[] table */ fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; n = lemp->nstate; while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; - fprintf(out, "#define YY_REDUCE_MAX %d\n", n-1); lineno++; + fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n-1); lineno++; + fprintf(out, "#define YY_REDUCE_MIN (%d)\n", mnNtOfst); lineno++; + fprintf(out, "#define YY_REDUCE_MAX (%d)\n", mxNtOfst); lineno++; fprintf(out, "static const %s yy_reduce_ofst[] = {\n", minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; for(i=j=0; isorted[i]; @@ -4095,11 +4118,15 @@ int n; n = pB->nNtAct - pA->nNtAct; if( n==0 ){ n = pB->nTknAct - pA->nTknAct; + if( n==0 ){ + n = pB->statenum - pA->statenum; + } } + assert( n!=0 ); return n; } /* @@ -4399,10 +4426,11 @@ ** smallest parser tables in SQLite. */ int Symbolcmpp(struct symbol **a, struct symbol **b){ int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + assert( i1!=i2 || strcmp((**a).name,(**b).name)==0 ); return i1-i2; } /* There is one instance of the following structure for each ** associative array of type "x2". Index: tool/lempar.c ================================================================== --- tool/lempar.c +++ tool/lempar.c @@ -123,11 +123,10 @@ ** yy_reduce_ofst[] For each state, the offset into yy_action for ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. */ %% -#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) /* The next table maps tokens into fallback tokens. If a construct ** like the following: ** ** %fallback ID X Y Z. @@ -384,16 +383,17 @@ YYCODETYPE iLookAhead /* The look-ahead token */ ){ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + if( stateno>YY_SHIFT_COUNT + || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ return yy_default[stateno]; } assert( iLookAhead!=YYNOCODE ); i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ if( iLookAhead>0 ){ #ifdef YYFALLBACK YYCODETYPE iFallback; /* Fallback token */ if( iLookAhead=0 && j=0 && +#endif +#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT + j %s\n", yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); } @@ -439,26 +447,26 @@ int stateno, /* Current state number */ YYCODETYPE iLookAhead /* The look-ahead token */ ){ int i; #ifdef YYERRORSYMBOL - if( stateno>YY_REDUCE_MAX ){ + if( stateno>YY_REDUCE_COUNT ){ return yy_default[stateno]; } #else - assert( stateno<=YY_REDUCE_MAX ); + assert( stateno<=YY_REDUCE_COUNT ); #endif i = yy_reduce_ofst[stateno]; assert( i!=YY_REDUCE_USE_DFLT ); assert( iLookAhead!=YYNOCODE ); i += iLookAhead; #ifdef YYERRORSYMBOL - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; } #else - assert( i>=0 && i=0 && ilen - pB->len; if( n==0 ){ n = strcmp(pA->zName, pB->zName); } + assert( n!=0 ); return n; } static int keywordCompare2(const void *a, const void *b){ const Keyword *pA = (Keyword*)a; const Keyword *pB = (Keyword*)b; int n = pB->longestSuffix - pA->longestSuffix; if( n==0 ){ n = strcmp(pA->zName, pB->zName); } + assert( n!=0 ); return n; } static int keywordCompare3(const void *a, const void *b){ const Keyword *pA = (Keyword*)a; const Keyword *pB = (Keyword*)b; int n = pA->offset - pB->offset; + if( n==0 ) n = pB->id - pA->id; + assert( n!=0 ); return n; } /* ** Return a KeywordTable entry with the given id Index: tool/mksqlite3c.tcl ================================================================== --- tool/mksqlite3c.tcl +++ tool/mksqlite3c.tcl @@ -20,13 +20,13 @@ # tclsh mksqlite3c.tcl # # The amalgamated SQLite code will be written into sqlite3.c # -# Begin by reading the "sqlite3.h" header file. Count the number of lines -# in this file and extract the version number. That information will be -# needed in order to generate the header of the amalgamation. +# Begin by reading the "sqlite3.h" header file. Extract the version number +# from in this file. The versioon number is needed to generate the header +# comment of the amalgamation. # if {[lsearch $argv --nostatic]>=0} { set addstatic 0 } else { set addstatic 1 @@ -58,18 +58,16 @@ ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have -** the "sqlite3.h" header file at hand, you will find a copy in the first -** $cnt lines past this header comment.) Additional code files may be -** needed if you want a wrapper to interface SQLite with your choice of -** programming language. The code for the "sqlite3" command-line shell -** is also in a separate file. This file contains only code for the core -** SQLite library. -** -** This amalgamation was generated on $today. +** the "sqlite3.h" header file at hand, you will find a copy embedded within +** the text of this file. Search for "Begin file sqlite3.h" to find the start +** of the embedded sqlite3.h header file.) Additional code files may be needed +** if you want a wrapper to interface SQLite with your choice of programming +** language. The code for the "sqlite3" command-line shell is also in a +** separate file. This file contains only code for the core SQLite library. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1}] if {$addstatic} { puts $out \ @@ -164,10 +162,11 @@ } elseif {[regexp {^#ifdef __cplusplus} $line]} { puts $out "#if 0" } elseif {[regexp {^#line} $line]} { # Skip #line directives. } elseif {$addstatic && ![regexp {^(static|typedef)} $line]} { + regsub {^SQLITE_API } $line {} line if {[regexp $declpattern $line all funcname]} { # Add the SQLITE_PRIVATE or SQLITE_API keyword before functions. # so that linkage can be modified at compile-time. if {[regexp {^sqlite3_} $funcname]} { puts $out "SQLITE_API $line" @@ -266,10 +265,11 @@ auth.c build.c callback.c delete.c func.c + fkey.c insert.c legacy.c loadext.c pragma.c prepare.c ADDED tool/mksqlite3h.tcl Index: tool/mksqlite3h.tcl ================================================================== --- /dev/null +++ tool/mksqlite3h.tcl @@ -0,0 +1,96 @@ +#!/usr/bin/tclsh +# +# This script constructs the "sqlite3.h" header file from the following +# sources: +# +# 1) The src/sqlite.h.in source file. This is the template for sqlite3.h. +# 2) The VERSION file containing the current SQLite version number. +# 3) The manifest file from the fossil SCM. This gives use the date. +# 4) The manifest.uuid file from the fossil SCM. This gives the SHA1 hash. +# +# Run this script by specifying the root directory of the source tree +# on the command-line. +# +# This script performs processing on src/sqlite.h.in. It: +# +# 1) Adds SQLITE_EXTERN in front of the declaration of global variables, +# 2) Adds SQLITE_API in front of the declaration of API functions, +# 3) Replaces the string --VERS-- with the current library version, +# formatted as a string (e.g. "3.6.17"), and +# 4) Replaces the string --VERSION-NUMBER-- with current library version, +# formatted as an integer (e.g. "3006017"). +# 5) Replaces the string --SOURCE-ID-- with the date and time and sha1 +# hash of the fossil-scm manifest for the source tree. +# +# This script outputs to stdout. +# +# Example usage: +# +# tclsh mksqlite3h.tcl ../sqlite >sqlite3.h +# + + +# Get the source tree root directory from the command-line +# +set TOP [lindex $argv 0] + +# Get the SQLite version number (ex: 3.6.18) from the $TOP/VERSION file. +# +set in [open $TOP/VERSION] +set zVersion [string trim [read $in]] +close $in +set nVersion [eval format "%d%03d%03d" [split $zVersion .]] + +# Get the fossil-scm version number from $TOP/manifest.uuid. +# +set in [open $TOP/manifest.uuid] +set zUuid [string trim [read $in]] +close $in + +# Get the fossil-scm check-in date from the "D" card of $TOP/manifest. +# +set in [open $TOP/manifest] +set zDate {} +while {![eof $in]} { + set line [gets $in] + if {[regexp {^D (2.*[0-9])} $line all date]} { + set zDate [string map {T { }} $date] + break + } +} +close $in + +# Set up patterns for recognizing API declarations. +# +set varpattern {^[a-zA-Z][a-zA-Z_0-9 *]+sqlite3_[_a-zA-Z0-9]+(\[|;| =)} +set declpattern {^ *[a-zA-Z][a-zA-Z_0-9 ]+ \**sqlite3_[_a-zA-Z0-9]+\(} + +# Process the src/sqlite.h.in file. +# +set in [open $TOP/src/sqlite.h.in] +while {![eof $in]} { + + set line [gets $in] + + regsub -- --VERS-- $line $zVersion line + regsub -- --VERSION-NUMBER-- $line $nVersion line + regsub -- --SOURCE-ID-- $line "$zDate $zUuid" line + + if {[regexp {define SQLITE_EXTERN extern} $line]} { + puts $line + puts [gets $in] + puts "" + puts "#ifndef SQLITE_API" + puts "# define SQLITE_API" + puts "#endif" + set line "" + } + + if {([regexp $varpattern $line] && ![regexp {^ *typedef} $line]) + || ([regexp $declpattern $line]) + } { + set line "SQLITE_API $line" + } + puts $line +} +close $in ADDED tool/shell1.test Index: tool/shell1.test ================================================================== --- /dev/null +++ tool/shell1.test @@ -0,0 +1,707 @@ +# 2009 Nov 11 +# +# 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. +# +#*********************************************************************** +# +# The focus of this file is testing the CLI shell tool. +# +# $Id: shell1.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ +# + +# Test plan: +# +# shell1-1.*: Basic command line option handling. +# shell1-2.*: Basic "dot" command token parsing. +# shell1-3.*: Basic test that "dot" command can be called. +# + +package require sqlite3 + +set CLI "./sqlite" + +proc do_test {name cmd expected} { + puts -nonewline "$name ..." + set res [uplevel $cmd] + if {$res eq $expected} { + puts Ok + } else { + puts Error + puts " Got: $res" + puts " Expected: $expected" + exit + } +} + +proc execsql {sql} { + uplevel [list db eval $sql] +} + +proc catchsql {sql} { + set rc [catch {uplevel [list db eval $sql]} msg] + list $rc $msg +} + +proc catchcmd {db cmd} { + global CLI + set out [open cmds.txt w] + puts $out $cmd + close $out + set line "exec $CLI $db < cmds.txt" + set rc [catch { eval $line } msg] + list $rc $msg +} + +file delete -force test.db test.db.journal +sqlite3 db test.db + +#---------------------------------------------------------------------------- +# Test cases shell1-1.*: Basic command line option handling. +# + +# invalid option +do_test shell1-1.1.1 { + set res [catchcmd "-bad test.db" ""] + set rc [lindex $res 0] + list $rc \ + [regexp {Error: unknown option: -bad} $res] +} {1 1} +# error on extra options +do_test shell1-1.1.2 { + set res [catchcmd "-bad test.db \"select 3\" \"select 4\"" ""] + set rc [lindex $res 0] + list $rc \ + [regexp {Error: too many options: "select 4"} $res] +} {1 1} +# error on extra options +do_test shell1-1.3.2 { + set res [catchcmd "-bad FOO test.db BAD" ".quit"] + set rc [lindex $res 0] + list $rc \ + [regexp {Error: too many options: "BAD"} $res] +} {1 1} + +# -help +do_test shell1-1.2.1 { + set res [catchcmd "-help test.db" ""] + set rc [lindex $res 0] + list $rc \ + [regexp {Usage} $res] \ + [regexp {\-init} $res] \ + [regexp {\-version} $res] +} {1 1 1 1} + +# -init filename read/process named file +do_test shell1-1.3.1 { + catchcmd "-init FOO test.db" "" +} {0 {}} +do_test shell1-1.3.2 { + set res [catchcmd "-init FOO test.db .quit BAD" ""] + set rc [lindex $res 0] + list $rc \ + [regexp {Error: too many options: "BAD"} $res] +} {1 1} + +# -echo print commands before execution +do_test shell1-1.4.1 { + catchcmd "-echo test.db" "" +} {0 {}} + +# -[no]header turn headers on or off +do_test shell1-1.5.1 { + catchcmd "-header test.db" "" +} {0 {}} +do_test shell1-1.5.2 { + catchcmd "-noheader test.db" "" +} {0 {}} + +# -bail stop after hitting an error +do_test shell1-1.6.1 { + catchcmd "-bail test.db" "" +} {0 {}} + +# -interactive force interactive I/O +do_test shell1-1.7.1 { + set res [catchcmd "-interactive test.db" ".quit"] + set rc [lindex $res 0] + list $rc \ + [regexp {SQLite version} $res] \ + [regexp {Enter SQL statements} $res] +} {0 1 1} + +# -batch force batch I/O +do_test shell1-1.8.1 { + catchcmd "-batch test.db" "" +} {0 {}} + +# -column set output mode to 'column' +do_test shell1-1.9.1 { + catchcmd "-column test.db" "" +} {0 {}} + +# -csv set output mode to 'csv' +do_test shell1-1.10.1 { + catchcmd "-csv test.db" "" +} {0 {}} + +# -html set output mode to HTML +do_test shell1-1.11.1 { + catchcmd "-html test.db" "" +} {0 {}} + +# -line set output mode to 'line' +do_test shell1-1.12.1 { + catchcmd "-line test.db" "" +} {0 {}} + +# -list set output mode to 'list' +do_test shell1-1.13.1 { + catchcmd "-list test.db" "" +} {0 {}} + +# -separator 'x' set output field separator (|) +do_test shell1-1.14.1 { + catchcmd "-separator 'x' test.db" "" +} {0 {}} +do_test shell1-1.14.2 { + catchcmd "-separator x test.db" "" +} {0 {}} +do_test shell1-1.14.3 { + set res [catchcmd "-separator" ""] + set rc [lindex $res 0] + list $rc \ + [regexp {Error: missing argument for option: -separator} $res] +} {1 1} + +# -nullvalue 'text' set text string for NULL values +do_test shell1-1.15.1 { + catchcmd "-nullvalue 'x' test.db" "" +} {0 {}} +do_test shell1-1.15.2 { + catchcmd "-nullvalue x test.db" "" +} {0 {}} +do_test shell1-1.15.3 { + set res [catchcmd "-nullvalue" ""] + set rc [lindex $res 0] + list $rc \ + [regexp {Error: missing argument for option: -nullvalue} $res] +} {1 1} + +# -version show SQLite version +do_test shell1-1.16.1 { + catchcmd "-version test.db" "" +} {0 3.6.20} + +#---------------------------------------------------------------------------- +# Test cases shell1-2.*: Basic "dot" command token parsing. +# + +# check first token handling +do_test shell1-2.1.1 { + catchcmd " test.db" ".foo" +} {1 {Error: unknown command or invalid arguments: "foo". Enter ".help" for help}} +do_test shell1-2.1.2 { + catchcmd " test.db" ".\"foo OFF\"" +} {1 {Error: unknown command or invalid arguments: "foo OFF". Enter ".help" for help}} +do_test shell1-2.1.3 { + catchcmd " test.db" ".\'foo OFF\'" +} {1 {Error: unknown command or invalid arguments: "foo OFF". Enter ".help" for help}} + +# unbalanced quotes +do_test shell1-2.2.1 { + catchcmd " test.db" ".\"foo OFF" +} {1 {Error: unknown command or invalid arguments: "foo OFF". Enter ".help" for help}} +do_test shell1-2.2.2 { + catchcmd " test.db" ".\'foo OFF" +} {1 {Error: unknown command or invalid arguments: "foo OFF". Enter ".help" for help}} +do_test shell1-2.2.3 { + catchcmd " test.db" ".explain \"OFF" +} {0 {}} +do_test shell1-2.2.4 { + catchcmd " test.db" ".explain \'OFF" +} {0 {}} +do_test shell1-2.2.5 { + catchcmd " test.db" ".mode \"insert FOO" +} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +do_test shell1-2.2.6 { + catchcmd " test.db" ".mode \'insert FOO" +} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} + +# check multiple tokens, and quoted tokens +do_test shell1-2.3.1 { + catchcmd " test.db" ".explain 1" +} {0 {}} +do_test shell1-2.3.2 { + catchcmd " test.db" ".explain on" +} {0 {}} +do_test shell1-2.3.3 { + catchcmd " test.db" ".explain \"1 2 3\"" +} {0 {}} +do_test shell1-2.3.4 { + catchcmd " test.db" ".explain \"OFF\"" +} {0 {}} +do_test shell1-2.3.5 { + catchcmd " test.db" ".\'explain\' \'OFF\'" +} {0 {}} +do_test shell1-2.3.6 { + catchcmd " test.db" ".explain \'OFF\'" +} {0 {}} +do_test shell1-2.3.7 { + catchcmd " test.db" ".\'explain\' \'OFF\'" +} {0 {}} + +# check quoted args are unquoted +do_test shell1-2.4.1 { + catchcmd " test.db" ".mode FOO" +} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +do_test shell1-2.4.2 { + catchcmd " test.db" ".mode csv" +} {0 {}} +do_test shell1-2.4.2 { + catchcmd " test.db" ".mode \"csv\"" +} {0 {}} + + +#---------------------------------------------------------------------------- +# Test cases shell1-3.*: Basic test that "dot" command can be called. +# + +# .backup ?DB? FILE Backup DB (default "main") to FILE +do_test shell1-3.1.1 { + catchcmd " test.db" ".backup" +} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}} +do_test shell1-3.1.2 { + # catchcmd " test.db" ".backup FOO" + #TBD!!! this asserts currently +} {} +do_test shell1-3.1.3 { + catchcmd " test.db" ".backup FOO BAR" +} {1 {Error: unknown database FOO}} +do_test shell1-3.1.4 { + # too many arguments + catchcmd " test.db" ".backup FOO BAR BAD" +} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}} + +# .bail ON|OFF Stop after hitting an error. Default OFF +do_test shell1-3.2.1 { + catchcmd " test.db" ".bail" +} {1 {Error: unknown command or invalid arguments: "bail". Enter ".help" for help}} +do_test shell1-3.2.2 { + catchcmd " test.db" ".bail ON" +} {0 {}} +do_test shell1-3.2.3 { + catchcmd " test.db" ".bail OFF" +} {0 {}} +do_test shell1-3.2.4 { + # too many arguments + catchcmd " test.db" ".bail OFF BAD" +} {1 {Error: unknown command or invalid arguments: "bail". Enter ".help" for help}} + +# .databases List names and files of attached databases +do_test shell1-3.3.1 { + set res [catchcmd " test.db" ".databases"] + regexp {0.*main.*test\.db} $res +} {1} +do_test shell1-3.3.2 { + # too many arguments + catchcmd " test.db" ".databases BAD" +} {1 {Error: unknown command or invalid arguments: "databases". Enter ".help" for help}} + +# .dump ?TABLE? ... Dump the database in an SQL text format +# If TABLE specified, only dump tables matching +# LIKE pattern TABLE. +do_test shell1-3.4.1 { + set res [catchcmd " test.db" ".dump"] + list [regexp {BEGIN TRANSACTION;} $res] \ + [regexp {COMMIT;} $res] +} {1 1} +do_test shell1-3.4.2 { + set res [catchcmd " test.db" ".dump FOO"] + list [regexp {BEGIN TRANSACTION;} $res] \ + [regexp {COMMIT;} $res] +} {1 1} +do_test shell1-3.4.3 { + # too many arguments + catchcmd " test.db" ".dump FOO BAD" +} {1 {Error: unknown command or invalid arguments: "dump". Enter ".help" for help}} + +# .echo ON|OFF Turn command echo on or off +do_test shell1-3.5.1 { + catchcmd " test.db" ".echo" +} {1 {Error: unknown command or invalid arguments: "echo". Enter ".help" for help}} +do_test shell1-3.5.2 { + catchcmd " test.db" ".echo ON" +} {0 {}} +do_test shell1-3.5.3 { + catchcmd " test.db" ".echo OFF" +} {0 {}} +do_test shell1-3.5.4 { + # too many arguments + catchcmd " test.db" ".echo OFF BAD" +} {1 {Error: unknown command or invalid arguments: "echo". Enter ".help" for help}} + +# .exit Exit this program +do_test shell1-3.6.1 { + catchcmd " test.db" ".exit" +} {0 {}} +do_test shell1-3.6.2 { + # too many arguments + catchcmd " test.db" ".exit BAD" +} {1 {Error: unknown command or invalid arguments: "exit". Enter ".help" for help}} + +# .explain ON|OFF Turn output mode suitable for EXPLAIN on or off. +do_test shell1-3.7.1 { + catchcmd " test.db" ".explain" + # explain is the exception to the booleans. without an option, it turns it on. +} {0 {}} +do_test shell1-3.7.2 { + catchcmd " test.db" ".explain ON" +} {0 {}} +do_test shell1-3.7.3 { + catchcmd " test.db" ".explain OFF" +} {0 {}} +do_test shell1-3.7.4 { + # too many arguments + catchcmd " test.db" ".explain OFF BAD" +} {1 {Error: unknown command or invalid arguments: "explain". Enter ".help" for help}} + +# .genfkey ?OPTIONS? Options are: +# --no-drop: Do not drop old fkey triggers. +# --ignore-errors: Ignore tables with fkey errors +# --exec: Execute generated SQL immediately +# See file tool/genfkey.README in the source +# distribution for further information. +do_test shell1-3.8.1 { + catchcmd " test.db" ".genfkey" +} {0 {}} +do_test shell1-3.8.2 { + catchcmd " test.db" ".genfkey FOO" +} {1 {unknown option: FOO}} + +# .header(s) ON|OFF Turn display of headers on or off +do_test shell1-3.9.1 { + catchcmd " test.db" ".header" +} {1 {Error: unknown command or invalid arguments: "header". Enter ".help" for help}} +do_test shell1-3.9.2 { + catchcmd " test.db" ".header ON" +} {0 {}} +do_test shell1-3.9.3 { + catchcmd " test.db" ".header OFF" +} {0 {}} +do_test shell1-3.9.4 { + # too many arguments + catchcmd " test.db" ".header OFF BAD" +} {1 {Error: unknown command or invalid arguments: "header". Enter ".help" for help}} + +do_test shell1-3.9.5 { + catchcmd " test.db" ".headers" +} {1 {Error: unknown command or invalid arguments: "headers". Enter ".help" for help}} +do_test shell1-3.9.6 { + catchcmd " test.db" ".headers ON" +} {0 {}} +do_test shell1-3.9.7 { + catchcmd " test.db" ".headers OFF" +} {0 {}} +do_test shell1-3.9.8 { + # too many arguments + catchcmd " test.db" ".headers OFF BAD" +} {1 {Error: unknown command or invalid arguments: "headers". Enter ".help" for help}} + +# .help Show this message +do_test shell1-3.10.1 { + set res [catchcmd " test.db" ".help"] + # look for a few of the possible help commands + list [regexp {.help} $res] \ + [regexp {.quit} $res] \ + [regexp {.show} $res] +} {1 1 1} +do_test shell1-3.10.2 { + # we allow .help to take extra args (it is help after all) + set res [catchcmd " test.db" ".help BAD"] + # look for a few of the possible help commands + list [regexp {.help} $res] \ + [regexp {.quit} $res] \ + [regexp {.show} $res] +} {1 1 1} + +# .import FILE TABLE Import data from FILE into TABLE +do_test shell1-3.11.1 { + catchcmd " test.db" ".import" +} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}} +do_test shell1-3.11.2 { + catchcmd " test.db" ".import FOO" +} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}} +do_test shell1-3.11.2 { + catchcmd " test.db" ".import FOO BAR" +} {1 {Error: no such table: BAR}} +do_test shell1-3.11.3 { + # too many arguments + catchcmd " test.db" ".import FOO BAR BAD" +} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}} + +# .indices ?TABLE? Show names of all indices +# If TABLE specified, only show indices for tables +# matching LIKE pattern TABLE. +do_test shell1-3.12.1 { + catchcmd " test.db" ".indices" +} {0 {}} +do_test shell1-3.12.2 { + catchcmd " test.db" ".indices FOO" +} {0 {}} +do_test shell1-3.12.3 { + # too many arguments + catchcmd " test.db" ".indices FOO BAD" +} {1 {Error: unknown command or invalid arguments: "indices". Enter ".help" for help}} + +# .mode MODE ?TABLE? Set output mode where MODE is one of: +# csv Comma-separated values +# column Left-aligned columns. (See .width) +# html HTML code +# insert SQL insert statements for TABLE +# line One value per line +# list Values delimited by .separator string +# tabs Tab-separated values +# tcl TCL list elements +do_test shell1-3.13.1 { + catchcmd " test.db" ".mode" +} {1 {Error: unknown command or invalid arguments: "mode". Enter ".help" for help}} +do_test shell1-3.13.2 { + catchcmd " test.db" ".mode FOO" +} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +do_test shell1-3.13.3 { + catchcmd " test.db" ".mode csv" +} {0 {}} +do_test shell1-3.13.4 { + catchcmd " test.db" ".mode column" +} {0 {}} +do_test shell1-3.13.5 { + catchcmd " test.db" ".mode html" +} {0 {}} +do_test shell1-3.13.6 { + catchcmd " test.db" ".mode insert" +} {0 {}} +do_test shell1-3.13.7 { + catchcmd " test.db" ".mode line" +} {0 {}} +do_test shell1-3.13.8 { + catchcmd " test.db" ".mode list" +} {0 {}} +do_test shell1-3.13.9 { + catchcmd " test.db" ".mode tabs" +} {0 {}} +do_test shell1-3.13.10 { + catchcmd " test.db" ".mode tcl" +} {0 {}} +do_test shell1-3.13.11 { + # too many arguments + catchcmd " test.db" ".mode tcl BAD" +} {1 {Error: invalid arguments: "BAD". Enter ".help" for help}} + +# don't allow partial mode type matches +do_test shell1-3.13.12 { + catchcmd " test.db" ".mode l" +} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +do_test shell1-3.13.13 { + catchcmd " test.db" ".mode li" +} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +do_test shell1-3.13.14 { + catchcmd " test.db" ".mode lin" +} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} + +# .nullvalue STRING Print STRING in place of NULL values +do_test shell1-3.14.1 { + catchcmd " test.db" ".nullvalue" +} {1 {Error: unknown command or invalid arguments: "nullvalue". Enter ".help" for help}} +do_test shell1-3.14.2 { + catchcmd " test.db" ".nullvalue FOO" +} {0 {}} +do_test shell1-3.14.3 { + # too many arguments + catchcmd " test.db" ".nullvalue FOO BAD" +} {1 {Error: unknown command or invalid arguments: "nullvalue". Enter ".help" for help}} + +# .output FILENAME Send output to FILENAME +do_test shell1-3.15.1 { + catchcmd " test.db" ".output" +} {1 {Error: unknown command or invalid arguments: "output". Enter ".help" for help}} +do_test shell1-3.15.2 { + catchcmd " test.db" ".output FOO" +} {0 {}} +do_test shell1-3.15.3 { + # too many arguments + catchcmd " test.db" ".output FOO BAD" +} {1 {Error: unknown command or invalid arguments: "output". Enter ".help" for help}} + +# .output stdout Send output to the screen +do_test shell1-3.16.1 { + catchcmd " test.db" ".output stdout" +} {0 {}} +do_test shell1-3.16.2 { + # too many arguments + catchcmd " test.db" ".output stdout BAD" +} {1 {Error: unknown command or invalid arguments: "output". Enter ".help" for help}} + +# .prompt MAIN CONTINUE Replace the standard prompts +do_test shell1-3.17.1 { + catchcmd " test.db" ".prompt" +} {1 {Error: unknown command or invalid arguments: "prompt". Enter ".help" for help}} +do_test shell1-3.17.2 { + catchcmd " test.db" ".prompt FOO" +} {0 {}} +do_test shell1-3.17.3 { + catchcmd " test.db" ".prompt FOO BAR" +} {0 {}} +do_test shell1-3.17.4 { + # too many arguments + catchcmd " test.db" ".prompt FOO BAR BAD" +} {1 {Error: unknown command or invalid arguments: "prompt". Enter ".help" for help}} + +# .quit Exit this program +do_test shell1-3.18.1 { + catchcmd " test.db" ".quit" +} {0 {}} +do_test shell1-3.18.2 { + # too many arguments + catchcmd " test.db" ".quit BAD" +} {1 {Error: unknown command or invalid arguments: "quit". Enter ".help" for help}} + +# .read FILENAME Execute SQL in FILENAME +do_test shell1-3.19.1 { + catchcmd " test.db" ".read" +} {1 {Error: unknown command or invalid arguments: "read". Enter ".help" for help}} +do_test shell1-3.19.2 { + file delete -force FOO + catchcmd " test.db" ".read FOO" +} {1 {Error: cannot open "FOO"}} +do_test shell1-3.19.3 { + # too many arguments + catchcmd " test.db" ".read FOO BAD" +} {1 {Error: unknown command or invalid arguments: "read". Enter ".help" for help}} + +# .restore ?DB? FILE Restore content of DB (default "main") from FILE +do_test shell1-3.20.1 { + catchcmd " test.db" ".restore" +} {1 {Error: unknown command or invalid arguments: "restore". Enter ".help" for help}} +do_test shell1-3.20.2 { + # catchcmd " test.db" ".restore FOO" + #TBD!!! this asserts currently +} {} +do_test shell1-3.20.3 { + catchcmd " test.db" ".restore FOO BAR" +} {1 {Error: unknown database FOO}} +do_test shell1-3.20.4 { + # too many arguments + catchcmd " test.db" ".restore FOO BAR BAD" +} {1 {Error: unknown command or invalid arguments: "restore". Enter ".help" for help}} + +# .schema ?TABLE? Show the CREATE statements +# If TABLE specified, only show tables matching +# LIKE pattern TABLE. +do_test shell1-3.21.1 { + catchcmd " test.db" ".schema" +} {0 {}} +do_test shell1-3.21.2 { + catchcmd " test.db" ".schema FOO" +} {0 {}} +do_test shell1-3.21.3 { + # too many arguments + catchcmd " test.db" ".schema FOO BAD" +} {1 {Error: unknown command or invalid arguments: "schema". Enter ".help" for help}} + +# .separator STRING Change separator used by output mode and .import +do_test shell1-3.22.1 { + catchcmd " test.db" ".separator" +} {1 {Error: unknown command or invalid arguments: "separator". Enter ".help" for help}} +do_test shell1-3.22.2 { + catchcmd " test.db" ".separator FOO" +} {0 {}} +do_test shell1-3.22.3 { + # too many arguments + catchcmd " test.db" ".separator FOO BAD" +} {1 {Error: unknown command or invalid arguments: "separator". Enter ".help" for help}} + +# .show Show the current values for various settings +do_test shell1-3.23.1 { + set res [catchcmd " test.db" ".show"] + list [regexp {echo:} $res] \ + [regexp {explain:} $res] \ + [regexp {headers:} $res] \ + [regexp {mode:} $res] \ + [regexp {nullvalue:} $res] \ + [regexp {output:} $res] \ + [regexp {separator:} $res] \ + [regexp {width:} $res] +} {1 1 1 1 1 1 1 1} +do_test shell1-3.23.2 { + # too many arguments + catchcmd " test.db" ".show BAD" +} {1 {Error: unknown command or invalid arguments: "show". Enter ".help" for help}} + +# .tables ?TABLE? List names of tables +# If TABLE specified, only list tables matching +# LIKE pattern TABLE. +do_test shell1-3.24.1 { + catchcmd " test.db" ".tables" +} {0 {}} +do_test shell1-3.24.2 { + catchcmd " test.db" ".tables FOO" +} {0 {}} +do_test shell1-3.24.3 { + # too many arguments + catchcmd " test.db" ".tables FOO BAD" +} {1 {Error: unknown command or invalid arguments: "tables". Enter ".help" for help}} + +# .timeout MS Try opening locked tables for MS milliseconds +do_test shell1-3.25.1 { + catchcmd " test.db" ".timeout" +} {1 {Error: unknown command or invalid arguments: "timeout". Enter ".help" for help}} +do_test shell1-3.25.2 { + catchcmd " test.db" ".timeout zzz" + # this should be treated the same as a '0' timeout +} {0 {}} +do_test shell1-3.25.3 { + catchcmd " test.db" ".timeout 1" +} {0 {}} +do_test shell1-3.25.4 { + # too many arguments + catchcmd " test.db" ".timeout 1 BAD" +} {1 {Error: unknown command or invalid arguments: "timeout". Enter ".help" for help}} + +# .width NUM NUM ... Set column widths for "column" mode +do_test shell1-3.26.1 { + catchcmd " test.db" ".width" +} {1 {Error: unknown command or invalid arguments: "width". Enter ".help" for help}} +do_test shell1-3.26.2 { + catchcmd " test.db" ".width xxx" + # this should be treated the same as a '0' width for col 1 +} {0 {}} +do_test shell1-3.26.3 { + catchcmd " test.db" ".width xxx yyy" + # this should be treated the same as a '0' width for col 1 and 2 +} {0 {}} +do_test shell1-3.26.4 { + catchcmd " test.db" ".width 1 1" + # this should be treated the same as a '1' width for col 1 and 2 +} {0 {}} + +# .timer ON|OFF Turn the CPU timer measurement on or off +do_test shell1-3.27.1 { + catchcmd " test.db" ".timer" +} {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}} +do_test shell1-3.27.2 { + catchcmd " test.db" ".timer ON" +} {0 {}} +do_test shell1-3.27.3 { + catchcmd " test.db" ".timer OFF" +} {0 {}} +do_test shell1-3.27.4 { + # too many arguments + catchcmd " test.db" ".timer OFF BAD" +} {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}} + +# ADDED tool/shell2.test Index: tool/shell2.test ================================================================== --- /dev/null +++ tool/shell2.test @@ -0,0 +1,85 @@ +# 2009 Nov 11 +# +# 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. +# +#*********************************************************************** +# +# The focus of this file is testing the CLI shell tool. +# +# $Id: shell2.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $ +# + +# Test plan: +# +# shell2-1.*: Misc. test of various tickets and reported errors. +# + +package require sqlite3 + +set CLI "./sqlite" + +proc do_test {name cmd expected} { + puts -nonewline "$name ..." + set res [uplevel $cmd] + if {$res eq $expected} { + puts Ok + } else { + puts Error + puts " Got: $res" + puts " Expected: $expected" + exit + } +} + +proc execsql {sql} { + uplevel [list db eval $sql] +} + +proc catchsql {sql} { + set rc [catch {uplevel [list db eval $sql]} msg] + list $rc $msg +} + +proc catchcmd {db cmd} { + global CLI + set out [open cmds.txt w] + puts $out $cmd + close $out + set line "exec $CLI $db < cmds.txt" + set rc [catch { eval $line } msg] + list $rc $msg +} + +file delete -force test.db test.db.journal +sqlite3 db test.db + + +#---------------------------------------------------------------------------- +# shell2-1.*: Misc. test of various tickets and reported errors. +# + +# Batch mode not creating databases. +# Reported on mailing list by Ken Zalewski. +# Ticket [aeff892c57]. +do_test shell2-1.1.1 { + file delete -force foo.db + set rc [ catchcmd "-batch foo.db" "CREATE TABLE t1(a);" ] + set fexist [file exist foo.db] + list $rc $fexist +} {{0 {}} 1} + +# Shell silently ignores extra parameters. +# Ticket [f5cb008a65]. +do_test shell2-1.2.1 { + set rc [catch { eval exec $CLI \":memory:\" \"select 3\" \"select 4\" } msg] + list $rc \ + [regexp {Error: too many options: "select 4"} $msg] +} {1 1} + + + Index: tool/vdbe-compress.tcl ================================================================== --- tool/vdbe-compress.tcl +++ tool/vdbe-compress.tcl @@ -93,10 +93,11 @@ append afterUnion $line\n set vlist {} } elseif {[llength $vlist]>0} { append line " " foreach v $vlist { + regsub -all "(\[^a-zA-Z0-9>.\])${v}(\\W)" $line "\\1u.$sname.$v\\2" line regsub -all "(\[^a-zA-Z0-9>.\])${v}(\\W)" $line "\\1u.$sname.$v\\2" line } append afterUnion [string trimright $line]\n } elseif {$line=="" && [eof stdin]} { # no-op