diff --git a/VERSION b/VERSION index 897e56be0b..c90806c1a7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7.6 +3.7.6.1 diff --git a/configure b/configure index 7d12f8c371..c18e5d18ec 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for sqlite 3.7.6. +# Generated by GNU Autoconf 2.62 for sqlite 3.7.6.1. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. @@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.7.6' -PACKAGE_STRING='sqlite 3.7.6' +PACKAGE_VERSION='3.7.6.1' +PACKAGE_STRING='sqlite 3.7.6.1' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. @@ -1485,7 +1485,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.7.6 to adapt to many kinds of systems. +\`configure' configures sqlite 3.7.6.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1550,7 +1550,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.7.6:";; + short | recursive ) echo "Configuration of sqlite 3.7.6.1:";; esac cat <<\_ACEOF @@ -1666,7 +1666,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.7.6 +sqlite configure 3.7.6.1 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1680,7 +1680,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.7.6, which was +It was created by sqlite $as_me 3.7.6.1, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -13942,7 +13942,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.7.6, which was +This file was extended by sqlite $as_me 3.7.6.1, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13995,7 +13995,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.7.6 +sqlite config.status 3.7.6.1 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/manifest b/manifest index f38f5e51b4..bae894c06c 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Fix\sfurther\smissing\scomments\sand\sother\sminor\sissues\sin\sthe\ssession\smodule\scode. -D 2011-04-18T15:47:08.694 +C Merge\strunk\schanges\sinto\ssessions\sbranch. +D 2011-04-18T17:30:56.521 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 -F VERSION d1056b26608c9a2695187d57cac12ae89492639d +F VERSION c97e5dcdea2407f4a94f9740294cdf39ce9e88c4 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248 @@ -22,7 +22,7 @@ F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 868fdb48c028421a203470e15c69ada15b9ba673 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure c38c1947db7ed4adaed2affcb09cea9d3acd5a9a x +F configure 61dbf78cdc4d6a871333dc599c130be6cce865c5 x F configure.ac 87a3c71bbe9c925381c154413eea7f3cdc397244 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 @@ -154,7 +154,7 @@ F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 3ae0d52da013a6326310655be6473fd472347b85 F src/main.c 8b97db74cb876bf34ca4fb3720b18e4ffdcf9fd5 -F src/malloc.c 788f2ed928786dfe305b6783d551d6b1a9080976 +F src/malloc.c 74c740e8ba22b806cfb980c8c0ddea1cbd54a20e F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206 F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf @@ -164,16 +164,16 @@ F src/memjournal.c 0ebce851677a7ac035ba1512a7e65851b34530c6 F src/mutex.c 6949180803ff05a7d0e2b9334a95b4fb5a00e23f F src/mutex.h fe2ef5e1c4dae531d5a544f9241f19c56d26803d F src/mutex_noop.c d5cfbca87168c661a0b118cd8e329a908e453151 -F src/mutex_os2.c f5d09e85b914643c230aa97db709fc0db370d93c +F src/mutex_os2.c 882d735098c07c8c6a5472b8dd66e19675fe117f F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579 F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33 F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f -F src/os_os2.c 2596fd2d5d0976c6c0c628d0c3c7c4e7a724f4cf -F src/os_unix.c a8fe62148d41e54e383d3360a711a01595feef58 -F src/os_win.c 24d72407a90551969744cf9bcbb1b4c72c5fa845 +F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 +F src/os_unix.c d7889a0f9389c8c2e1d3b380f5aa1256c22a90e8 +F src/os_win.c d149b9a7dfdd38de09afc054f8168cd3cd80630b F src/pager.c 055239dcdfe12b3f5d97f6f01f85da01e2d6d912 F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 @@ -186,9 +186,9 @@ F src/printf.c 585a36b6a963df832cfb69505afa3a34ed5ef8a1 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 -F src/select.c 649a6f10f7eb7b52a5a28847773cb9968a828ae8 -F src/shell.c 9c8389796764f65d4506bcd614ac8061f4160d5c -F src/sqlite.h.in 9cff46ef60540f044c1b665db606aa63ba67048f +F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff +F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79 +F src/sqlite.h.in 0cf61c41c48e1e6b863ff8cf9ecd69620932330e F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 F src/sqliteInt.h 9a29e5bb82f3abef6b4af91e18d637050fa3c883 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d @@ -243,7 +243,7 @@ F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080 F src/trigger.c 144cc18bb701f3286484aae4292a9531f09278c8 F src/update.c 3f3f3bb734a0da1dffd0ed33e504642b35ed3605 F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60 -F src/util.c cd997077bad039efc0597eb027c929658f93c018 +F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e F src/vdbe.c dd53dda1cf786397e72643a497b5c2f368ff11ba F src/vdbe.h 44fd57aeed86da0cd31206626c13cdde0e72cc0e @@ -398,7 +398,7 @@ F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3 F test/exclusive.test 53e1841b422e554cecf0160f937c473d6d0e3062 F test/exclusive2.test 343d55130c12c67b8bf10407acec043a6c26c86b F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 -F test/exists.test 81363f6982ea49dfd820a50845466390e60a4a0c +F test/exists.test 5e2d64b4eb5a9d08876599bdae2e1213d2d12e2a F test/expr.test 19e8ac40313e2282a47b586d11c4892040990d3a F test/fallocate.test 43dc34b8c24be6baffadc3b4401ee15710ce83c6 F test/filectrl.test 97003734290887566e01dded09dc9e99cb937e9e @@ -623,7 +623,7 @@ F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 -F test/quota.test ddafe133653093eb9a99ccd6264884ae43f9c9b8 +F test/quota.test 48c3a5a98687d67ef06fc16d2e603284756bbec3 F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459 F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a @@ -688,7 +688,7 @@ F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082 -F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3 +F test/sync.test 2bd73b585089c99e76b3e1796c42059b7d89d872 F test/syscall.test 707c95e4ab7863e13f1293c6b0c76bead30249b3 F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f F test/table.test 04ba066432430657712d167ebf28080fe878d305 @@ -838,7 +838,7 @@ F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4 F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31 F test/triggerA.test eaf11a29db2a11967d2d4b49d37f92bce598194e F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe -F test/triggerC.test 8a691ff6dd47df2e57395bbec4b62101fac0f363 +F test/triggerC.test 811ab569af9e6fc894afbcc0d77d14500b2406c5 F test/triggerD.test c6add3817351451e419f6ff9e9a259b02b6e2de7 F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff @@ -846,6 +846,7 @@ F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150 F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2 F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e +F test/unordered.test e81169ce2a8f31b2c6b66af691887e1376ab3ced F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172 F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae F test/vacuum.test 29b60e8cc9e573b39676df6c4a75fe9e02d04a09 @@ -873,7 +874,7 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d F test/wal.test 5617ad308bfdb8a8885220d8a261a6096a8d7e57 -F test/wal2.test e561a8c6fdd1c2cd1876f3e39757934e7b7361f8 +F test/wal2.test aa0fb2314b3235be4503c06873e41ebfc0757782 F test/wal3.test 5c396cc22497244d627306f4c1d360167353f8dd F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30 F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b @@ -920,12 +921,13 @@ F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87 F tool/omittest.tcl b1dd290c1596e0f31fd335160a74ec5dfea3df4a F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a -F tool/shell1.test f608a009b04c490fd360c5ded458a6f98b4e7ec4 +F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 +F tool/shell1.test fee04fce1bf55e6e081523545fd4e0e83490ff8e F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3 F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836 F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce F tool/shell5.test 62bfaf9267296da1b91e4b1c03e44e7b393f6a94 -F tool/showdb.c 471c0f8fa472e71bb7654500096a5bdb4ea1fb2a +F tool/showdb.c 43e913d954684c2f5007dcab46d1a1308852a0ad F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02 F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9 F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe @@ -938,7 +940,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 20d7c280235201e519f296372f269e7cecda24da -R 22fa2c52781f892ef560f2c8f63b1707 +P 99f0f35092b0b78b7016b21c242da263ab64b77b 3e135748f1efacb52b414b3ac3f4ae2c08bcd8fb +R b2692e50ee7820b096983b9aa33c1efd U dan -Z e6f0d0d675f579cb5b46b0fe2c73f457 +Z c7fa020efb14904762c294d2eedf7456 diff --git a/manifest.uuid b/manifest.uuid index 240a76a7a9..446a5128f3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -99f0f35092b0b78b7016b21c242da263ab64b77b \ No newline at end of file +b91b4c31fe311b292044c9c747feba294ffce25c \ No newline at end of file diff --git a/src/malloc.c b/src/malloc.c index 50fdf524c5..3585f1245d 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -266,7 +266,7 @@ static int mallocWithAlarm(int n, void **pp){ sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - if( nUsed+nFull >= mem0.alarmThreshold ){ + if( nUsed >= mem0.alarmThreshold - nFull ){ mem0.nearlyFull = 1; sqlite3MallocAlarm(nFull); }else{ @@ -507,7 +507,7 @@ void sqlite3DbFree(sqlite3 *db, void *p){ ** Change the size of an existing memory allocation */ void *sqlite3Realloc(void *pOld, int nBytes){ - int nOld, nNew; + int nOld, nNew, nDiff; void *pNew; if( pOld==0 ){ return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */ @@ -530,8 +530,9 @@ void *sqlite3Realloc(void *pOld, int nBytes){ }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); - if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= - mem0.alarmThreshold ){ + nDiff = nNew - nOld; + if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= + mem0.alarmThreshold-nDiff ){ sqlite3MallocAlarm(nNew-nOld); } assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); @@ -543,7 +544,7 @@ void *sqlite3Realloc(void *pOld, int nBytes){ } if( pNew ){ nNew = sqlite3MallocSize(pNew); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); + sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nDiff); } sqlite3_mutex_leave(mem0.mutex); }else{ diff --git a/src/mutex_os2.c b/src/mutex_os2.c index 85129f3e48..ce650d994e 100644 --- a/src/mutex_os2.c +++ b/src/mutex_os2.c @@ -251,7 +251,7 @@ static void os2MutexLeave(sqlite3_mutex *p){ #endif } -SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ +sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ static const sqlite3_mutex_methods sMutex = { os2MutexInit, os2MutexEnd, diff --git a/src/os_os2.c b/src/os_os2.c index 2a786b4dea..487ac3c3c8 100644 --- a/src/os_os2.c +++ b/src/os_os2.c @@ -55,20 +55,35 @@ */ #include "os_common.h" +/* Forward references */ +typedef struct os2File os2File; /* The file structure */ +typedef struct os2ShmNode os2ShmNode; /* A shared descritive memory node */ +typedef struct os2ShmLink os2ShmLink; /* A connection to shared-memory */ + /* ** The os2File structure is subclass of sqlite3_file specific for the OS/2 ** protability layer. */ -typedef struct os2File os2File; struct os2File { const sqlite3_io_methods *pMethod; /* Always the first entry */ HFILE h; /* Handle for accessing the file */ - char* pathToDel; /* Name of file to delete on close, NULL if not */ - unsigned char locktype; /* Type of lock currently held on this file */ + int flags; /* Flags provided to os2Open() */ + int locktype; /* Type of lock currently held on this file */ + int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ + char *zFullPathCp; /* Full path name of this file */ + os2ShmLink *pShmLink; /* Instance of shared memory on this file */ }; #define LOCK_TIMEOUT 10L /* the default locking timeout */ +/* +** Missing from some versions of the OS/2 toolkit - +** used to allocate from high memory if possible +*/ +#ifndef OBJ_ANY +# define OBJ_ANY 0x00000400 +#endif + /***************************************************************************** ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. @@ -78,21 +93,24 @@ struct os2File { ** Close a file. */ static int os2Close( sqlite3_file *id ){ - APIRET rc = NO_ERROR; - os2File *pFile; - if( id && (pFile = (os2File*)id) != 0 ){ - OSTRACE(( "CLOSE %d\n", pFile->h )); - rc = DosClose( pFile->h ); - pFile->locktype = NO_LOCK; - if( pFile->pathToDel != NULL ){ - rc = DosForceDelete( (PSZ)pFile->pathToDel ); - free( pFile->pathToDel ); - pFile->pathToDel = NULL; - } - id = 0; - OpenCounter( -1 ); - } + APIRET rc; + os2File *pFile = (os2File*)id; + assert( id!=0 ); + OSTRACE(( "CLOSE %d (%s)\n", pFile->h, pFile->zFullPathCp )); + + rc = DosClose( pFile->h ); + + if( pFile->flags & SQLITE_OPEN_DELETEONCLOSE ) + DosForceDelete( (PSZ)pFile->zFullPathCp ); + + free( pFile->zFullPathCp ); + pFile->zFullPathCp = NULL; + pFile->locktype = NO_LOCK; + pFile->h = (HFILE)-1; + pFile->flags = 0; + + OpenCounter( -1 ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR; } @@ -165,10 +183,21 @@ static int os2Write( ** Truncate an open file to a specified size */ static int os2Truncate( sqlite3_file *id, i64 nByte ){ - APIRET rc = NO_ERROR; + APIRET rc; os2File *pFile = (os2File*)id; + assert( id!=0 ); OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte )); SimulateIOError( return SQLITE_IOERR_TRUNCATE ); + + /* If the user has configured a chunk-size for this file, truncate the + ** file so that it consists of an integer number of chunks (i.e. the + ** actual file size after the operation may be larger than the requested + ** size). + */ + if( pFile->szChunk ){ + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; + } + rc = DosSetFileSize( pFile->h, nByte ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; } @@ -532,6 +561,20 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ ((os2File*)id)->h, ((os2File*)id)->locktype )); return SQLITE_OK; } + case SQLITE_FCNTL_CHUNK_SIZE: { + ((os2File*)id)->szChunk = *(int*)pArg; + return SQLITE_OK; + } + case SQLITE_FCNTL_SIZE_HINT: { + sqlite3_int64 sz = *(sqlite3_int64*)pArg; + SimulateIOErrorBenign(1); + os2Truncate(id, sz); + SimulateIOErrorBenign(0); + return SQLITE_OK; + } + case SQLITE_FCNTL_SYNC_OMITTED: { + return SQLITE_OK; + } } return SQLITE_NOTFOUND; } @@ -547,6 +590,7 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ ** same for both. */ static int os2SectorSize(sqlite3_file *id){ + UNUSED_PARAMETER(id); return SQLITE_DEFAULT_SECTOR_SIZE; } @@ -554,7 +598,8 @@ static int os2SectorSize(sqlite3_file *id){ ** Return a vector of device characteristics. */ static int os2DeviceCharacteristics(sqlite3_file *id){ - return 0; + UNUSED_PARAMETER(id); + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } @@ -641,12 +686,663 @@ char *convertCpPathToUtf8( const char *in ){ return out; } + +#ifndef SQLITE_OMIT_WAL + +/* +** Use main database file for interprocess locking. If un-defined +** a separate file is created for this purpose. The file will be +** used only to set file locks. There will be no data written to it. +*/ +#define SQLITE_OS2_NO_WAL_LOCK_FILE + +#if 0 +static void _ERR_TRACE( const char *fmt, ... ) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fflush(stderr); +} +#define ERR_TRACE(rc, msg) \ + if( (rc) != SQLITE_OK ) _ERR_TRACE msg; +#else +#define ERR_TRACE(rc, msg) +#endif + +/* +** Helper functions to obtain and relinquish the global mutex. The +** global mutex is used to protect os2ShmNodeList. +** +** Function os2ShmMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() +** statements. e.g. +** +** os2ShmEnterMutex() +** assert( os2ShmMutexHeld() ); +** os2ShmLeaveMutex() +*/ +static void os2ShmEnterMutex(void){ + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +static void os2ShmLeaveMutex(void){ + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +#ifdef SQLITE_DEBUG +static int os2ShmMutexHeld(void) { + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +int GetCurrentProcessId(void) { + PPIB pib; + DosGetInfoBlocks(NULL, &pib); + return (int)pib->pib_ulpid; +} +#endif + +/* +** Object used to represent a the shared memory area for a single log file. +** When multiple threads all reference the same log-summary, each thread has +** its own os2File object, but they all point to a single instance of this +** object. In other words, each log-summary is opened only once per process. +** +** os2ShmMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: +** +** nRef +** pNext +** +** The following fields are read-only after the object is created: +** +** szRegion +** hLockFile +** shmBaseName +** +** Either os2ShmNode.mutex must be held or os2ShmNode.nRef==0 and +** os2ShmMutexHeld() is true when reading or writing any other field +** in this structure. +** +*/ +struct os2ShmNode { + sqlite3_mutex *mutex; /* Mutex to access this object */ + os2ShmNode *pNext; /* Next in list of all os2ShmNode objects */ + + int szRegion; /* Size of shared-memory regions */ + + int nRegion; /* Size of array apRegion */ + void **apRegion; /* Array of pointers to shared-memory regions */ + + int nRef; /* Number of os2ShmLink objects pointing to this */ + os2ShmLink *pFirst; /* First os2ShmLink object pointing to this */ + + HFILE hLockFile; /* File used for inter-process memory locking */ + char shmBaseName[1]; /* Name of the memory object !!! must last !!! */ +}; + + +/* +** Structure used internally by this VFS to record the state of an +** open shared memory connection. +** +** The following fields are initialized when this object is created and +** are read-only thereafter: +** +** os2Shm.pShmNode +** os2Shm.id +** +** All other fields are read/write. The os2Shm.pShmNode->mutex must be held +** while accessing any read/write fields. +*/ +struct os2ShmLink { + os2ShmNode *pShmNode; /* The underlying os2ShmNode object */ + os2ShmLink *pNext; /* Next os2Shm with the same os2ShmNode */ + u32 sharedMask; /* Mask of shared locks held */ + u32 exclMask; /* Mask of exclusive locks held */ +#ifdef SQLITE_DEBUG + u8 id; /* Id of this connection with its os2ShmNode */ +#endif +}; + + +/* +** A global list of all os2ShmNode objects. +** +** The os2ShmMutexHeld() must be true while reading or writing this list. +*/ +static os2ShmNode *os2ShmNodeList = NULL; + +/* +** Constants used for locking +*/ +#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE +#define OS2_SHM_BASE (PENDING_BYTE + 0x10000) /* first lock byte */ +#else +#define OS2_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ +#endif + +#define OS2_SHM_DMS (OS2_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ + +/* +** Apply advisory locks for all n bytes beginning at ofst. +*/ +#define _SHM_UNLCK 1 /* no lock */ +#define _SHM_RDLCK 2 /* shared lock, no wait */ +#define _SHM_WRLCK 3 /* exlusive lock, no wait */ +#define _SHM_WRLCK_WAIT 4 /* exclusive lock, wait */ +static int os2ShmSystemLock( + os2ShmNode *pNode, /* Apply locks to this open shared-memory segment */ + int lockType, /* _SHM_UNLCK, _SHM_RDLCK, _SHM_WRLCK or _SHM_WRLCK_WAIT */ + int ofst, /* Offset to first byte to be locked/unlocked */ + int nByte /* Number of bytes to lock or unlock */ +){ + APIRET rc; + FILELOCK area; + ULONG mode, timeout; + + /* Access to the os2ShmNode object is serialized by the caller */ + assert( sqlite3_mutex_held(pNode->mutex) || pNode->nRef==0 ); + + mode = 1; /* shared lock */ + timeout = 0; /* no wait */ + area.lOffset = ofst; + area.lRange = nByte; + + switch( lockType ) { + case _SHM_WRLCK_WAIT: + timeout = (ULONG)-1; /* wait forever */ + case _SHM_WRLCK: + mode = 0; /* exclusive lock */ + case _SHM_RDLCK: + rc = DosSetFileLocks(pNode->hLockFile, + NULL, &area, timeout, mode); + break; + /* case _SHM_UNLCK: */ + default: + rc = DosSetFileLocks(pNode->hLockFile, + &area, NULL, 0, 0); + break; + } + + OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n", + pNode->hLockFile, + rc==SQLITE_OK ? "ok" : "failed", + lockType==_SHM_UNLCK ? "Unlock" : "Lock", + rc)); + + ERR_TRACE(rc, ("os2ShmSystemLock: %d %s\n", rc, pNode->shmBaseName)) + + return ( rc == 0 ) ? SQLITE_OK : SQLITE_BUSY; +} + +/* +** Find an os2ShmNode in global list or allocate a new one, if not found. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static int os2OpenSharedMemory( os2File *fd, int szRegion ) { + os2ShmLink *pLink; + os2ShmNode *pNode; + int cbShmName, rc = SQLITE_OK; + char shmName[CCHMAXPATH + 30]; +#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE + ULONG action; +#endif + + /* We need some additional space at the end to append the region number */ + cbShmName = sprintf(shmName, "\\SHAREMEM\\%s", fd->zFullPathCp ); + if( cbShmName >= CCHMAXPATH-8 ) + return SQLITE_IOERR_SHMOPEN; + + /* Replace colon in file name to form a valid shared memory name */ + shmName[10+1] = '!'; + + /* Allocate link object (we free it later in case of failure) */ + pLink = sqlite3_malloc( sizeof(*pLink) ); + if( !pLink ) + return SQLITE_NOMEM; + + /* Access node list */ + os2ShmEnterMutex(); + + /* Find node by it's shared memory base name */ + for( pNode = os2ShmNodeList; + pNode && stricmp(shmName, pNode->shmBaseName) != 0; + pNode = pNode->pNext ) ; + + /* Not found: allocate a new node */ + if( !pNode ) { + pNode = sqlite3_malloc( sizeof(*pNode) + cbShmName ); + if( pNode ) { + memset(pNode, 0, sizeof(*pNode) ); + pNode->szRegion = szRegion; + pNode->hLockFile = (HFILE)-1; + strcpy(pNode->shmBaseName, shmName); + +#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE + if( DosDupHandle(fd->h, &pNode->hLockFile) != 0 ) { +#else + sprintf(shmName, "%s-lck", fd->zFullPathCp); + if( DosOpen((PSZ)shmName, &pNode->hLockFile, &action, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW, + OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | + OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR, + NULL) != 0 ) { +#endif + sqlite3_free(pNode); + rc = SQLITE_IOERR; + } else { + pNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( !pNode->mutex ) { + sqlite3_free(pNode); + rc = SQLITE_NOMEM; + } + } + } else { + rc = SQLITE_NOMEM; + } + + if( rc == SQLITE_OK ) { + pNode->pNext = os2ShmNodeList; + os2ShmNodeList = pNode; + } else { + pNode = NULL; + } + } else if( pNode->szRegion != szRegion ) { + rc = SQLITE_IOERR_SHMSIZE; + pNode = NULL; + } + + if( pNode ) { + sqlite3_mutex_enter(pNode->mutex); + + memset(pLink, 0, sizeof(*pLink)); + + pLink->pShmNode = pNode; + pLink->pNext = pNode->pFirst; + pNode->pFirst = pLink; + pNode->nRef++; + + fd->pShmLink = pLink; + + sqlite3_mutex_leave(pNode->mutex); + + } else { + /* Error occured. Free our link object. */ + sqlite3_free(pLink); + } + + os2ShmLeaveMutex(); + + ERR_TRACE(rc, ("os2OpenSharedMemory: %d %s\n", rc, fd->zFullPathCp)) + + return rc; +} + +/* +** Purge the os2ShmNodeList list of all entries with nRef==0. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static void os2PurgeShmNodes( int deleteFlag ) { + os2ShmNode *pNode; + os2ShmNode **ppNode; + + os2ShmEnterMutex(); + + ppNode = &os2ShmNodeList; + + while( *ppNode ) { + pNode = *ppNode; + + if( pNode->nRef == 0 ) { + *ppNode = pNode->pNext; + + if( pNode->apRegion ) { + /* Prevent other processes from resizing the shared memory */ + os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1); + + while( pNode->nRegion-- ) { +#ifdef SQLITE_DEBUG + int rc = +#endif + DosFreeMem(pNode->apRegion[pNode->nRegion]); + + OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", + (int)GetCurrentProcessId(), pNode->nRegion, + rc == 0 ? "ok" : "failed")); + } + + /* Allow other processes to resize the shared memory */ + os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1); + + sqlite3_free(pNode->apRegion); + } + + DosClose(pNode->hLockFile); + +#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE + if( deleteFlag ) { + char fileName[CCHMAXPATH]; + /* Skip "\\SHAREMEM\\" */ + sprintf(fileName, "%s-lck", pNode->shmBaseName + 10); + /* restore colon */ + fileName[1] = ':'; + + DosForceDelete(fileName); + } +#endif + + sqlite3_mutex_free(pNode->mutex); + + sqlite3_free(pNode); + + } else { + ppNode = &pNode->pNext; + } + } + + os2ShmLeaveMutex(); +} + +/* +** This function is called to obtain a pointer to region iRegion of the +** shared-memory associated with the database file id. Shared-memory regions +** are numbered starting from zero. Each shared-memory region is szRegion +** bytes in size. +** +** If an error occurs, an error code is returned and *pp is set to NULL. +** +** Otherwise, if the bExtend parameter is 0 and the requested shared-memory +** region has not been allocated (by any client, including one running in a +** separate process), then *pp is set to NULL and SQLITE_OK returned. If +** bExtend is non-zero and the requested shared-memory region has not yet +** been allocated, it is allocated by this function. +** +** If the shared-memory region has already been allocated or is allocated by +** this call as described above, then it is mapped into this processes +** address space (if it is not already), *pp is set to point to the mapped +** memory and SQLITE_OK returned. +*/ +static int os2ShmMap( + sqlite3_file *id, /* Handle open on database file */ + int iRegion, /* Region to retrieve */ + int szRegion, /* Size of regions */ + int bExtend, /* True to extend block if necessary */ + void volatile **pp /* OUT: Mapped memory */ +){ + PVOID pvTemp; + void **apRegion; + os2ShmNode *pNode; + int n, rc = SQLITE_OK; + char shmName[CCHMAXPATH]; + os2File *pFile = (os2File*)id; + + *pp = NULL; + + if( !pFile->pShmLink ) + rc = os2OpenSharedMemory( pFile, szRegion ); + + if( rc == SQLITE_OK ) { + pNode = pFile->pShmLink->pShmNode ; + + sqlite3_mutex_enter(pNode->mutex); + + assert( szRegion==pNode->szRegion ); + + /* Unmapped region ? */ + if( iRegion >= pNode->nRegion ) { + /* Prevent other processes from resizing the shared memory */ + os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1); + + apRegion = sqlite3_realloc( + pNode->apRegion, (iRegion + 1) * sizeof(apRegion[0])); + + if( apRegion ) { + pNode->apRegion = apRegion; + + while( pNode->nRegion <= iRegion ) { + sprintf(shmName, "%s-%u", + pNode->shmBaseName, pNode->nRegion); + + if( DosGetNamedSharedMem(&pvTemp, (PSZ)shmName, + PAG_READ | PAG_WRITE) != NO_ERROR ) { + if( !bExtend ) + break; + + if( DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion, + PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_ANY) != NO_ERROR && + DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion, + PAG_READ | PAG_WRITE | PAG_COMMIT) != NO_ERROR ) { + rc = SQLITE_NOMEM; + break; + } + } + + apRegion[pNode->nRegion++] = pvTemp; + } + + /* zero out remaining entries */ + for( n = pNode->nRegion; n <= iRegion; n++ ) + pNode->apRegion[n] = NULL; + + /* Return this region (maybe zero) */ + *pp = pNode->apRegion[iRegion]; + } else { + rc = SQLITE_NOMEM; + } + + /* Allow other processes to resize the shared memory */ + os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1); + + } else { + /* Region has been mapped previously */ + *pp = pNode->apRegion[iRegion]; + } + + sqlite3_mutex_leave(pNode->mutex); + } + + ERR_TRACE(rc, ("os2ShmMap: %s iRgn = %d, szRgn = %d, bExt = %d : %d\n", + pFile->zFullPathCp, iRegion, szRegion, bExtend, rc)) + + return rc; +} + +/* +** Close a connection to shared-memory. Delete the underlying +** storage if deleteFlag is true. +** +** If there is no shared memory associated with the connection then this +** routine is a harmless no-op. +*/ +static int os2ShmUnmap( + sqlite3_file *id, /* The underlying database file */ + int deleteFlag /* Delete shared-memory if true */ +){ + os2File *pFile = (os2File*)id; + os2ShmLink *pLink = pFile->pShmLink; + + if( pLink ) { + int nRef = -1; + os2ShmLink **ppLink; + os2ShmNode *pNode = pLink->pShmNode; + + sqlite3_mutex_enter(pNode->mutex); + + for( ppLink = &pNode->pFirst; + *ppLink && *ppLink != pLink; + ppLink = &(*ppLink)->pNext ) ; + + assert(*ppLink); + + if( *ppLink ) { + *ppLink = pLink->pNext; + nRef = --pNode->nRef; + } else { + ERR_TRACE(1, ("os2ShmUnmap: link not found ! %s\n", + pNode->shmBaseName)) + } + + pFile->pShmLink = NULL; + sqlite3_free(pLink); + + sqlite3_mutex_leave(pNode->mutex); + + if( nRef == 0 ) + os2PurgeShmNodes( deleteFlag ); + } + + return SQLITE_OK; +} + +/* +** Change the lock state for a shared-memory segment. +** +** Note that the relationship between SHAREd and EXCLUSIVE locks is a little +** different here than in posix. In xShmLock(), one can go from unlocked +** to shared and back or from unlocked to exclusive and back. But one may +** not go from shared to exclusive or from exclusive to shared. +*/ +static int os2ShmLock( + sqlite3_file *id, /* Database file holding the shared memory */ + int ofst, /* First lock to acquire or release */ + int n, /* Number of locks to acquire or release */ + int flags /* What to do with the lock */ +){ + u32 mask; /* Mask of locks to take or release */ + int rc = SQLITE_OK; /* Result code */ + os2File *pFile = (os2File*)id; + os2ShmLink *p = pFile->pShmLink; /* The shared memory being locked */ + os2ShmLink *pX; /* For looping over all siblings */ + os2ShmNode *pShmNode = p->pShmNode; /* Our node */ + + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); + assert( n>=1 ); + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + + mask = (u32)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex); + + if( flags & SQLITE_SHM_UNLOCK ){ + u32 allMask = 0; /* Mask of locks held by siblings */ + + /* See if any siblings hold this same lock */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); + allMask |= pX->sharedMask; + } + + /* Unlock the system-level locks */ + if( (mask & allMask)==0 ){ + rc = os2ShmSystemLock(pShmNode, _SHM_UNLCK, ofst+OS2_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } + + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~mask; + p->sharedMask &= ~mask; + } + }else if( flags & SQLITE_SHM_SHARED ){ + u32 allShared = 0; /* Union of locks held by connections other than "p" */ + + /* Find out which shared locks are already held by sibling connections. + ** If any sibling already holds an exclusive lock, go ahead and return + ** SQLITE_BUSY. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + allShared |= pX->sharedMask; + } + + /* Get shared locks at the system level, if necessary */ + if( rc==SQLITE_OK ){ + if( (allShared & mask)==0 ){ + rc = os2ShmSystemLock(pShmNode, _SHM_RDLCK, ofst+OS2_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } + } + + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= mask; + } + }else{ + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + } + + /* Get the exclusive locks at the system level. Then if successful + ** also mark the local connection as being locked. + */ + if( rc==SQLITE_OK ){ + rc = os2ShmSystemLock(pShmNode, _SHM_WRLCK, ofst+OS2_SHM_BASE, n); + if( rc==SQLITE_OK ){ + assert( (p->sharedMask & mask)==0 ); + p->exclMask |= mask; + } + } + } + + sqlite3_mutex_leave(pShmNode->mutex); + + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", + p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask, + rc ? "failed" : "ok")); + + ERR_TRACE(rc, ("os2ShmLock: ofst = %d, n = %d, flags = 0x%x -> %d \n", + ofst, n, flags, rc)) + + return rc; +} + +/* +** Implement a memory barrier or memory fence on shared memory. +** +** All loads and stores begun before the barrier must complete before +** any load or store begun after the barrier. +*/ +static void os2ShmBarrier( + sqlite3_file *id /* Database file holding the shared memory */ +){ + UNUSED_PARAMETER(id); + os2ShmEnterMutex(); + os2ShmLeaveMutex(); +} + +#else +# define os2ShmMap 0 +# define os2ShmLock 0 +# define os2ShmBarrier 0 +# define os2ShmUnmap 0 +#endif /* #ifndef SQLITE_OMIT_WAL */ + + /* ** This vector defines all the methods that can operate on an ** sqlite3_file for os2. */ static const sqlite3_io_methods os2IoMethod = { - 1, /* iVersion */ + 2, /* iVersion */ os2Close, /* xClose */ os2Read, /* xRead */ os2Write, /* xWrite */ @@ -659,12 +1355,13 @@ static const sqlite3_io_methods os2IoMethod = { os2FileControl, /* xFileControl */ os2SectorSize, /* xSectorSize */ os2DeviceCharacteristics, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0 /* xShmUnmap */ + os2ShmMap, /* xShmMap */ + os2ShmLock, /* xShmLock */ + os2ShmBarrier, /* xShmBarrier */ + os2ShmUnmap /* xShmUnmap */ }; + /*************************************************************************** ** Here ends the I/O methods that form the sqlite3_io_methods object. ** @@ -676,50 +1373,57 @@ static const sqlite3_io_methods os2IoMethod = { ** hold at pVfs->mxPathname characters. */ static int getTempname(int nBuf, char *zBuf ){ - static const unsigned char zChars[] = + static const char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; - char zTempPathBuf[3]; - PSZ zTempPath = (PSZ)&zTempPathBuf; - if( sqlite3_temp_directory ){ - zTempPath = sqlite3_temp_directory; - }else{ - if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){ - if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){ - if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){ - ULONG ulDriveNum = 0, ulDriveMap = 0; - DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); - sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) ); - } - } - } + PSZ zTempPathCp; + char zTempPath[CCHMAXPATH]; + ULONG ulDriveNum, ulDriveMap; + + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. + */ + SimulateIOError( return SQLITE_IOERR ); + + if( sqlite3_temp_directory ) { + sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", sqlite3_temp_directory); + } else if( DosScanEnv( (PSZ)"TEMP", &zTempPathCp ) == NO_ERROR || + DosScanEnv( (PSZ)"TMP", &zTempPathCp ) == NO_ERROR || + DosScanEnv( (PSZ)"TMPDIR", &zTempPathCp ) == NO_ERROR ) { + char *zTempPathUTF = convertCpPathToUtf8( (char *)zTempPathCp ); + sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", zTempPathUTF); + free( zTempPathUTF ); + } else if( DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ) == NO_ERROR ) { + zTempPath[0] = (char)('A' + ulDriveNum - 1); + zTempPath[1] = ':'; + zTempPath[2] = '\0'; + } else { + zTempPath[0] = '\0'; } + /* Strip off a trailing slashes or backslashes, otherwise we would get * * multiple (back)slashes which causes DosOpen() to fail. * * Trailing spaces are not allowed, either. */ j = sqlite3Strlen30(zTempPath); - while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' - || zTempPath[j-1] == ' ' ) ){ + while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' || + zTempPath[j-1] == ' ' ) ){ j--; } zTempPath[j] = '\0'; - if( !sqlite3_temp_directory ){ - char *zTempPathUTF = convertCpPathToUtf8( zTempPath ); - sqlite3_snprintf( nBuf-30, zBuf, - "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPathUTF ); - free( zTempPathUTF ); - }else{ - sqlite3_snprintf( nBuf-30, zBuf, - "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath ); - } - j = sqlite3Strlen30( zBuf ); + + /* We use 20 bytes to randomize the name */ + sqlite3_snprintf(nBuf-22, zBuf, + "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); + j = sqlite3Strlen30(zBuf); sqlite3_randomness( 20, &zBuf[j] ); for( i = 0; i < 20; i++, j++ ){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; + OSTRACE(( "TEMP FILENAME: %s\n", zBuf )); return SQLITE_OK; } @@ -739,8 +1443,8 @@ static int os2FullPathname( char *zRelativeCp = convertUtf8PathToCp( zRelative ); char zFullCp[CCHMAXPATH] = "\0"; char *zFullUTF; - APIRET rc = DosQueryPathInfo( zRelativeCp, FIL_QUERYFULLNAME, zFullCp, - CCHMAXPATH ); + APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME, + zFullCp, CCHMAXPATH ); free( zRelativeCp ); zFullUTF = convertCpPathToUtf8( zFullCp ); sqlite3_snprintf( nFull, zFull, zFullUTF ); @@ -770,10 +1474,10 @@ static int os2Open( char zTmpname[CCHMAXPATH]; int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); - int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); int isReadWrite = (flags & SQLITE_OPEN_READWRITE); #ifndef NDEBUG + int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isReadonly = (flags & SQLITE_OPEN_READONLY); int eType = (flags & 0xFFFFFF00); int isOpenJournal = (isCreate && ( @@ -813,7 +1517,7 @@ static int os2Open( ); memset( pFile, 0, sizeof(*pFile) ); - pFile->pMethod = &os2IoMethod; + pFile->h = (HFILE)-1; /* If the second argument to this function is NULL, generate a ** temporary file name to use @@ -835,11 +1539,11 @@ static int os2Open( /* Open in random access mode for possibly better speed. Allow full ** sharing because file locks will provide exclusive access when needed. + ** The handle should not be inherited by child processes and we don't + ** want popups from the critical error handler. */ - ulOpenMode |= OPEN_FLAGS_RANDOM; - ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; - ulOpenMode |= OPEN_FLAGS_NOINHERIT; - ulOpenMode |= OPEN_SHARE_DENYNONE; + ulOpenMode |= OPEN_FLAGS_RANDOM | OPEN_SHARE_DENYNONE | + OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR; /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is ** created. SQLite doesn't use it to indicate "exclusive access" @@ -857,13 +1561,6 @@ static int os2Open( ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; } - /* For DELETEONCLOSE, save a pointer to the converted filename */ - if( isDelete ){ - char pathUtf8[CCHMAXPATH]; - os2FullPathname( pVfs, zUtf8Name, CCHMAXPATH, pathUtf8 ); - pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); - } - zNameCp = convertUtf8PathToCp( zUtf8Name ); rc = DosOpen( (PSZ)zNameCp, &h, @@ -878,9 +1575,6 @@ static int os2Open( if( rc != NO_ERROR ){ OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n", rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode )); - if( pFile->pathToDel ) - free( pFile->pathToDel ); - pFile->pathToDel = NULL; if( isReadWrite ){ return os2Open( pVfs, zName, id, @@ -895,7 +1589,12 @@ static int os2Open( *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; } + os2FullPathname( pVfs, zUtf8Name, sizeof( zTmpname ), zTmpname ); + pFile->zFullPathCp = convertUtf8PathToCp( zTmpname ); + pFile->pMethod = &os2IoMethod; + pFile->flags = flags; pFile->h = h; + OpenCounter(+1); OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); return SQLITE_OK; @@ -930,30 +1629,42 @@ static int os2Access( int flags, /* Type of test to make on this file */ int *pOut /* Write results here */ ){ + APIRET rc; FILESTATUS3 fsts3ConfigInfo; - APIRET rc = NO_ERROR; - char *zFilenameCp = convertUtf8PathToCp( zFilename ); + char *zFilenameCp; - memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) ); + UNUSED_PARAMETER(pVfs); + SimulateIOError( return SQLITE_IOERR_ACCESS; ); + + zFilenameCp = convertUtf8PathToCp( zFilename ); rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD, &fsts3ConfigInfo, sizeof(FILESTATUS3) ); free( zFilenameCp ); OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n", fsts3ConfigInfo.attrFile, flags, rc )); + switch( flags ){ - case SQLITE_ACCESS_READ: case SQLITE_ACCESS_EXISTS: - rc = (rc == NO_ERROR); - OSTRACE(( "ACCESS %s access of read and exists rc=%d\n", zFilename, rc)); + /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file + ** as if it does not exist. + */ + if( fsts3ConfigInfo.cbFile == 0 ) + rc = ERROR_FILE_NOT_FOUND; + break; + case SQLITE_ACCESS_READ: break; case SQLITE_ACCESS_READWRITE: - rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 ); - OSTRACE(( "ACCESS %s access of read/write rc=%d\n", zFilename, rc )); + if( fsts3ConfigInfo.attrFile & FILE_READONLY ) + rc = ERROR_ACCESS_DENIED; break; default: + rc = ERROR_FILE_NOT_FOUND; assert( !"Invalid flags argument" ); } - *pOut = rc; + + *pOut = (rc == NO_ERROR); + OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut )); + return SQLITE_OK; } @@ -968,11 +1679,10 @@ static int os2Access( ** within the shared library, and closing the shared library. */ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ - UCHAR loadErr[256]; HMODULE hmod; APIRET rc; char *zFilenameCp = convertUtf8PathToCp(zFilename); - rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilenameCp, &hmod); + rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod); free(zFilenameCp); return rc != NO_ERROR ? 0 : (void*)hmod; } @@ -986,14 +1696,14 @@ static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ PFN pfn; APIRET rc; - rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn); + rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)zSymbol, &pfn); if( rc != NO_ERROR ){ /* if the symbol itself was not found, search again for the same * symbol with an extra underscore, that might be needed depending * on the calling convention */ char _zSymbol[256] = "_"; - strncat(_zSymbol, zSymbol, 255); - rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn); + strncat(_zSymbol, zSymbol, 254); + rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn); } return rc != NO_ERROR ? 0 : (void(*)(void))pfn; } @@ -1017,54 +1727,39 @@ static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){ n = nBuf; memset(zBuf, 0, nBuf); #else - int sizeofULong = sizeof(ULONG); - if( (int)sizeof(DATETIME) <= nBuf - n ){ - DATETIME x; - DosGetDateTime(&x); - memcpy(&zBuf[n], &x, sizeof(x)); - n += sizeof(x); + int i; + PPIB ppib; + PTIB ptib; + DATETIME dt; + static unsigned c = 0; + /* Ordered by variation probability */ + static ULONG svIdx[6] = { QSV_MS_COUNT, QSV_TIME_LOW, + QSV_MAXPRMEM, QSV_MAXSHMEM, + QSV_TOTAVAILMEM, QSV_TOTRESMEM }; + + /* 8 bytes; timezone and weekday don't increase the randomness much */ + if( (int)sizeof(dt)-3 <= nBuf - n ){ + c += 0x0100; + DosGetDateTime(&dt); + dt.year = (USHORT)((dt.year - 1900) | c); + memcpy(&zBuf[n], &dt, sizeof(dt)-3); + n += sizeof(dt)-3; } - if( sizeofULong <= nBuf - n ){ - PPIB ppib; - DosGetInfoBlocks(NULL, &ppib); - memcpy(&zBuf[n], &ppib->pib_ulpid, sizeofULong); - n += sizeofULong; + /* 4 bytes; PIDs and TIDs are 16 bit internally, so combine them */ + if( (int)sizeof(ULONG) <= nBuf - n ){ + DosGetInfoBlocks(&ptib, &ppib); + *(PULONG)&zBuf[n] = MAKELONG(ppib->pib_ulpid, + ptib->tib_ptib2->tib2_ultid); + n += sizeof(ULONG); } - if( sizeofULong <= nBuf - n ){ - PTIB ptib; - DosGetInfoBlocks(&ptib, NULL); - memcpy(&zBuf[n], &ptib->tib_ptib2->tib2_ultid, sizeofULong); - n += sizeofULong; - } - - /* if we still haven't filled the buffer yet the following will */ - /* grab everything once instead of making several calls for a single item */ - if( sizeofULong <= nBuf - n ){ - ULONG ulSysInfo[QSV_MAX]; - DosQuerySysInfo(1L, QSV_MAX, ulSysInfo, sizeofULong * QSV_MAX); - - memcpy(&zBuf[n], &ulSysInfo[QSV_MS_COUNT - 1], sizeofULong); - n += sizeofULong; - - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIMER_INTERVAL - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_LOW - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_HIGH - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TOTAVAILMEM - 1], sizeofULong); - n += sizeofULong; - } - } + /* Up to 6 * 4 bytes; variables depend on the system state */ + for( i = 0; i < 6 && (int)sizeof(ULONG) <= nBuf - n; i++ ){ + DosQuerySysInfo(svIdx[i], svIdx[i], + (PULONG)&zBuf[n], sizeof(ULONG)); + n += sizeof(ULONG); + } #endif return n; @@ -1091,48 +1786,6 @@ static int os2Sleep( sqlite3_vfs *pVfs, int microsec ){ int sqlite3_current_time = 0; #endif -/* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. -*/ -int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ - double now; - SHORT minute; /* needs to be able to cope with negative timezone offset */ - USHORT hundredths, second, hour, - day, month, year; - DATETIME dt; - DosGetDateTime( &dt ); - hundredths = (USHORT)dt.hundredths; - second = (USHORT)dt.seconds; - minute = (SHORT)dt.minutes + dt.timezone; - hour = (USHORT)dt.hours; - day = (USHORT)dt.day; - month = (USHORT)dt.month; - year = (USHORT)dt.year; - - /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html - http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c */ - /* Calculate the Julian days */ - now = day - 32076 + - 1461*(year + 4800 + (month - 14)/12)/4 + - 367*(month - 2 - (month - 14)/12*12)/12 - - 3*((year + 4900 + (month - 14)/12)/100)/4; - - /* Add the fractional hours, mins and seconds */ - now += (hour + 12.0)/24.0; - now += minute/1440.0; - now += second/86400.0; - now += hundredths/8640000.0; - *prNow = now; -#ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *prNow = sqlite3_current_time/86400.0 + 2440587.5; - } -#endif - return 0; -} - /* ** Find the current time (in Universal Coordinated Time). Write into *piNow ** the current time and date as a Julian Day number times 86_400_000. In @@ -1143,13 +1796,89 @@ int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ ** On success, return 0. Return 1 if the time and date cannot be found. */ static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ - double now; - os2CurrentTime(pVfs, &now); - *piNow = now * 86400000; +#ifdef SQLITE_TEST + static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; +#endif + int year, month, datepart, timepart; + + DATETIME dt; + DosGetDateTime( &dt ); + + year = dt.year; + month = dt.month; + + /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html + ** http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c + ** Calculate the Julian days + */ + datepart = (int)dt.day - 32076 + + 1461*(year + 4800 + (month - 14)/12)/4 + + 367*(month - 2 - (month - 14)/12*12)/12 - + 3*((year + 4900 + (month - 14)/12)/100)/4; + + /* Time in milliseconds, hours to noon added */ + timepart = 12*3600*1000 + dt.hundredths*10 + dt.seconds*1000 + + ((int)dt.minutes + dt.timezone)*60*1000 + dt.hours*3600*1000; + + *piNow = (sqlite3_int64)datepart*86400*1000 + timepart; + +#ifdef SQLITE_TEST + if( sqlite3_current_time ){ + *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; + } +#endif + + UNUSED_PARAMETER(pVfs); return 0; } +/* +** Find the current time (in Universal Coordinated Time). Write the +** current time and date as a Julian Day number into *prNow and +** return 0. Return 1 if the time and date cannot be found. +*/ +static int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ + int rc; + sqlite3_int64 i; + rc = os2CurrentTimeInt64(pVfs, &i); + if( !rc ){ + *prNow = i/86400000.0; + } + return rc; +} + +/* +** The idea is that this function works like a combination of +** GetLastError() and FormatMessage() on windows (or errno and +** strerror_r() on unix). After an error is returned by an OS +** function, SQLite calls this function with zBuf pointing to +** a buffer of nBuf bytes. The OS layer should populate the +** buffer with a nul-terminated UTF-8 encoded error message +** describing the last IO error to have occurred within the calling +** thread. +** +** If the error message is too large for the supplied buffer, +** it should be truncated. The return value of xGetLastError +** is zero if the error message fits in the buffer, or non-zero +** otherwise (if the message was truncated). If non-zero is returned, +** then it is not necessary to include the nul-terminator character +** in the output buffer. +** +** Not supplying an error message will have no adverse effect +** on SQLite. It is fine to have an implementation that never +** returns an error message: +** +** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ +** assert(zBuf[0]=='\0'); +** return 0; +** } +** +** However if an error message is supplied, it will be incorporated +** by sqlite into the error message available to the user using +** sqlite3_errmsg(), possibly making IO errors easier to debug. +*/ static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ + assert(zBuf[0]=='\0'); return 0; } @@ -1177,13 +1906,14 @@ int sqlite3_os_init(void){ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ os2GetLastError, /* xGetLastError */ - os2CurrentTimeInt64 /* xCurrentTimeInt64 */ + os2CurrentTimeInt64, /* xCurrentTimeInt64 */ 0, /* xSetSystemCall */ 0, /* xGetSystemCall */ - 0, /* xNextSystemCall */ + 0 /* xNextSystemCall */ }; sqlite3_vfs_register(&os2Vfs, 1); initUconvObjects(); +/* sqlite3OSTrace = 1; */ return SQLITE_OK; } int sqlite3_os_end(void){ diff --git a/src/os_unix.c b/src/os_unix.c index e3fa9ea465..2d3a616373 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -293,7 +293,7 @@ static struct unix_syscall { sqlite3_syscall_ptr pDefault; /* Default value */ } aSyscall[] = { { "open", (sqlite3_syscall_ptr)open, 0 }, -#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) +#define osOpen ((int(*)(const char*,int,...))aSyscall[0].pCurrent) { "close", (sqlite3_syscall_ptr)close, 0 }, #define osClose ((int(*)(int))aSyscall[1].pCurrent) @@ -330,7 +330,7 @@ static struct unix_syscall { { "read", (sqlite3_syscall_ptr)read, 0 }, #define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) -#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE { "pread", (sqlite3_syscall_ptr)pread, 0 }, #else { "pread", (sqlite3_syscall_ptr)0, 0 }, @@ -347,7 +347,7 @@ static struct unix_syscall { { "write", (sqlite3_syscall_ptr)write, 0 }, #define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) -#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, #else { "pwrite", (sqlite3_syscall_ptr)0, 0 }, @@ -363,7 +363,11 @@ static struct unix_syscall { #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ aSyscall[13].pCurrent) +#if SQLITE_ENABLE_LOCKING_STYLE { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, +#else + { "fchmod", (sqlite3_syscall_ptr)0, 0 }, +#endif #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE @@ -929,7 +933,7 @@ struct unixInodeInfo { UnixUnusedFd *pUnused; /* Unused file descriptors to close */ unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ unixInodeInfo *pPrev; /* .... doubly linked */ -#if defined(SQLITE_ENABLE_LOCKING_STYLE) +#if SQLITE_ENABLE_LOCKING_STYLE unsigned long long sharedByte; /* for AFP simulated shared lock */ #endif #if OS_VXWORKS @@ -3086,7 +3090,7 @@ static int unixWrite( SimulateDiskfullError(( wrote=0, amt=1 )); if( amt>0 ){ - if( wrote<0 ){ + if( wrote<0 && pFile->lastErrno!=ENOSPC ){ /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ @@ -3911,7 +3915,7 @@ static int unixShmMap( MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion ); if( pMem==MAP_FAILED ){ - rc = SQLITE_IOERR; + rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); goto shmpage_out; } }else{ @@ -6682,6 +6686,10 @@ int sqlite3_os_init(void){ }; unsigned int i; /* Loop counter */ + /* Double-check that the aSyscall[] array has been constructed + ** correctly. See ticket [bb3a86e890c8e96ab] */ + assert( ArraySize(aSyscall)==16 ); + /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); diff --git a/src/os_win.c b/src/os_win.c index c8768339cd..654a964468 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -118,6 +118,7 @@ struct winFile { #endif }; + /* ** Forward prototypes. */ @@ -298,6 +299,109 @@ static char *utf8ToMbcs(const char *zFilename){ return zFilenameMbcs; } + +/* +** 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){ + /* FormatMessage returns 0 on failure. Otherwise it + ** returns the number of TCHARs written to the output + ** buffer, excluding the terminating null char. + */ + 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); + } + return 0; +} + +/* +** +** This function - winLogErrorAtLine() - is only ever called via the macro +** winLogError(). +** +** This routine is invoked after an error occurs in an OS function. +** It logs a message using sqlite3_log() containing the current value of +** error code and, if possible, the human-readable equivalent from +** FormatMessage. +** +** The first argument passed to the macro should be the error code that +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). +** The two subsequent arguments should be the name of the OS function that +** failed and the the associated file-system path, if any. +*/ +#define winLogError(a,b,c) winLogErrorAtLine(a,b,c,__LINE__) +static int winLogErrorAtLine( + int errcode, /* SQLite error code */ + const char *zFunc, /* Name of OS function that failed */ + const char *zPath, /* File path associated with error */ + int iLine /* Source line number where error occurred */ +){ + char zMsg[500]; /* Human readable error text */ + int i; /* Loop counter */ + DWORD iErrno = GetLastError(); /* Error code */ + + zMsg[0] = 0; + getLastErrorMsg(sizeof(zMsg), zMsg); + assert( errcode!=SQLITE_OK ); + if( zPath==0 ) zPath = ""; + for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} + zMsg[i] = 0; + sqlite3_log(errcode, + "os_win.c:%d: (%d) %s(%s) - %s", + iLine, iErrno, zFunc, zPath, zMsg + ); + + return errcode; +} + #if SQLITE_OS_WINCE /************************************************************************* ** This section contains code for WinCE only. @@ -375,6 +479,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ pFile->hMutex = CreateMutexW(NULL, FALSE, zName); if (!pFile->hMutex){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename); free(zName); return FALSE; } @@ -406,6 +511,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ /* If mapping failed, close the shared memory handle and erase it */ if (!pFile->shared){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename); CloseHandle(pFile->hShared); pFile->hShared = NULL; } @@ -651,6 +757,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath); return 1; } @@ -696,7 +803,8 @@ static int winClose(sqlite3_file *id){ #endif OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); OpenCounter(-1); - return rc ? SQLITE_OK : SQLITE_IOERR; + return rc ? SQLITE_OK + : winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath); } /* @@ -722,7 +830,7 @@ static int winRead( } if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ pFile->lastErrno = GetLastError(); - return SQLITE_IOERR_READ; + return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath); } if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ @@ -773,7 +881,7 @@ static int winWrite( if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ return SQLITE_FULL; } - return SQLITE_IOERR_WRITE; + return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath); } return SQLITE_OK; } @@ -801,10 +909,10 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ if( seekWinFile(pFile, nByte) ){ - rc = SQLITE_IOERR_TRUNCATE; + rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath); }else if( 0==SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError(); - rc = SQLITE_IOERR_TRUNCATE; + rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath); } OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); @@ -826,6 +934,7 @@ int sqlite3_fullsync_count = 0; static int winSync(sqlite3_file *id, int flags){ #if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || defined(SQLITE_DEBUG) winFile *pFile = (winFile*)id; + BOOL rc; #else UNUSED_PARAMETER(id); #endif @@ -838,20 +947,19 @@ static int winSync(sqlite3_file *id, int flags){ OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype)); -#ifndef SQLITE_TEST - UNUSED_PARAMETER(flags); -#else - if( flags & SQLITE_SYNC_FULL ){ - sqlite3_fullsync_count++; - } - sqlite3_sync_count++; -#endif - /* Unix cannot, but some systems may return SQLITE_FULL from here. This ** line is to test that doing so does not cause any problems. */ SimulateDiskfullError( return SQLITE_FULL ); - SimulateIOError( return SQLITE_IOERR; ); + +#ifndef SQLITE_TEST + UNUSED_PARAMETER(flags); +#else + if( (flags&0x0F)==SQLITE_SYNC_FULL ){ + sqlite3_fullsync_count++; + } + sqlite3_sync_count++; +#endif /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a ** no-op @@ -859,11 +967,13 @@ static int winSync(sqlite3_file *id, int flags){ #ifdef SQLITE_NO_SYNC return SQLITE_OK; #else - if( FlushFileBuffers(pFile->h) ){ + rc = FlushFileBuffers(pFile->h); + SimulateIOError( rc=FALSE ); + if( rc ){ return SQLITE_OK; }else{ pFile->lastErrno = GetLastError(); - return SQLITE_IOERR; + return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath); } #endif } @@ -884,7 +994,7 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ && ((error = GetLastError()) != NO_ERROR) ) { pFile->lastErrno = error; - return SQLITE_IOERR_FSTAT; + return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath); } *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits; return SQLITE_OK; @@ -923,6 +1033,7 @@ static int getReadLock(winFile *pFile){ } if( res == 0 ){ pFile->lastErrno = GetLastError(); + /* No need to log a failure to lock */ } return res; } @@ -943,6 +1054,7 @@ static int unlockReadLock(winFile *pFile){ } if( res == 0 ){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath); } return res; } @@ -1143,7 +1255,7 @@ static int winUnlock(sqlite3_file *id, int locktype){ if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ /* This should never happen. We should always be able to ** reacquire the read lock */ - rc = SQLITE_IOERR_UNLOCK; + rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath); } } if( type>=RESERVED_LOCK ){ @@ -1500,7 +1612,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); if( rc!=SQLITE_OK ){ - rc = SQLITE_IOERR_SHMOPEN; + rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath); } } if( rc==SQLITE_OK ){ @@ -1759,7 +1871,7 @@ static int winShmMap( */ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); if( rc!=SQLITE_OK ){ - rc = SQLITE_IOERR_SHMSIZE; + rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath); goto shmpage_out; } @@ -1773,7 +1885,7 @@ static int winShmMap( if( !isWrite ) goto shmpage_out; rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); if( rc!=SQLITE_OK ){ - rc = SQLITE_IOERR_SHMSIZE; + rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath); goto shmpage_out; } } @@ -1810,7 +1922,7 @@ static int winShmMap( } if( !pMap ){ pShmNode->lastErrno = GetLastError(); - rc = SQLITE_IOERR; + rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath); if( hMap ) CloseHandle(hMap); goto shmpage_out; } @@ -1972,68 +2084,6 @@ static int getTempname(int nBuf, char *zBuf){ return SQLITE_OK; } -/* -** 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){ - /* FormatMessage returns 0 on failure. Otherwise it - ** returns the number of TCHARs written to the output - ** buffer, excluding the terminating null char. - */ - 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); - } - return 0; -} - /* ** Open a file. */ @@ -2205,6 +2255,7 @@ static int winOpen( if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name); free(zConverted); if( isReadWrite ){ return winOpen(pVfs, zName, id, @@ -2308,7 +2359,8 @@ static int winDelete( "ok" : "failed" )); return ( (rc == INVALID_FILE_ATTRIBUTES) - && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE; + && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : + winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename); } /* @@ -2348,6 +2400,7 @@ static int winAccess( } }else{ if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ + winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename); free(zConverted); return SQLITE_IOERR_ACCESS; }else{ diff --git a/src/select.c b/src/select.c index 3a4a881684..8dcfb84b8a 100644 --- a/src/select.c +++ b/src/select.c @@ -4239,11 +4239,13 @@ int sqlite3Select( ** and pKeyInfo to the KeyInfo structure required to navigate the ** index. ** + ** (2011-04-15) Do not do a full scan of an unordered index. + ** ** In practice the KeyInfo structure will not be used. It is only ** passed to keep OP_OpenRead happy. */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( !pBest || pIdx->nColumnnColumn ){ + if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumnnColumn) ){ pBest = pIdx; } } diff --git a/src/shell.c b/src/shell.c index 4f34ccceb6..aab70b29db 100644 --- a/src/shell.c +++ b/src/shell.c @@ -71,6 +71,9 @@ extern int isatty(); #define isatty(x) 1 #endif +/* True if the timer is enabled */ +static int enableTimer = 0; + #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL) #include #include @@ -78,9 +81,6 @@ extern int isatty(); /* Saved resource information for the beginning of an operation */ static struct rusage sBegin; -/* True if the timer is enabled */ -static int enableTimer = 0; - /* ** Begin timing an operation */ @@ -124,9 +124,6 @@ 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). diff --git a/src/sqlite.h.in b/src/sqlite.h.in index a1743ba2dc..2ba183cdf9 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -447,6 +447,8 @@ int sqlite3_exec( #define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) #define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) #define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) diff --git a/src/util.c b/src/util.c index 5cc2b61308..1c9b401f89 100644 --- a/src/util.c +++ b/src/util.c @@ -26,8 +26,8 @@ */ #ifdef SQLITE_COVERAGE_TEST void sqlite3Coverage(int x){ - static int dummy = 0; - dummy += x; + static unsigned dummy = 0; + dummy += (unsigned)x; } #endif diff --git a/test/exists.test b/test/exists.test index db87afc788..c7c14a2402 100644 --- a/test/exists.test +++ b/test/exists.test @@ -17,175 +17,181 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl -set testprefix exists -# This block of tests is targeted at CREATE XXX IF NOT EXISTS statements. -# -do_multiclient_test tn { +foreach jm {rollback wal} { - # TABLE objects. + set testprefix exists-$jm + + # This block of tests is targeted at CREATE XXX IF NOT EXISTS statements. # - do_test 1.$tn.1.1 { - sql2 { CREATE TABLE t1(x) } - sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } - sql2 { DROP TABLE t1 } - sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } - sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } - } {t1} + do_multiclient_test tn { - do_test 1.$tn.1.2 { - sql2 { CREATE TABLE t2(x) } - sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } - sql2 { DROP TABLE t2 } - sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } - sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } - } {t1 t2} + # TABLE objects. + # + do_test 1.$tn.1.1 { + if {$jm == "wal"} { sql2 { PRAGMA journal_mode = WAL } } + sql2 { CREATE TABLE t1(x) } + sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } + sql2 { DROP TABLE t1 } + sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {t1} + + do_test 1.$tn.1.2 { + sql2 { CREATE TABLE t2(x) } + sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } + sql2 { DROP TABLE t2 } + sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {t1 t2} - # INDEX objects. + # INDEX objects. + # + do_test 1.$tn.2 { + sql2 { CREATE INDEX i1 ON t1(a) } + sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } + sql2 { DROP INDEX i1 } + sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } + sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } + } {i1} + + # VIEW objects. + # + do_test 1.$tn.3 { + sql2 { CREATE VIEW v1 AS SELECT * FROM t1 } + sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } + sql2 { DROP VIEW v1 } + sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } + } {v1} + + # TRIGGER objects. + # + do_test $tn.4 { + sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql2 { DROP TRIGGER tr1 } + sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } + } {tr1} + } + + # This block of tests is targeted at DROP XXX IF EXISTS statements. # - do_test 1.$tn.2 { - sql2 { CREATE INDEX i1 ON t1(a) } - sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } - sql2 { DROP INDEX i1 } - sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } - sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } - } {i1} + do_multiclient_test tn { - # VIEW objects. + # TABLE objects. + # + do_test 2.$tn.1 { + if {$jm == "wal"} { sql1 { PRAGMA journal_mode = WAL } } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { CREATE TABLE t1(x) } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {} + + # INDEX objects. + # + do_test 2.$tn.2 { + sql1 { CREATE TABLE t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { CREATE INDEX i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } + } {} + + # VIEW objects. + # + do_test 2.$tn.3 { + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { CREATE VIEW v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } + } {} + + # TRIGGER objects. + # + do_test 2.$tn.4 { + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } + } {} + } + + # This block of tests is targeted at DROP XXX IF EXISTS statements with + # attached databases. # - do_test 1.$tn.3 { - sql2 { CREATE VIEW v1 AS SELECT * FROM t1 } - sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } - sql2 { DROP VIEW v1 } - sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } - sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } - } {v1} + do_multiclient_test tn { - # TRIGGER objects. - # - do_test $tn.4 { - sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } -sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } - sql2 { DROP TRIGGER tr1 } -sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } - sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } - } {tr1} -} + forcedelete test.db2 + do_test 3.$tn.0 { + sql1 { ATTACH 'test.db2' AS aux } + sql2 { ATTACH 'test.db2' AS aux } + } {} -# This block of tests is targeted at DROP XXX IF EXISTS statements. -# -do_multiclient_test tn { + # TABLE objects. + # + do_test 3.$tn.1.1 { + sql1 { DROP TABLE IF EXISTS aux.t1 } + sql2 { CREATE TABLE aux.t1(x) } + sql1 { DROP TABLE IF EXISTS aux.t1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } + } {} + do_test 3.$tn.1.2 { + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { CREATE TABLE aux.t1(x) } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } + } {} - # TABLE objects. - # - do_test 2.$tn.1 { - sql1 { DROP TABLE IF EXISTS t1 } - sql2 { CREATE TABLE t1(x) } - sql1 { DROP TABLE IF EXISTS t1 } - sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } - } {} + # INDEX objects. + # + do_test 3.$tn.2.1 { + sql1 { CREATE TABLE aux.t2(x) } + sql1 { DROP INDEX IF EXISTS aux.i2 } + sql2 { CREATE INDEX aux.i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS aux.i2 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } + } {} + do_test 3.$tn.2.2 { + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { CREATE INDEX aux.i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } + } {} - # INDEX objects. - # - do_test 2.$tn.2 { - sql1 { CREATE TABLE t2(x) } - sql1 { DROP INDEX IF EXISTS i2 } - sql2 { CREATE INDEX i2 ON t2(x) } - sql1 { DROP INDEX IF EXISTS i2 } - sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } - } {} + # VIEW objects. + # + do_test 3.$tn.3.1 { + sql1 { DROP VIEW IF EXISTS aux.v1 } + sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS aux.v1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } + } {} + do_test 3.$tn.3.2 { + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } + } {} - # VIEW objects. - # - do_test 2.$tn.3 { - sql1 { DROP VIEW IF EXISTS v1 } - sql2 { CREATE VIEW v1 AS SELECT * FROM t2 } - sql1 { DROP VIEW IF EXISTS v1 } - sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } - } {} - - # TRIGGER objects. - # - do_test 2.$tn.4 { - sql1 { DROP TRIGGER IF EXISTS tr1 } - sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } - sql1 { DROP TRIGGER IF EXISTS tr1 } - sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } - } {} -} - -# This block of tests is targeted at DROP XXX IF EXISTS statements with -# attached databases. -# -do_multiclient_test tn { - - forcedelete test.db2 - do_test 3.$tn.0 { - sql1 { ATTACH 'test.db2' AS aux } - sql2 { ATTACH 'test.db2' AS aux } - } {} - - # TABLE objects. - # - do_test 3.$tn.1.1 { - sql1 { DROP TABLE IF EXISTS aux.t1 } - sql2 { CREATE TABLE aux.t1(x) } - sql1 { DROP TABLE IF EXISTS aux.t1 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } - } {} - do_test 3.$tn.1.2 { - sql1 { DROP TABLE IF EXISTS t1 } - sql2 { CREATE TABLE aux.t1(x) } - sql1 { DROP TABLE IF EXISTS t1 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } - } {} - - # INDEX objects. - # - do_test 3.$tn.2.1 { - sql1 { CREATE TABLE aux.t2(x) } - sql1 { DROP INDEX IF EXISTS aux.i2 } - sql2 { CREATE INDEX aux.i2 ON t2(x) } - sql1 { DROP INDEX IF EXISTS aux.i2 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } - } {} - do_test 3.$tn.2.2 { - sql1 { DROP INDEX IF EXISTS i2 } - sql2 { CREATE INDEX aux.i2 ON t2(x) } - sql1 { DROP INDEX IF EXISTS i2 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } - } {} - - # VIEW objects. - # - do_test 3.$tn.3.1 { - sql1 { DROP VIEW IF EXISTS aux.v1 } - sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } - sql1 { DROP VIEW IF EXISTS aux.v1 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } - } {} - do_test 3.$tn.3.2 { - sql1 { DROP VIEW IF EXISTS v1 } - sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } - sql1 { DROP VIEW IF EXISTS v1 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } - } {} - - # TRIGGER objects. - # - do_test 3.$tn.4.1 { - sql1 { DROP TRIGGER IF EXISTS aux.tr1 } - sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } - sql1 { DROP TRIGGER IF EXISTS aux.tr1 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } - } {} - do_test 3.$tn.4.2 { - sql1 { DROP TRIGGER IF EXISTS tr1 } - sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } - sql1 { DROP TRIGGER IF EXISTS tr1 } - sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } - } {} + # TRIGGER objects. + # + do_test 3.$tn.4.1 { + sql1 { DROP TRIGGER IF EXISTS aux.tr1 } + sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS aux.tr1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } + } {} + do_test 3.$tn.4.2 { + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } + } {} + } } diff --git a/test/quota.test b/test/quota.test index 2cf7628004..d7601e0e52 100644 --- a/test/quota.test +++ b/test/quota.test @@ -202,6 +202,9 @@ do_test quota-3.2.9 { set ::quota [list] proc quota_callback {file limitvar size} { upvar $limitvar limit + if {$::tcl_platform(platform)=="windows"} { + set file [ lindex [string map {\\ \/} $file] 0 ] + } lappend ::quota $file $size set limit 0 } diff --git a/test/sync.test b/test/sync.test index 98aa7731e7..4b02ad72ab 100644 --- a/test/sync.test +++ b/test/sync.test @@ -19,19 +19,27 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl # -# These tests are only applicable on unix when pager pragma are +# These tests are only applicable when pager pragma are # enabled. Also, since every test uses an ATTACHed database, they # are only run when ATTACH is enabled. # -if {$::tcl_platform(platform)!="unix"} { - finish_test - return -} ifcapable !pager_pragmas||!attach { finish_test return } +set sqlite_sync_count 0 +proc cond_incr_sync_count {adj} { + global sqlite_sync_count + if {$::tcl_platform(platform) == "windows"} { + incr sqlite_sync_count $adj + } { + ifcapable !dirsync { + incr sqlite_sync_count $adj + } + } +} + do_test sync-1.1 { set sqlite_sync_count 0 file delete -force test2.db @@ -42,9 +50,7 @@ do_test sync-1.1 { ATTACH DATABASE 'test2.db' AS db2; CREATE TABLE db2.t2(x,y); } - ifcapable !dirsync { - incr sqlite_sync_count 2 - } + cond_incr_sync_count 2 set sqlite_sync_count } 8 ifcapable pager_pragmas { @@ -58,9 +64,7 @@ ifcapable pager_pragmas { INSERT INTO t2 VALUES(3,4); COMMIT; } - ifcapable !dirsync { - incr sqlite_sync_count 3 - } + cond_incr_sync_count 3 set sqlite_sync_count } 8 } @@ -74,9 +78,7 @@ do_test sync-1.3 { INSERT INTO t2 VALUES(5,6); COMMIT; } - ifcapable !dirsync { - incr sqlite_sync_count 3 - } + cond_incr_sync_count 3 set sqlite_sync_count } 10 ifcapable pager_pragmas { diff --git a/test/triggerC.test b/test/triggerC.test index 694d069d71..a54aa62fae 100644 --- a/test/triggerC.test +++ b/test/triggerC.test @@ -246,7 +246,7 @@ do_test triggerC-2.2 { } {100} do_test triggerC-2.3 { - execsql { + execsql " CREATE TABLE t23(x PRIMARY KEY); CREATE TRIGGER t23a AFTER INSERT ON t23 BEGIN @@ -254,15 +254,15 @@ do_test triggerC-2.3 { END; CREATE TRIGGER t23b BEFORE INSERT ON t23 BEGIN - SELECT CASE WHEN new.x>500 + SELECT CASE WHEN new.x>[expr $SQLITE_MAX_TRIGGER_DEPTH / 2] THEN RAISE(IGNORE) ELSE NULL END; END; INSERT INTO t23 VALUES(1); SELECT count(*) FROM t23; - } -} {500} + " +} [list [expr $SQLITE_MAX_TRIGGER_DEPTH / 2]] #----------------------------------------------------------------------- @@ -288,12 +288,12 @@ do_test triggerC-3.1.3 { } {} do_test triggerC-3.2.1 { - execsql { + execsql " CREATE TABLE t3b(x); - CREATE TRIGGER t3bi AFTER INSERT ON t3b WHEN new.x<2000 BEGIN + CREATE TRIGGER t3bi AFTER INSERT ON t3b WHEN new.x<[expr $SQLITE_MAX_TRIGGER_DEPTH * 2] BEGIN INSERT INTO t3b VALUES(new.x+1); END; - } + " catchsql { INSERT INTO t3b VALUES(1); } @@ -303,39 +303,39 @@ do_test triggerC-3.2.2 { } {} do_test triggerC-3.3.1 { - catchsql { - INSERT INTO t3b VALUES(1001); - } + catchsql " + INSERT INTO t3b VALUES([expr $SQLITE_MAX_TRIGGER_DEPTH + 1]); + " } {0 {}} do_test triggerC-3.3.2 { db eval {SELECT count(*), max(x), min(x) FROM t3b} -} {1000 2000 1001} +} [list $SQLITE_MAX_TRIGGER_DEPTH [expr $SQLITE_MAX_TRIGGER_DEPTH * 2] [expr $SQLITE_MAX_TRIGGER_DEPTH + 1]] do_test triggerC-3.4.1 { - catchsql { + catchsql " DELETE FROM t3b; - INSERT INTO t3b VALUES(999); - } + INSERT INTO t3b VALUES([expr $SQLITE_MAX_TRIGGER_DEPTH - 1]); + " } {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); - } + sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH [expr $SQLITE_MAX_TRIGGER_DEPTH / 10] + catchsql " + INSERT INTO t3b VALUES([expr ($SQLITE_MAX_TRIGGER_DEPTH * 2) - ($SQLITE_MAX_TRIGGER_DEPTH / 10) + 1]); + " } {0 {}} do_test triggerC-3.5.2 { db eval {SELECT count(*), max(x), min(x) FROM t3b} -} {100 2000 1901} +} [list [expr $SQLITE_MAX_TRIGGER_DEPTH / 10] [expr $SQLITE_MAX_TRIGGER_DEPTH * 2] [expr ($SQLITE_MAX_TRIGGER_DEPTH * 2) - ($SQLITE_MAX_TRIGGER_DEPTH / 10) + 1]] do_test triggerC-3.5.3 { - catchsql { + catchsql " DELETE FROM t3b; - INSERT INTO t3b VALUES(1900); - } + INSERT INTO t3b VALUES([expr ($SQLITE_MAX_TRIGGER_DEPTH * 2) - ($SQLITE_MAX_TRIGGER_DEPTH / 10)]); + " } {1 {too many levels of trigger recursion}} do_test triggerC-3.5.4 { db eval {SELECT count(*), max(x), min(x) FROM t3b} @@ -343,25 +343,25 @@ do_test triggerC-3.5.4 { do_test triggerC-3.6.1 { sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1 - catchsql { - INSERT INTO t3b VALUES(2000); - } + catchsql " + INSERT INTO t3b VALUES([expr $SQLITE_MAX_TRIGGER_DEPTH * 2]); + " } {0 {}} do_test triggerC-3.6.2 { db eval {SELECT count(*), max(x), min(x) FROM t3b} -} {1 2000 2000} +} [list 1 [expr $SQLITE_MAX_TRIGGER_DEPTH * 2] [expr $SQLITE_MAX_TRIGGER_DEPTH * 2]] do_test triggerC-3.6.3 { - catchsql { + catchsql " DELETE FROM t3b; - INSERT INTO t3b VALUES(1999); - } + INSERT INTO t3b VALUES([expr ($SQLITE_MAX_TRIGGER_DEPTH * 2) - 1]); + " } {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 - +sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH $SQLITE_MAX_TRIGGER_DEPTH + #----------------------------------------------------------------------- # This next block of tests, triggerC-4.*, checks that affinity diff --git a/test/unordered.test b/test/unordered.test new file mode 100644 index 0000000000..a9c6253432 --- /dev/null +++ b/test/unordered.test @@ -0,0 +1,68 @@ +# 2011 April 9 +# +# 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 + +set testprefix unordered + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a); + INSERT INTO t1 VALUES(1, 'xxx'); + INSERT INTO t1 SELECT a+1, b FROM t1; + INSERT INTO t1 SELECT a+2, b FROM t1; + INSERT INTO t1 SELECT a+4, b FROM t1; + INSERT INTO t1 SELECT a+8, b FROM t1; + INSERT INTO t1 SELECT a+16, b FROM t1; + INSERT INTO t1 SELECT a+32, b FROM t1; + INSERT INTO t1 SELECT a+64, b FROM t1; + ANALYZE; +} {} + +foreach idxmode {ordered unordered} { + if {$idxmode == "unordered"} { + execsql { UPDATE sqlite_stat1 SET stat = stat || ' unordered' } + db close + sqlite3 db test.db + } + foreach {tn sql r(ordered) r(unordered)} { + 1 "SELECT * FROM t1 ORDER BY a" + {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} + 2 "SELECT * FROM t1 WHERE a >?" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~32 rows)}} + {0 0 0 {SCAN TABLE t1 (~42 rows)}} + 3 "SELECT * FROM t1 WHERE a = ? ORDER BY rowid" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)} + 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} + 4 "SELECT max(a) FROM t1" + {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 (~1 rows)}} + 5 "SELECT group_concat(b) FROM t1 GROUP BY a" + {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}} + + 6 "SELECT * FROM t1 WHERE a = ?" + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + 7 "SELECT count(*) FROM t1" + {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1(~128 rows)}} + {0 0 0 {SCAN TABLE t1 (~128 rows)}} + } { + do_eqp_test 1.$idxmode.$tn $sql $r($idxmode) + } +} + +finish_test diff --git a/test/wal2.test b/test/wal2.test index 82234330fa..97966cd94e 100644 --- a/test/wal2.test +++ b/test/wal2.test @@ -23,6 +23,18 @@ set testprefix wal2 ifcapable !wal {finish_test ; return } +set sqlite_sync_count 0 +proc cond_incr_sync_count {adj} { + global sqlite_sync_count + if {$::tcl_platform(platform) == "windows"} { + incr sqlite_sync_count $adj + } { + ifcapable !dirsync { + incr sqlite_sync_count $adj + } + } +} + proc set_tvfs_hdr {file args} { # Set $nHdr to the number of bytes in the wal-index header: @@ -1189,6 +1201,7 @@ foreach {tn sql reslist} { } {10 0 5 5 0 2 2} do_test wal2-14.$tn.3 { + cond_incr_sync_count 1 list $sqlite_sync_count $sqlite_fullsync_count } [lrange $reslist 0 1] diff --git a/tool/rollback-test.c b/tool/rollback-test.c new file mode 100644 index 0000000000..915d9d203d --- /dev/null +++ b/tool/rollback-test.c @@ -0,0 +1,155 @@ +/* +** This program is used to generate and verify databases with hot journals. +** Use this program to generate a hot journal on one machine and verify +** that it rolls back correctly on another machine with a different +** architecture. +** +** Usage: +** +** rollback-test new [-utf8] [-utf16le] [-utf16be] [-pagesize=N] DATABASE +** rollback-test check DATABASE +** rollback-test crash [-wal] [-rollback] DATABASE +*/ +#include +#include +#include +#include "sqlite3.h" + +static void usage(char *argv0){ + fprintf(stderr, + "Usage: %s new [-utf8] [-utf16le] [-utf16be] [-pagesize=N] DATABASE\n" + " %s check DATABASE\n" + " %s crash [-wal] DATABASE\n", + argv0, argv0, argv0 + ); + exit(1); +} + +static sqlite3 *openDb(const char *zFilename){ + int rc; + sqlite3 *db; + rc = sqlite3_open(zFilename, &db); + if( rc ){ + fprintf(stderr, "Cannot open \"%s\": %s\n", + zFilename, sqlite3_errmsg(db)); + sqlite3_close(db); + exit(1); + } + return db; +} + +static int nReply = 0; +static char zReply[1000]; + +static int execCallback(void *NotUsed, int nArg, char **azArg, char **azCol){ + int i, n; + char *z; + for(i=0; i0 && nReply=sizeof(zReply)-1 ) n = sizeof(zReply) - nReply - 1; + memcpy(&zReply[nReply], z, n); + nReply += n; + zReply[nReply] = 0; + } + return 0; +} + +static void runSql(sqlite3 *db, const char *zSql){ + char *zErr = 0; + int rc; + nReply = 0; + rc = sqlite3_exec(db, zSql, execCallback, 0, &zErr); + if( zErr ){ + fprintf(stderr, "SQL error: %s\n", zErr); + exit(1); + } + if( rc ){ + fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + exit(1); + } +} + +int main(int argc, char **argv){ + sqlite3 *db; + int i; + + if( argc<3 ) usage(argv[0]); + if( strcmp(argv[1], "new")==0 ){ + db = openDb(argv[argc-1]); + for(i=2; i('abcdefghijklmnopqrstuvwxyz' || x)" + ); + if( strcmp(zReply, "0")!=0 ){ + fprintf(stderr, "Wrong content\n"); + exit(1); + } + printf("Ok\n"); + }else if( strcmp(argv[1], "crash")==0 ){ + db = openDb(argv[argc-1]); + for(i=2; i