Compare commits

...

81 Commits

Author SHA1 Message Date
drh cb15f35f3b Change the name ZERO_DAMAGE to the more descriptive POWERSAFE_OVERWRITE.
The query parameter used to control this device characteristic is now "psow".

FossilOrigin-Name: 6191c5e45175f5c6040e891843b0725a929d6dd7
2011-12-23 01:04:17 +00:00
drh bec7c97899 Merge the latest trunk changes into the statvfs branch.
FossilOrigin-Name: d5e36327c12f264429eb079bddbb71a310f76389
2011-12-23 00:25:02 +00:00
drh 9291372094 Add interfaces sqlite3_uri_boolean() and sqlite3_uri_int64() which are
wrappers around sqlite3_uri_parameter() combined with internal routines for
converting strings to booleans and 64-bit integers.

FossilOrigin-Name: 83d26b9a9115eadac9e59a33d608bca0ab2519e3
2011-12-23 00:07:33 +00:00
drh 64803bd2fa Fix minor issues with FTS and RTREE discovered by coverity.
FossilOrigin-Name: 1c27d842163e27c39bbe9409f50657b9de9ade6e
2011-12-22 17:31:17 +00:00
drh de0f18154e Check the return code from sqlite3_initialize() called from within
sqlite3_soft_heap_limit64() and return an error from sqlite3_soft_heap_limit64()
if sqlite3_initialize() fails.

FossilOrigin-Name: 676acce274fec692a1eb34d27ee4b1327b4b3334
2011-12-22 17:10:35 +00:00
dan 0c8cda6e0d Fix a problem in fts3_write.c causing stack memory to be referenced after it is out of scope.
FossilOrigin-Name: f9c4a7c8f4e5821b47c1393d6272e32416d8886c
2011-12-22 15:30:46 +00:00
drh 3a7df358ab Merge recent trunk changes into the statvfs branch.
FossilOrigin-Name: 995ec8a41a7d091d9d87cb8f18f62102c8fda7d7
2011-12-22 02:10:57 +00:00
drh 3b5b351b65 Remove a redundant assignment operator.
FossilOrigin-Name: 19a6852a1e5905b35bc69b309951c5ec8ecd0bef
2011-12-22 01:46:04 +00:00
dan 7c3210e641 Fix other problems where 32-bit integer overflow may cause a problem. Two that require an improbably large sub-journal and two in test harness code.
FossilOrigin-Name: c723e3e18a008922281d8d6e8e3aba07941eb173
2011-12-21 18:04:41 +00:00
dan 27d47fbe6e Avoid 32-bit overflow when calculating the byte offset of an overflow page in SQLITE_DIRECT_OVERFLOW_READ code. Fix for [ac0ff496b7].
FossilOrigin-Name: c5256b59ad70104c1e181b9f49d1d712cf4cc9f6
2011-12-21 17:00:16 +00:00
drh f12b3f609f Add the ability to enable or disable SQLITE_IOCAP_ZERO_DAMAGE using a URI
parameter for both unix and windows.  Add a file-control to query or disable
the ZERO_DAMAGE setting.  Add the -uri option to the "sqlite3" TCL command
in tclsqlite3.c.  Allow the sqlite3_uri_parameter() interface to accept a
NULL pointer for its first parameter.

FossilOrigin-Name: c83627b73285f883719845c1b9fe85f378f28dd2
2011-12-21 14:42:29 +00:00
drh f694aa6454 Remove the code that tries to detect OOO header writes on a WAL recovery.
The code is made obsolete by syncing the WAL header.

FossilOrigin-Name: 7ac713a14e24c48651c2a97acc9839850fa7f3c3
2011-12-20 22:18:51 +00:00
drh d992b150c7 Refactor the sqlite3WalFrames() routine for clarity of presentation.
Do the padded transaction sync as the write pointer crosses the final
sector boundary instead of at the end, for efficiency.  Always sync
the WAL header immediately after it is written.

FossilOrigin-Name: 92c73b421b6242b09247dfb759777a531a107523
2011-12-20 20:13:25 +00:00
drh 4120994fdf Assert that the isCommit parameter to sqlite3WalFrames() is zero if and
only if the nTruncate parameter is zero.

FossilOrigin-Name: 979daf92e09305665d943e197b93b81139197c5b
2011-12-20 13:13:09 +00:00
dan 19969d96ce Add test for WAL mode to unixexcl.test.
FossilOrigin-Name: 4d518bd4801c31bb1e4fb0329ad057e549035237
2011-12-19 15:46:51 +00:00
dan 3604d7c687 Merge [21b76af6ed] into statvfs branch.
FossilOrigin-Name: e694f7b166144a0afba7846e1e18ad568b33a081
2011-12-19 11:57:41 +00:00
dan 7f9026dd94 Fix a couple of test cases to account for the master-journal name related change in [cf3bccc2].
FossilOrigin-Name: 21b76af6edd48f665cdd3af5f99d477f030c7668
2011-12-19 11:16:39 +00:00
dan 0774bb59c6 Modify test cases to account for the ZERO_DAMAGE change.
FossilOrigin-Name: 68684495f1a62a41ad27934f3a6d3bc9d290a57d
2011-12-19 10:07:56 +00:00
drh 1eaaf93a83 Some fixes to the test suite so that it works with ZERO_DAMAGE set to true.
Still lots more problems remain.

FossilOrigin-Name: 41891b231e20a1d32b1d7449e7863470eb38ca0a
2011-12-19 00:31:09 +00:00
drh 374f4a0447 For improved clarity of presentation,
refactor some of the code associated with ZERO_DAMAGE and sector-size.

FossilOrigin-Name: 1dde96c9ee88af1c4e37c2e65acb7c0fe6a20e2a
2011-12-17 20:02:11 +00:00
drh 8bbaa89d8d Add SQLITE_IOCAP_ZERO_DAMAGE and enable it for both unix and windows. Use
this device characteristic to reduce the required work in journaling.
A side effect is that this changes the default page exists back to 1024
even with the use of statvfs().

FossilOrigin-Name: a0be6ea464695fdf1eaf2b7cf0652778617814f2
2011-12-17 19:49:02 +00:00
drh 9c0e29371e Fix a bad #endif with the previous check-in on this branch.
FossilOrigin-Name: 915713ffe4d02ddf1d09a82e39a47b88d3d95ea0
2011-12-17 16:25:17 +00:00
drh 1da88f025f Add support for statvfs() in os_unix.c, for determining the sector size.
This causes many TCL test failures under Linux.

FossilOrigin-Name: e0d44450b9bec8ea7b057c1ad0a2088cd3f1f221
2011-12-17 16:09:16 +00:00
drh fe6163d7cf Merge in changes that cause the first sector of the WAL file to be synced
when the WAL restarts.  This is a fix for the power-loss corruption
problem described in ticket [ff5be73dee086]

FossilOrigin-Name: 44ca4d123385d759c11919865525c998c2e35bdb
2011-12-17 13:45:28 +00:00
dan 533100d3a3 Add tests to walcrash3.test.
FossilOrigin-Name: d76880428013ae2c5be00d87bb3e1695af6f706f
2011-12-17 08:10:34 +00:00
drh d2980310d0 Make sure the Salt-1 value in the WAL file is sequentially numbered at
each checkpoint, as it says it should be in the documentation.

FossilOrigin-Name: 7b63b11b93396079131686abb36c4221354fa50e
2011-12-17 01:31:44 +00:00
drh 4eb02a4556 Enhance the WAL header sync so that it honors the various synchronous
pragmas, settings, and device characteristics.

FossilOrigin-Name: 9799241f7de952c4d1ea8bf6508b577d2b57a370
2011-12-16 21:26:26 +00:00
drh 88f975a7a4 Proposed changes that ensure that the WAL header is written prior to the
first commit mark.

FossilOrigin-Name: 91d0437c0702904d27f0ef7b1b52d0797efe1826
2011-12-16 19:34:36 +00:00
dan e98842f02d Add code for a test that was failing before the persistent-wal related changes of [9ccc4a1be].
FossilOrigin-Name: 49d21ce50fcf535e470d284ccbb9eb4d4bcfa3a6
2011-12-16 17:01:04 +00:00
drh eed4250598 Merge the fix for [a1fa75cbdd02] from the experimental branch. Also
fix the persistent-wal mode feature of truncating the WAL on close so that
it always truncates the WAL to zero bytes.

FossilOrigin-Name: 09ccc4a1be7ba81890f10aac6623dd90dab4f990
2011-12-16 15:38:52 +00:00
drh 84968c051d Improved logging of master-journal name conflicts.
FossilOrigin-Name: b1005ef46cc2b46dd8e448ae1a9a9508bd5666ab
2011-12-16 15:11:39 +00:00
drh b654ee20fe Merge the nx-devkit changes into trunk.
FossilOrigin-Name: cf3bccc2e944cd2dd3efb8554682994a06115f16
2011-12-16 13:42:03 +00:00
dan f60b7f36c1 Experimental fix for [a1fa75cbdd].
FossilOrigin-Name: 6492af76ea6585a1b377d69751af930c0ccfe688
2011-12-16 13:24:27 +00:00
drh 8c24a369a5 Tweaks to the way multiplexSubOpen() works, for backwards compatibility.
FossilOrigin-Name: bb40338887c912be70cb6fe3b760d87c14bb88a5
2011-12-16 05:50:39 +00:00
drh 0699966798 Fix a potential infinite loop (inserted by the previous check-in)
on the multiplexWrite() method of the multiplexor.

FossilOrigin-Name: 022bf427c2edfe494ec7c222f436953ff56574a6
2011-12-16 05:09:46 +00:00
drh e7d9f13d99 The xTruncate method for the multiplexor now changes surplus overflow
files to zero-length by default.  Or if the "truncate" query parameter 
is used, it actually deletes the surplus overflow files.  This allows
VACUUM to shrink the database again.

FossilOrigin-Name: 5aefef04d6cc3b734c3f05e036b3869a52248f4d
2011-12-16 04:57:32 +00:00
drh 38deeb9763 Call sqlite3_log() with an appropriate message if unable to find a unique
master-journal filename.

FossilOrigin-Name: e9177f7d4ec19f0841d0613990d2fb5725700ba1
2011-12-16 01:30:15 +00:00
drh 5c531a4aab A better solution to being unable to find a unique master-journal filename:
just delete an existing master-journal and reuse it.

FossilOrigin-Name: 2685c2b949061f18bf6a4940eac8c8148873abb6
2011-12-16 01:21:31 +00:00
drh f580860f51 Make sure the antipenultimate character of master-journal filenames is a "9"
in order to avoid collisions with other files in 8+3 filename mode.  Also,
limit the number of attempts at finding a unique master-journal filename.

FossilOrigin-Name: 34a0483605d36e6cf03065ed0df33fb1f7c8a272
2011-12-16 00:33:04 +00:00
drh 14ec1ffecf Add stdio support to the quota VFS.
FossilOrigin-Name: 322bd15f97143d39b3a88d5f6cf7afb454e0666e
2011-12-15 17:44:33 +00:00
drh c39f3e0085 Use _commit() rather than FlushFileBuffers() as a substitute for fsync()
on windows.  Also cast for C++ and add support for SQLITE_FCNTL_VFSNAME.

FossilOrigin-Name: e85cfe9a17a2943ee0cf7915451ff6cc05908030
2011-12-15 17:42:35 +00:00
dan e712b5823e When deleting a file with the multiplexor VFS, also delete any overflow files that exist.
FossilOrigin-Name: 3af1feaa35d3fb2e7be550cd32a727001b874938
2011-12-15 17:00:10 +00:00
drh 78c0eafb35 Make sure the chunksize in test_multiplex does not cause the pending byte
to fall near the end of a chunk.  Adjust the chunksize upward as necessary
to prevent this.

FossilOrigin-Name: e05f8a2998f4f4cbdb65702baa65893c538b3d38
2011-12-15 13:29:12 +00:00
dan 31b21295b0 Fix some problems with dropped error codes in multiplexOpen().
FossilOrigin-Name: 2d50f78188e3297e8cefdf73cff51fa0a3b36e65
2011-12-15 11:45:19 +00:00
drh 2be25bffca In the multiplexor, do not try to delete overflow files that do not exist.
And assume all but the last overflow file is the size of the chunk size.

FossilOrigin-Name: a822a80d3cfe42b2fca6f8c9ff11762993114a27
2011-12-15 02:22:16 +00:00
drh a60ef3d570 Merge the nx-devkit changes into trunk. This includes the new
SQLITE_FCNTL_VFSNAME file-control.

FossilOrigin-Name: da118e02c0576ce16f7a26663f59413316223d55
2011-12-14 18:33:13 +00:00
drh 0f2ab8db33 Merge the VFSNAME file-control addition into the nx-devkit branch.
FossilOrigin-Name: 08c1dc517c1340737a55ad9012b7b06f72899c6f
2011-12-14 18:28:18 +00:00
drh de60fc2d87 Add a file-control that will discover the name of the bottom-level VFS and
all the shims in between.

FossilOrigin-Name: cdbfb553af02d10767a905489d9dfc57fc6be54e
2011-12-14 17:53:36 +00:00
drh 43a6d4bd44 Improvements to comments. No code changes.
FossilOrigin-Name: 08bbbd8e38697908ab8ae0f6b512d01c32f7e4da
2011-12-14 01:38:12 +00:00
mistachkin 6ca514b075 Treat a zero return value from the Win32 APIs MultiByteToWideChar and WideCharToMultiByte as an error condition.
FossilOrigin-Name: c65e5a36f1a1c91cb3415158ebe0f5759cbcdf96
2011-12-14 00:37:45 +00:00
drh 49ed5c8562 Harden the utf8-to-mbcs converter in the quota module against failures.
FossilOrigin-Name: 1cda511deb625868395a23c95346e14d0c300670
2011-12-14 00:04:52 +00:00
drh 27cec37d8e Enhancements to test_quota.c: Remove the external dependency on
sqlite3_win32_utf8_to_msbc().  Add an extra parameter to quota_fflush()
that will also do an fsync (or the equivalent).

FossilOrigin-Name: 92f4188f90e3cdd71f1457a6e0eb22615e4a54f4
2011-12-13 23:26:10 +00:00
dan 27e69643cf Add a hard limit to the number of chunks a multiplexed database may consist of if ENABLE_8_3_NAMES is defined.
FossilOrigin-Name: 43a1264088c57bf598787b7a9f5d7a2536603d67
2011-12-13 19:03:34 +00:00
drh c7f946297a Change the SQLITE_EXTRA_INIT routine to take a single argument which is a
pointer to a string.  Call SQLITE_EXTRA_INIT with a NULL argument.
Fixes to multiplexor to treat the VFS properly in corner cases.  Fix the
initialization of multiplex3.test.

FossilOrigin-Name: 8e65b9132530e46c62bd1352bfc2e9c29f57af5f
2011-12-13 18:22:38 +00:00
dan add995cc25 Add extra tests for the multiplexor VFS. No changes to code.
FossilOrigin-Name: c7de6f683d0fec62bc138b4a53b5cccc80c736c3
2011-12-13 16:40:33 +00:00
drh 1cdf011da3 Merge the nx-devkit changes into trunk.
FossilOrigin-Name: 03a70c3dae8d912fccd9d72c575dc372b198d238
2011-12-13 15:37:12 +00:00
drh 658dd586ed Update the multiplex.test script to conform to that found in the "experimental"
branch off of trunk.

FossilOrigin-Name: 2eb79efbff9cdab843b172e9fa9fb400c542fab1
2011-12-13 15:25:06 +00:00
drh 5b1626aa47 Move the multiplexor changes in the experimental branch
(check-ins [255d21499b] and [199f52bced]) into the nx-devkit branch.

FossilOrigin-Name: eb95d2f72c625bcfd0f8e9db1db2cb8799854b57
2011-12-13 15:02:54 +00:00
drh e5077c1211 When an sqlite3_auto_extension() function fails, report back its actual
error code, not the generic SQLITE_ERROR.

FossilOrigin-Name: ce55f250f56fa3a1ca864f81e9e86f911b981490
2011-12-13 04:08:36 +00:00
drh 43795e3b0b When the multiplexor FileSize procedure fails to open the file, assume that
the file is zero bytes in size.

FossilOrigin-Name: dc8118cd89da71f2035c6ee07c0edb772bba2186
2011-12-13 02:41:13 +00:00
drh 9797706c04 Add a compile-time shutdown procedure to be called by sqlite3_shutdown()
to undo the effects of the compile-time initialization procedure.

FossilOrigin-Name: c2ed86f5945662a4ddcdc7b303df3f64fb3bf17d
2011-12-13 01:34:21 +00:00
drh 73795becfe Backport a minimal set of changes needed to get 8+3 filenames and the
multiplexor shim playing well together.

FossilOrigin-Name: c4e2ce486217c96373836bfe641f46abf891799a
2011-12-12 20:01:12 +00:00
dan 28a67fd3f8 Fix os_unix.c so that, unless 8.3 filenames are actually in use, journal and wal file permissions are assigned correctly even if SQLITE_ENABLE_8_3_NAMES is defined.
FossilOrigin-Name: 169e12295cca701443746b1209bd6a7714fd8988
2011-12-12 19:48:43 +00:00
drh 663cebfeae Make sure the quota logic is usable as C++.
FossilOrigin-Name: f4534bd3023a599691018f35389a76045e49d831
2011-12-12 19:47:25 +00:00
dan 694592b247 Change the multiplexor VFS so that xTruncate is a no-op on database files.
FossilOrigin-Name: ee1e012256ae8010b6b6c4895a74b6883f20e73c
2011-12-12 18:55:44 +00:00
dan dc8ac1dd76 Fix for the xFileSize method of test_multiplex.c when used on a file opened with the DELETEONCLOSE flag set.
FossilOrigin-Name: 713071c493e9937a20c436b07299df428249e378
2011-12-12 18:21:44 +00:00
drh 743e003c0e Add magic comments to shell.c to make it easier for scripts to
insert additional version information displays for custom builds.

FossilOrigin-Name: 9b2691aa911289d74bfe3edf45937a33fb7eaa8d
2011-12-12 16:51:50 +00:00
drh 58cd3c6162 In the multiplexor shim, when using 8+3 filenames, begin numbering journal
overflow files with 101 instead of 001 to avoid name collisions with the
main database file.

FossilOrigin-Name: 05bf8a0b17a13a581ac973dc84ceaf66532d7f92
2011-12-11 22:44:09 +00:00
drh 630d296c57 Update comments describing the flattening optimization in select.c.
FossilOrigin-Name: dab4c137a852222f11179fa2ade52d17a4206dd2
2011-12-11 21:51:04 +00:00
drh e752cda894 Fix STAT3 so that it works with the new uninitialized register logic of
the VDBE.  Ticket [7bbfb7d4422ff]

FossilOrigin-Name: d11a57985c394772043c63d4c2b534944be0a664
2011-12-11 02:30:35 +00:00
drh 13e0ea9923 Fix harmless compiler warnings.
FossilOrigin-Name: 1e6a698aab9270637d3ea00fd6b0e94f4172e875
2011-12-11 02:29:25 +00:00
drh 4b3ac73c2f Minor cleanups of the compound-subquery flattening logic. New test cases
added for joins the compound subquery.

FossilOrigin-Name: 5061d85ff934db3c217c97acbbbed3286d9e02aa
2011-12-10 23:18:32 +00:00
drh 1acc6cbe00 Allow UNION ALL compounds to be promoted up to replace a simple wrapper
SELECT even if the compounds are joins.

FossilOrigin-Name: 3d4b4f4fb7c1d9f1c742fef884383e9f50f8630e
2011-12-10 17:17:17 +00:00
drh 678a9aa7bd Import the experimental parse-tree explainer, with fixes, from the
tree-explain branch.

FossilOrigin-Name: bcbc7152d49107afa926c8950360c61a6cf3d244
2011-12-10 15:55:01 +00:00
drh 69b2232d6e Add the sqlite3_quota_fflush() interface. Enhance sqlite3_quota_remove()
so that it can remove entire directories.

FossilOrigin-Name: abcb65af4cdd192beaccdbc2109ad45b9e7f9d00
2011-12-03 00:13:06 +00:00
drh a003691735 One minor documentation enhancement.
FossilOrigin-Name: 8cfd3575c8d9f5361c5276d6b83aba47606975a3
2011-12-02 15:31:07 +00:00
drh eff1433b6b Documentation improvements and additional test cases.
FossilOrigin-Name: fa71896089538589fb7015d6507e22961e72233b
2011-12-02 15:27:41 +00:00
drh 0661ca6ce2 Run quota-stdio tests in binary mode so that they work on windows.
FossilOrigin-Name: 71e4e97d9c883aa9f1d43d61543685924fc9339a
2011-12-01 22:12:58 +00:00
drh c8ccda6076 Progress toward getting things to work better on windows.
FossilOrigin-Name: 253dd7072ef7f4e8742e5b6430658f8e5102e0bb
2011-12-01 22:07:22 +00:00
drh a76e858917 Add test logic and some test cases.
FossilOrigin-Name: a4730586cc1f686ead956ccd1cc218b5931942c9
2011-12-01 20:48:15 +00:00
drh 60f21e4b6e Add stdio-like I/O interfaces to the test_quota VFS. This is a prototype
change for discussion and is mostly untested.  This is an alternative to
adding stdio-like I/O interfaces in the core.  There is no guarantee that
this code will make it into the trunk.  If it does get to trunk, there
could be many changes to the interface first.

FossilOrigin-Name: bd3ce723f1b5be52be46ede8614ca316f56e7e6f
2011-12-01 18:44:21 +00:00
58 changed files with 2983 additions and 641 deletions
+10 -11
View File
@@ -1386,7 +1386,6 @@ int sqlite3Fts3SegReaderNew(
int nRoot, /* Size of buffer containing root node */
Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */
){
int rc = SQLITE_OK; /* Return code */
Fts3SegReader *pReader; /* Newly allocated SegReader object */
int nExtra = 0; /* Bytes to allocate segment root node */
@@ -1414,13 +1413,8 @@ int sqlite3Fts3SegReaderNew(
}else{
pReader->iCurrentBlock = iStartLeaf-1;
}
if( rc==SQLITE_OK ){
*ppReader = pReader;
}else{
sqlite3Fts3SegReaderFree(pReader);
}
return rc;
*ppReader = pReader;
return SQLITE_OK;
}
/*
@@ -1470,6 +1464,7 @@ int sqlite3Fts3SegReaderPending(
Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */
){
Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */
Fts3HashElem *pE; /* Iterator variable */
Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */
int nElem = 0; /* Size of array at aElem */
int rc = SQLITE_OK; /* Return Code */
@@ -1478,7 +1473,6 @@ int sqlite3Fts3SegReaderPending(
pHash = &p->aIndex[iIndex].hPending;
if( bPrefix ){
int nAlloc = 0; /* Size of allocated array at aElem */
Fts3HashElem *pE = 0; /* Iterator variable */
for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){
char *zKey = (char *)fts3HashKey(pE);
@@ -1512,8 +1506,13 @@ int sqlite3Fts3SegReaderPending(
}else{
/* The query is a simple term lookup that matches at most one term in
** the index. All that is required is a straight hash-lookup. */
Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm);
** the index. All that is required is a straight hash-lookup.
**
** Because the stack address of pE may be accessed via the aElem pointer
** below, the "Fts3HashElem *pE" must be declared so that it is valid
** within this entire function, not just this "else{...}" block.
*/
pE = fts3HashFindElem(pHash, zTerm, nTerm);
if( pE ){
aElem = &pE;
nElem = 1;
+1 -1
View File
@@ -1193,7 +1193,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
int nBlob;
/* Check that value is actually a blob. */
if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_ERROR;
if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR;
/* Check that the blob is roughly the right size. */
nBlob = sqlite3_value_bytes(pValue);
+61 -53
View File
@@ -1,5 +1,5 @@
C Merge\sthe\slatest\strunk\schanges\sinto\stree-explain\sbranch.
D 2011-12-10T14:44:31.699
C Change\sthe\sname\sZERO_DAMAGE\sto\sthe\smore\sdescriptive\sPOWERSAFE_OVERWRITE.\nThe\squery\sparameter\sused\sto\scontrol\sthis\sdevice\scharacteristic\sis\snow\s"psow".
D 2011-12-23T01:04:17.601
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -78,14 +78,14 @@ F ext/fts3/fts3_test.c 24fa13f330db011500acb95590da9eee24951894
F ext/fts3/fts3_tokenizer.c 9ff7ec66ae3c5c0340fa081958e64f395c71a106
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
F ext/fts3/fts3_tokenizer1.c 0dde8f307b8045565cf63797ba9acfaff1c50c68
F ext/fts3/fts3_write.c c097228bff4d33c6b8a270c9717b9f8339068776
F ext/fts3/fts3_write.c fdf0c99830360146ec7128150271c8c014a8fef7
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 692e9192d148f318b3dca9f744600346a175eedd
F ext/rtree/rtree.c b92ab2e91e35c4964644647322813419c65fe1ce
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test 28e1b8da4da98093ce3210187434dd760a8d89d8
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
@@ -119,13 +119,13 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5
F src/analyze.c 5a1db16a651ce6310c8b046b2cbb736e030e14b9
F src/analyze.c f32ff304da413851eefa562b04e61ff6cb88248b
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 4368158da74d4711888e03264105c5c527d76caf
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c 80ea65224512884bb72976c93810d2dcaecc1353
F src/btree.c 2fdde7d16c80bd4e8a0913038e766c4297818f6f
F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce
F src/btreeInt.h ea863a819224d3e6845ad1e39954d41558b8cd8b
F src/build.c 8915bb6d72ead998f94c2756ea8d143c77709b70
@@ -134,11 +134,11 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
F src/expr.c d3a969a22368077b0efcb6028faa1c48ba7d2598
F src/expr.c 537591e95eac74af783e4eb033954fb218cf398e
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
F src/global.c 107ccaacb4b30895cf3a3a39decf417c804acfa1
F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
@@ -146,9 +146,9 @@ F src/insert.c ea820fe9af748075b3b6827fb6f23f25079bf1f7
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
F src/loadext.c d0d2022a5a07274d408820b978b9e549189d314f
F src/main.c 0e0b9dd5b054ed1aa3861b257035910aff9e1842
F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
F src/main.c e2e3fb34be05658f41ac65ee5d9a44e00a358b48
F src/malloc.c 15afac5e59b6584efe072e9933aefb4230e74f97
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
@@ -166,9 +166,9 @@ F src/os.c 28bbdab2170dfce84d86c45456a18eab1d0f99a9
F src/os.h 549b1a2e5e0ed1e1499f252dac126c4973e7379c
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
F src/os_unix.c 4fbb91726165e105c1679a2660f49a3f4c376e4f
F src/os_win.c 8af100f78f157eb6185fd9153d7f35b829c4da04
F src/pager.c d981f3bfcc0e4460537d983899620700ccf8f539
F src/os_unix.c 3a441671f35569df4b72641e928fdb1ab5cd8576
F src/os_win.c 569fe7448e781bfb8116aa79081df0eadf576fc6
F src/pager.c d03fb1de7bd724f0cdcae0aab0a733d89c94ac2f
F src/pager.h 5cd760857707529b403837d813d86b68938d6183
F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
F src/pcache.c 1fdd77978c1525d1ca4b9ef48eb80abca710cb4c
@@ -178,23 +178,23 @@ F src/pragma.c dd66f21fafe7be40e1a48ad4195764cc191cf583
F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e
F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
F src/resolve.c 3d3e80a98f203ac6b9329e9621e29eda85ddfd40
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c 97275df6f40f1df1c389e275c2185a4edf202ad2
F src/shell.c a1eadb2fdbfa45e54307263f0c8da8ee8cd61b8b
F src/sqlite.h.in 1dc07194eb1a2c69c8ef75f88022b170be08024a
F src/select.c a1d075db66a0ea42807353501b62997969e5be79
F src/shell.c aa4183d4a5243d8110b1d3d77faa4aea7e9c9c2d
F src/sqlite.h.in 3d1a77e27b9d9979fb7ec84834a8423602d29dc5
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
F src/sqliteInt.h d781d89415ada4815794fc2587601cd44272f94d
F src/sqliteInt.h b8fdd9c39c8d7f5c794f4ea917293d9c75b9aff2
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c de581e2e71f5e7f98366156afad83b4742ac6fe0
F src/test1.c fe455fc1be1b0dbf4ce45b11c255900e8ae18da3
F src/tclsqlite.c bd86070f52ae3f77a2e6b3b065ff03adb9140bfa
F src/test1.c 1b1e514e85ffe7152b02cba38bd0a1ce8cd56113
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
F src/test5.c e1a19845625144caf038031234a12185e40d315c
F src/test6.c 3191b4ab964a144230ff9ef96c093520375c7b2a
F src/test6.c cf6ab27a59e1ab64b011bb251ba600131e803e59
F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843
F src/test8.c 99f70341d6ec480313775127f4cd14b4a47db557
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
@@ -211,46 +211,47 @@ F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
F src/test_journal.c 03313c693cca72959dcaaf79f8d76f21c01e19ff
F src/test_journal.c 2c06e4be6584d51b935dc8b353980a9388de62ef
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
F src/test_malloc.c 8d416f29ad8573f32601f6056c9d2b17472e9ad5
F src/test_multiplex.c 185378cade15fa9012bd17d9d5b04783d4478f79
F src/test_multiplex.c 7a41891cda4c6b43298656d88e37e3c6244a90ff
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32
F src/test_osinst.c 6abf0a37ce831120c4ef1b913afdd813e7ac1a73
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
F src/test_quota.c a391c866217e92986c6f523f05b08aa6956c8419
F src/test_quota.c 1a5874e3ee9074426f43b37e8d7404948065b585
F src/test_quota.h 9ffa1d3ad6d0a6a24e8670ea64b909c717ec3358
F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
F src/test_stat.c 69de4361c7a69fc1136d31ab7144408cd00805c7
F src/test_stat.c 80271ad7d776a79babe0e025bb3a1bfcd3a3cfb1
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c 35022393dd54d147b998b6b7f7e945b01114d666
F src/test_vfs.c 27b7d9de40630f603b9e2cf9ef2a7c81d31c4515
F src/test_vfstrace.c 0b884e06094a746da729119a2cabdc7aa790063d
F src/test_vfs.c 07157a0bbfe161cb5e32cad2079abd26cd611c4b
F src/test_vfstrace.c 065c7270a614254b2c68fbc7ba8d1fb1d5cbc823
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
F src/update.c d3076782c887c10e882996550345da9c4c9f9dea
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
F src/util.c 01238e2b0f24a14779181dbf991fe02620a80e31
F src/util.c 19ae3ee0dfa88e6054838afc5920327c5918fa2b
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
F src/vdbe.c 029add0c5197a61db588824a58570547330b9d8f
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
F src/vdbeInt.h 72222af1a52bcdfa94e1165e083734484fc0863f
F src/vdbeInt.h 48c158b2fceca9682d1577e61c62da3c58cf0748
F src/vdbeapi.c 86189ebba2c49791d75eaa12929f3ce6527596bd
F src/vdbeaux.c 1b99a1f6c56aecfebc172ff6985ba9818f6c8caa
F src/vdbeaux.c 3015179b27672cb773d014495023eaa4a8cd8f9c
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
F src/vdbetrace.c 7e5946109138ff6f7f94e79fc702755bf79373a8
F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
F src/wal.c 7e6e7fe68ee649505dca38c8ab83eda0d0d96ae5
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
F src/wal.c 13c34ed72fd3a0cb81b3addbc901b3be60430cca
F src/wal.h 42f8313f7aaf8913e2d1fdf7b47025c23491ea1d
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c af623942514571895818b9b7ae11db95ae3b3d88
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
@@ -296,6 +297,7 @@ F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
F test/bigfile.test a8ec8073a20207456dab01a29ad9cde42b0dd103
F test/bigfile2.test f8e83eca9abef60692a34255a2ebcb96aff897fc
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
@@ -513,7 +515,7 @@ F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
F test/incrvacuum2.test 62fbeb85459fe4e501684d8fb5b6e98a23e3b0c0
F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b
F test/incrvacuum_ioerr.test 22f208d01c528403240e05beecc41dc98ed01637
F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
@@ -543,7 +545,7 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901
F test/journal1.test 8b71ef1ed5798bdc0e6eb616d8694e2c2c188d4d
F test/journal2.test 29937bdbb253bbfd92057610120bdc0aa7e84a0a
F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1
F test/journal3.test 6fd28532c88b447db844186bc190523108b6dbb4
F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
@@ -605,7 +607,9 @@ F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test eafaa41b9133d7a2ded4641bbe5f340731d35a52
F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054
F test/multiplex.test 770f0295dd6673e60458cb93abd033ed2f253291
F test/multiplex.test 8bc3c71f73fe833bc8a659d454d320044a33b5da
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test 15903c343f1eaa4b00998b7ceacfc4987e4ccfe9
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
@@ -616,7 +620,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
F test/pager1.test 1b630b3248c7d28862fe9e190cfe52234b502504
F test/pager1.test 9e9f5f1c6d4df4831dbff213b1262ef94bf72118
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
@@ -626,14 +630,16 @@ F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
F test/permutations.test 522823b47238cb1754198f80817fe9f9158ede55
F test/permutations.test 8db6d3b72e6ce423cfb94d87926e5edcb4b0078f
F test/pragma.test 7fa35e53085812dac94c2bfcbb02c2a4ad35df5e
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quota.test 1c59a396e8f7b5d8466fa74b59f2aeb778d74f7a
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
F test/quota.test af47d25c166aa7b33ef25f21bb7f2afb29d82c77
F test/quota2.test 1b8df088e604f2df573f96e726b5e518cb0cddaa
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
@@ -666,7 +672,7 @@ F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
F test/select9.test 74c0fb2c6eecb0219cbed0cbe3df136f8fbf9343
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
F test/selectB.test 0d072c5846071b569766e6cd7f923f646a8b2bfa
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test f9bf1bc4581b5b8158caa6e4e4f682acb379fb25
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/shared.test 34945a516532b11182c3eb26e31247eee3c9ae48
@@ -698,9 +704,9 @@ F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 7b1167925e9d30a5d1f0701d24812fdda42c3a86
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
F test/syscall.test 2a922050dbee032f587249b070fb42692f5e1e22
F test/syscall.test 265cda616f56a297406728ee1e74c9b4a93aa6dd
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
@@ -866,7 +872,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e
F test/unixexcl.test 892937c53d0c16e76631674e38a0fce052ae5e9c
F test/unordered.test f53095cee37851bf30130fa1bf299a8845e837bb
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
F test/uri.test 0d289d32396bdbc491e9dc845f1a52e13f861e0b
@@ -896,11 +902,11 @@ F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
F test/wal.test c743be787e60c1242fa6cdf73b410e64b2977e25
F test/wal2.test ad6412596815f553cd30f271d291ab003092bc7e
F test/wal3.test 18da4e65c30c43c646ad40e145e9a074e4062fc9
F test/wal.test edefe316b4125d7f68004ea53c5e73c398d436cc
F test/wal2.test f11883dd3cb7f647c5d2acfd7b5c6d4ba5770cc9
F test/wal3.test 0f9bb79aa5712e9fa2343d1cdc9e2785adddcfc2
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b
F test/wal5.test f58ed4b8b542f71c7441da12fbd769d99b362437
F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
@@ -909,11 +915,12 @@ F test/walbig.test 0ab8a430ef420a3114f7092e0f30fc9585ffa155
F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde
F test/walcrash.test 4fcb661faf71db91214156d52d43ee327f52bde1
F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
F test/walcrash3.test 595e44c6197f0d0aa509fc135be2fd0209d11a2c
F test/walfault.test efb0d5724893133e71b8d9d90abdb781845a6bb0
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c
F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
F test/walpersist.test 710b1b6cf6f8333e984f437724d1fa9e0511c5aa
F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
F test/walro.test e6bb27762c9f22601cbb8bff6e0acfd124e74b63
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
@@ -934,6 +941,7 @@ F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test 7ef0680559163118705975be132933898d349674
F tool/build-shell.sh 12aa4391073a777fcb6dcc490b219a018ae98bac
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
@@ -978,7 +986,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 7b457ea4551ba411a4747d74fb78b795cc8d9ee6 256e27bd118ed3ab6ecb19ad6a6494b71ac9bdd5
R aa2b80b8c93dd58f9be82d377cb63c4d
P d5e36327c12f264429eb079bddbb71a310f76389
R d510fa35ee7302e7724e03225e057408
U drh
Z 940f9f8f15ab9108dec876188e59d100
Z cc6357a73838dede95445f3f634219d1
+1 -1
View File
@@ -1 +1 @@
1a360da0f8314f232c224c71829646bc7558892b
6191c5e45175f5c6040e891843b0725a929d6dd7
+1
View File
@@ -529,6 +529,7 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum);
sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
(char*)&stat3InitFuncdef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, 2);
+1 -1
View File
@@ -3979,7 +3979,7 @@ static int accessPayload(
u8 aSave[4];
u8 *aWrite = &pBuf[-4];
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, pBt->pageSize * (nextPage-1));
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
nextPage = get4byte(aWrite);
memcpy(aWrite, aSave, 4);
}else
+4 -4
View File
@@ -870,7 +870,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
pItem->iCol = pOldItem->iCol;
pItem->iOrderByCol = pOldItem->iOrderByCol;
pItem->iAlias = pOldItem->iAlias;
}
return pNew;
@@ -2949,7 +2949,7 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
return inReg;
}
#if defined(SQLITE_DEBUG)
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
/*
** Generate a human-readable explanation of an expression tree.
*/
@@ -3178,9 +3178,9 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
sqlite3ExplainPrintf(pOut,")");
}
}
#endif /* defined(SQLITE_DEBUG) */
#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */
#if defined(SQLITE_DEBUG)
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
/*
** Generate a human-readable explanation of an expression list.
*/
+1 -1
View File
@@ -147,7 +147,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
500, /* nLookaside */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
{0,0,0,0,0,0,0,0,0,0,0}, /* pcache2 */
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
(void*)0, /* pHeap */
0, /* nHeap */
0, 0, /* mnHeap, mxHeap */
+3 -2
View File
@@ -625,6 +625,7 @@ void sqlite3_reset_auto_extension(void){
void sqlite3AutoLoadExtensions(sqlite3 *db){
int i;
int go = 1;
int rc;
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
wsdAutoextInit;
@@ -647,8 +648,8 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
}
sqlite3_mutex_leave(mutex);
zErrmsg = 0;
if( xInit && xInit(db, &zErrmsg, &sqlite3Apis) ){
sqlite3Error(db, SQLITE_ERROR,
if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){
sqlite3Error(db, rc,
"automatic extension loading failed: %s", zErrmsg);
go = 0;
}
+42 -8
View File
@@ -239,8 +239,8 @@ int sqlite3_initialize(void){
*/
#ifdef SQLITE_EXTRA_INIT
if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
int SQLITE_EXTRA_INIT(void);
rc = SQLITE_EXTRA_INIT();
int SQLITE_EXTRA_INIT(const char*);
rc = SQLITE_EXTRA_INIT(0);
}
#endif
@@ -257,6 +257,10 @@ int sqlite3_initialize(void){
*/
int sqlite3_shutdown(void){
if( sqlite3GlobalConfig.isInit ){
#ifdef SQLITE_EXTRA_SHUTDOWN
void SQLITE_EXTRA_SHUTDOWN(void);
SQLITE_EXTRA_SHUTDOWN();
#endif
sqlite3_os_end();
sqlite3_reset_auto_extension();
sqlite3GlobalConfig.isInit = 0;
@@ -2239,10 +2243,13 @@ static int openDatabase(
/* Load automatic extensions - extensions that have been registered
** using the sqlite3_automatic_extension() API.
*/
sqlite3AutoLoadExtensions(db);
rc = sqlite3_errcode(db);
if( rc!=SQLITE_OK ){
goto opendb_out;
if( rc==SQLITE_OK ){
sqlite3AutoLoadExtensions(db);
rc = sqlite3_errcode(db);
if( rc!=SQLITE_OK ){
goto opendb_out;
}
}
#ifdef SQLITE_ENABLE_FTS1
@@ -2936,12 +2943,13 @@ int sqlite3_test_control(int op, ...){
break;
}
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
/* sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT,
** sqlite3_stmt*,const char**);
**
** If compiled with SQLITE_DEBUG, each sqlite3_stmt holds a string that
** describes the optimized parse tree. This test-control returns a
** pointer to that string.
** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds
** a string that describes the optimized parse tree. This test-control
** returns a pointer to that string.
*/
case SQLITE_TESTCTRL_EXPLAIN_STMT: {
sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*);
@@ -2949,6 +2957,7 @@ int sqlite3_test_control(int op, ...){
*pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt);
break;
}
#endif
}
va_end(ap);
@@ -2968,6 +2977,7 @@ int sqlite3_test_control(int op, ...){
** returns a NULL pointer.
*/
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
if( zFilename==0 ) return 0;
zFilename += sqlite3Strlen30(zFilename) + 1;
while( zFilename[0] ){
int x = strcmp(zFilename, zParam);
@@ -2978,6 +2988,30 @@ const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
return 0;
}
/*
** Return a boolean value for a query parameter.
*/
int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){
const char *z = sqlite3_uri_parameter(zFilename, zParam);
return z ? sqlite3GetBoolean(z) : (bDflt!=0);
}
/*
** Return a 64-bit integer value for a query parameter.
*/
sqlite3_int64 sqlite3_uri_int64(
const char *zFilename, /* Filename as passed to xOpen */
const char *zParam, /* URI parameter sought */
sqlite3_int64 bDflt /* return if parameter is missing */
){
const char *z = sqlite3_uri_parameter(zFilename, zParam);
sqlite3_int64 v;
if( z && sqlite3Atoi64(z, &v, sqlite3Strlen30(z), SQLITE_UTF8)==SQLITE_OK ){
bDflt = v;
}
return bDflt;
}
/*
** Return the filename of the database associated with a database
** connection.
+2 -1
View File
@@ -130,7 +130,8 @@ sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
sqlite3_int64 priorLimit;
sqlite3_int64 excess;
#ifndef SQLITE_OMIT_AUTOINIT
sqlite3_initialize();
int rc = sqlite3_initialize();
if( rc ) return -1;
#endif
sqlite3_mutex_enter(mem0.mutex);
priorLimit = mem0.alarmThreshold;
+86 -24
View File
@@ -122,6 +122,10 @@
#ifndef SQLITE_OMIT_WAL
#include <sys/mman.h>
#endif
#ifndef MISSING_STATVFS
#include <sys/statvfs.h>
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
# include <sys/ioctl.h>
@@ -206,10 +210,12 @@ struct UnixUnusedFd {
typedef struct unixFile unixFile;
struct unixFile {
sqlite3_io_methods const *pMethod; /* Always the first entry */
sqlite3_vfs *pVfs; /* The VFS that created this unixFile */
unixInodeInfo *pInode; /* Info about locks on this inode */
int h; /* The file descriptor */
unsigned char eFileLock; /* The type of lock held on this fd */
unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
unsigned char szSector; /* Sectorsize/512 */
int lastErrno; /* The unix errno from last I/O error */
void *lockingContext; /* Locking style specific state */
UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
@@ -257,6 +263,7 @@ struct unixFile {
#else
# define UNIXFILE_DIRSYNC 0x00
#endif
#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
/*
** Include code that is common to all os_*.c files
@@ -413,6 +420,14 @@ static struct unix_syscall {
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
#if defined(MISSING_STATVFS)
{ "statvfs", (sqlite3_syscall_ptr)0, 0 },
#define osStatvfs ((int(*)(const char*,void*))aSyscall[20].pCurrent)
#else
{ "statvfs", (sqlite3_syscall_ptr)statvfs, 0 },
#define osStatvfs ((int(*)(const char*,struct statvfs*))aSyscall[20].pCurrent)
#endif
}; /* End of the overrideable system calls */
/*
@@ -2014,8 +2029,8 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
rc = osRmdir(zLockFile);
if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile);
if( rc<0 ){
int rc = 0;
int tErrno = errno;
rc = 0;
if( ENOENT != tErrno ){
rc = SQLITE_IOERR_UNLOCK;
}
@@ -3497,6 +3512,22 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
return SQLITE_OK;
}
/*
** If *pArg is inititially negative then this is a query. Set *pArg to
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
**
** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
*/
static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
if( *pArg<0 ){
*pArg = (pFile->ctrlFlags & mask)!=0;
}else if( (*pArg)==0 ){
pFile->ctrlFlags &= ~mask;
}else{
pFile->ctrlFlags |= mask;
}
}
/*
** Information and control of an open file handle.
*/
@@ -3523,14 +3554,15 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return rc;
}
case SQLITE_FCNTL_PERSIST_WAL: {
int bPersist = *(int*)pArg;
if( bPersist<0 ){
*(int*)pArg = (pFile->ctrlFlags & UNIXFILE_PERSIST_WAL)!=0;
}else if( bPersist==0 ){
pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
}else{
pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
}
unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_VFSNAME: {
*(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
return SQLITE_OK;
}
#ifndef NDEBUG
@@ -3567,17 +3599,46 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
** a database and its journal file) that the sector size will be the
** same for both.
*/
static int unixSectorSize(sqlite3_file *NotUsed){
UNUSED_PARAMETER(NotUsed);
return SQLITE_DEFAULT_SECTOR_SIZE;
static int unixSectorSize(sqlite3_file *pFile){
unixFile *p = (unixFile*)pFile;
if( p->szSector==0 ){
#ifdef MISSING_STATVFS
p->szSector = SQLITE_DEFAULT_SECTOR_SIZE/512;
#else
struct statvfs x;
int sz;
memset(&x, 0, sizeof(x));
osStatvfs(p->zPath, &x);
sz = (int)x.f_frsize;
if( sz<512 || sz>65536 || (sz&(sz-1))!=0 ){
sz = SQLITE_DEFAULT_SECTOR_SIZE;
}
p->szSector = sz/512;
#endif
}
return p->szSector*512;
}
/*
** Return the device characteristics for the file. This is always 0 for unix.
** Return the device characteristics for the file.
**
** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
** However, that choice is contraversial since technically the underlying
** file system does not always provide powersafe overwrites. (In other
** words, after a power-loss event, parts of the file that were never
** written might end up being altered.) However, non-PSOW behavior is very,
** very rare. And asserting PSOW makes a large reduction in the amount
** of required I/O for journaling, since a lot of padding is eliminated.
** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control
** available to turn it off and URI query parameter available to turn it off.
*/
static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
UNUSED_PARAMETER(NotUsed);
return 0;
static int unixDeviceCharacteristics(sqlite3_file *id){
unixFile *p = (unixFile*)id;
if( p->ctrlFlags & UNIXFILE_PSOW ){
return SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}else{
return 0;
}
}
#ifndef SQLITE_OMIT_WAL
@@ -3861,10 +3922,8 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
}
if( pInode->bProcessLock==0 ){
const char *zRO;
int openFlags = O_RDWR | O_CREAT;
zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm");
if( zRO && sqlite3GetBoolean(zRO) ){
if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
openFlags = O_RDONLY;
pShmNode->isReadonly = 1;
}
@@ -4560,11 +4619,14 @@ static int fillInUnixFile(
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
pNew->h = h;
pNew->pVfs = pVfs;
pNew->zPath = zFilename;
pNew->ctrlFlags = 0;
if( sqlite3_uri_boolean(zFilename, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
pNew->ctrlFlags |= UNIXFILE_PSOW;
}
if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
pNew->ctrlFlags = UNIXFILE_EXCL;
}else{
pNew->ctrlFlags = 0;
pNew->ctrlFlags |= UNIXFILE_EXCL;
}
if( isReadOnly ){
pNew->ctrlFlags |= UNIXFILE_RDONLY;
@@ -4899,7 +4961,7 @@ static int findCreateFileMode(
*/
nDb = sqlite3Strlen30(zPath) - 1;
#ifdef SQLITE_ENABLE_8_3_NAMES
while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--;
while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--;
if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
#else
while( zPath[nDb]!='-' ){
@@ -6771,7 +6833,7 @@ int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
assert( ArraySize(aSyscall)==20 );
assert( ArraySize(aSyscall)==21 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
+50 -9
View File
@@ -59,7 +59,7 @@ struct winFile {
HANDLE h; /* Handle for accessing the file */
u8 locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
u8 bPersistWal; /* True to persist WAL files */
u8 ctrlFlags; /* Flags. See WINFILE_* below */
DWORD lastErrno; /* The Windows errno from the last I/O error */
DWORD sectorSize; /* Sector size of the device file is on */
winShm *pShm; /* Instance of shared memory on this file */
@@ -74,6 +74,12 @@ struct winFile {
#endif
};
/*
** Allowed values for winFile.ctrlFlags
*/
#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
/*
* If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
* various Win32 API heap functions instead of our own.
@@ -918,6 +924,9 @@ static LPWSTR utf8ToUnicode(const char *zFilename){
LPWSTR zWideFilename;
nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
if( nChar==0 ){
return 0;
}
zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) );
if( zWideFilename==0 ){
return 0;
@@ -940,6 +949,9 @@ static char *unicodeToUtf8(LPCWSTR zWideFilename){
char *zFilename;
nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
if( nByte == 0 ){
return 0;
}
zFilename = sqlite3_malloc( nByte );
if( zFilename==0 ){
return 0;
@@ -967,6 +979,9 @@ static LPWSTR mbcsToUnicode(const char *zFilename){
nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL,
0)*sizeof(WCHAR);
if( nByte==0 ){
return 0;
}
zMbcsFilename = sqlite3_malloc( nByte*sizeof(zMbcsFilename[0]) );
if( zMbcsFilename==0 ){
return 0;
@@ -993,6 +1008,9 @@ static char *unicodeToMbcs(LPCWSTR zWideFilename){
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
if( nByte == 0 ){
return 0;
}
zFilename = sqlite3_malloc( nByte );
if( zFilename==0 ){
return 0;
@@ -2113,6 +2131,22 @@ static int winUnlock(sqlite3_file *id, int locktype){
return rc;
}
/*
** If *pArg is inititially negative then this is a query. Set *pArg to
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
**
** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
*/
static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
if( *pArg<0 ){
*pArg = (pFile->ctrlFlags & mask)!=0;
}else if( (*pArg)==0 ){
pFile->ctrlFlags &= ~mask;
}else{
pFile->ctrlFlags |= mask;
}
}
/*
** Control and query of the open file handle.
*/
@@ -2148,12 +2182,15 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK;
}
case SQLITE_FCNTL_PERSIST_WAL: {
int bPersist = *(int*)pArg;
if( bPersist<0 ){
*(int*)pArg = pFile->bPersistWal;
}else{
pFile->bPersistWal = bPersist!=0;
}
winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
winModeBit(pFile, WINFILE_PSOW, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_VFSNAME: {
*(char**)pArg = sqlite3_mprintf("win32");
return SQLITE_OK;
}
case SQLITE_FCNTL_SYNC_OMITTED: {
@@ -2196,8 +2233,9 @@ static int winSectorSize(sqlite3_file *id){
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
UNUSED_PARAMETER(id);
return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
winFile *p = (winFile*)id;
return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
}
#ifndef SQLITE_OMIT_WAL
@@ -3162,6 +3200,9 @@ static int winOpen(
pFile->pVfs = pVfs;
pFile->pShm = 0;
pFile->zPath = zName;
if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
pFile->ctrlFlags |= WINFILE_PSOW;
}
pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
#if SQLITE_OS_WINCE
+47 -24
View File
@@ -616,6 +616,7 @@ struct Pager {
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
u8 tempFile; /* zFilename is a temporary file */
u8 readOnly; /* True for a read-only database */
@@ -786,7 +787,7 @@ static int pagerUseWal(Pager *pPager){
#else
# define pagerUseWal(x) 0
# define pagerRollbackWal(x) 0
# define pagerWalFrames(v,w,x,y,z) 0
# define pagerWalFrames(v,w,x,y) 0
# define pagerOpenWalIfPresent(z) SQLITE_OK
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
@@ -2514,23 +2515,36 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
** the value returned by the xSectorSize() method rounded up to 32 if
** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it
** is greater than MAX_SECTOR_SIZE.
**
** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set
** the effective sector size to its minimum value (512). The purpose of
** pPager->sectorSize is to define the "blast radius" of bytes that
** might change if a crash occurs while writing to a single byte in
** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero
** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector
** size. For backwards compatibility of the rollback journal file format,
** we cannot reduce the effective sector size below 512.
*/
static void setSectorSize(Pager *pPager){
assert( isOpen(pPager->fd) || pPager->tempFile );
if( !pPager->tempFile ){
if( pPager->tempFile
|| (sqlite3OsDeviceCharacteristics(pPager->fd) &
SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
){
/* Sector size doesn't matter for temporary files. Also, the file
** may not have been opened yet, in which case the OsSectorSize()
** call will segfault.
*/
pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
}
if( pPager->sectorSize<32 ){
** call will segfault. */
pPager->sectorSize = 512;
}
if( pPager->sectorSize>MAX_SECTOR_SIZE ){
assert( MAX_SECTOR_SIZE>=512 );
pPager->sectorSize = MAX_SECTOR_SIZE;
}else{
pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
if( pPager->sectorSize<32 ){
pPager->sectorSize = 512;
}
if( pPager->sectorSize>MAX_SECTOR_SIZE ){
assert( MAX_SECTOR_SIZE>=512 );
pPager->sectorSize = MAX_SECTOR_SIZE;
}
}
}
@@ -2955,8 +2969,7 @@ static int pagerWalFrames(
Pager *pPager, /* Pager object */
PgHdr *pList, /* List of frames to log */
Pgno nTruncate, /* Database size after this commit */
int isCommit, /* True if this is a commit */
int syncFlags /* Flags to pass to OsSync() (or 0) */
int isCommit /* True if this is a commit */
){
int rc; /* Return code */
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
@@ -2987,7 +3000,7 @@ static int pagerWalFrames(
if( pList->pgno==1 ) pager_write_changecounter(pList);
rc = sqlite3WalFrames(pPager->pWal,
pPager->pageSize, pList, nTruncate, isCommit, syncFlags
pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags
);
if( rc==SQLITE_OK && pPager->pBackup ){
PgHdr *p;
@@ -3267,13 +3280,13 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
*/
if( pSavepoint ){
u32 ii; /* Loop counter */
i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize);
i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize);
if( pagerUseWal(pPager) ){
rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData);
}
for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){
assert( offset==ii*(4+pPager->pageSize) );
assert( offset==(i64)ii*(4+pPager->pageSize) );
rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1);
}
assert( rc!=SQLITE_DONE );
@@ -3367,6 +3380,10 @@ void sqlite3PagerSetSafetyLevel(
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
}
pPager->walSyncFlags = pPager->syncFlags;
if( pPager->fullSync ){
pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
}
}
#endif
@@ -4121,7 +4138,7 @@ static int subjournalPage(PgHdr *pPg){
** write the journal record into the file. */
if( rc==SQLITE_OK ){
void *pData = pPg->pData;
i64 offset = pPager->nSubRec*(4+pPager->pageSize);
i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize);
char *pData2;
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
@@ -4194,7 +4211,7 @@ static int pagerStress(void *p, PgHdr *pPg){
rc = subjournalPage(pPg);
}
if( rc==SQLITE_OK ){
rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
rc = pagerWalFrames(pPager, pPg, 0, 0);
}
}else{
@@ -4533,9 +4550,17 @@ int sqlite3PagerOpen(
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
pPager->fullSync = pPager->noSync ?0:1;
pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = pPager->syncFlags;
if( pPager->noSync ){
assert( pPager->fullSync==0 );
assert( pPager->syncFlags==0 );
assert( pPager->walSyncFlags==0 );
assert( pPager->ckptSyncFlags==0 );
}else{
pPager->fullSync = 1;
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
}
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
@@ -5765,9 +5790,7 @@ int sqlite3PagerCommitPhaseOne(
}
assert( rc==SQLITE_OK );
if( ALWAYS(pList) ){
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
(pPager->fullSync ? pPager->syncFlags : 0)
);
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1);
}
sqlite3PagerUnref(pPageOne);
if( rc==SQLITE_OK ){
+7 -7
View File
@@ -799,7 +799,7 @@ static int resolveCompoundOrderBy(
pE->pColl = pColl;
pE->flags |= EP_IntValue | flags;
pE->u.iValue = iCol;
pItem->iCol = (u16)iCol;
pItem->iOrderByCol = (u16)iCol;
pItem->done = 1;
}else{
moreToDo = 1;
@@ -848,12 +848,12 @@ int sqlite3ResolveOrderGroupBy(
pEList = pSelect->pEList;
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
if( pItem->iCol ){
if( pItem->iCol>pEList->nExpr ){
if( pItem->iOrderByCol ){
if( pItem->iOrderByCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType);
resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType);
}
}
return 0;
@@ -900,7 +900,7 @@ static int resolveOrderGroupBy(
** a copy of the iCol-th result-set column. The subsequent call to
** sqlite3ResolveOrderGroupBy() will convert the expression to a
** copy of the iCol-th result-set expression. */
pItem->iCol = (u16)iCol;
pItem->iOrderByCol = (u16)iCol;
continue;
}
if( sqlite3ExprIsInteger(pE, &iCol) ){
@@ -911,12 +911,12 @@ static int resolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, nResult);
return 1;
}
pItem->iCol = (u16)iCol;
pItem->iOrderByCol = (u16)iCol;
continue;
}
/* Otherwise, treat the ORDER BY term as an ordinary expression */
pItem->iCol = 0;
pItem->iOrderByCol = 0;
if( sqlite3ResolveExprNames(pNC, pE) ){
return 1;
}
+22 -15
View File
@@ -2219,8 +2219,8 @@ static int multiSelectOrderBy(
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
assert( pItem->iCol>0 );
if( pItem->iCol==i ) break;
assert( pItem->iOrderByCol>0 );
if( pItem->iOrderByCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
@@ -2228,7 +2228,7 @@ static int multiSelectOrderBy(
pNew->flags |= EP_IntValue;
pNew->u.iValue = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
pOrderBy->a[nOrderBy++].iCol = (u16)i;
pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i;
}
}
}
@@ -2244,8 +2244,8 @@ static int multiSelectOrderBy(
if( aPermute ){
struct ExprList_item *pItem;
for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
assert( pItem->iCol>0 && pItem->iCol<=p->pEList->nExpr );
aPermute[i] = pItem->iCol - 1;
assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->iOrderByCol - 1;
}
pKeyMerge =
sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
@@ -2588,9 +2588,8 @@ static void substSelect(
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries in order to speed
** execution. It returns 1 if it makes changes and 0 if no flattening
** occurs.
** This routine attempts to flatten subqueries as a performance optimization.
** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following
** query:
@@ -2632,7 +2631,10 @@ static void substSelect(
** (6) The subquery does not use aggregates or the outer query is not
** DISTINCT.
**
** (7) The subquery has a FROM clause.
** (7) The subquery has a FROM clause. TODO: For subqueries without
** A FROM clause, consider adding a FROM close with the special
** table sqlite_once that consists of a single row containing a
** single NULL.
**
** (8) The subquery does not use LIMIT or the outer query is not a join.
**
@@ -2665,11 +2667,14 @@ static void substSelect(
**
** * is not itself part of a compound select,
** * is not an aggregate or DISTINCT query, and
** * has no other tables or sub-selects in the FROM clause.
** * is not a join
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
** LIMIT and OFFSET clauses.
** LIMIT and OFFSET clauses. The subquery cannot use any compound
** operator other than UNION ALL because all the other compound
** operators have an implied DISTINCT which is disallowed by
** restriction (4).
**
** (18) If the sub-query is a compound select, then all terms of the
** ORDER by clause of the parent must be simple references to
@@ -2681,7 +2686,7 @@ static void substSelect(
** (20) If the sub-query is a compound select, then it must not use
** an ORDER BY clause. Ticket #3773. We could relax this constraint
** somewhat by saying that the terms of the ORDER BY clause must
** appear as unmodified result columns in the outer query. But
** appear as unmodified result columns in the outer query. But we
** have other optimizations in mind to deal with that case.
**
** (21) The subquery does not use LIMIT or the outer query is not
@@ -2810,19 +2815,21 @@ static int flattenSubquery(
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
assert( pSub->pSrc!=0 );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
|| NEVER(pSub1->pSrc==0) || pSub1->pSrc->nSrc!=1
|| pSub1->pSrc->nSrc<1
){
return 0;
}
testcase( pSub1->pSrc->nSrc>1 );
}
/* Restriction 18. */
if( p->pOrderBy ){
int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
if( p->pOrderBy->a[ii].iCol==0 ) return 0;
if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0;
}
}
}
@@ -4494,7 +4501,7 @@ select_end:
return rc;
}
#if defined(SQLITE_DEBUG)
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
/*
** Generate a human-readable description of a the Select object.
*/
+15 -2
View File
@@ -1405,6 +1405,7 @@ static char zHelp[] =
" If TABLE specified, only list tables matching\n"
" LIKE pattern TABLE.\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
".vfsname ?AUX? Print the name of the VFS stack\n"
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
;
@@ -2344,10 +2345,22 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
printf("SQLite %s %s\n",
printf("SQLite %s %s\n" /*extra-version-info*/,
sqlite3_libversion(), sqlite3_sourceid());
}else
if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){
const char *zDbName = nArg==2 ? azArg[1] : "main";
char *zVfsName = 0;
if( p->db ){
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
if( zVfsName ){
printf("%s\n", zVfsName);
sqlite3_free(zVfsName);
}
}
}else
if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
int j;
assert( nArg<=ArraySize(azArg) );
@@ -2941,7 +2954,7 @@ int main(int argc, char **argv){
char *zHistory = 0;
int nHistory;
printf(
"SQLite version %s %.19s\n"
"SQLite version %s %.19s\n" /*extra-version-info*/
"Enter \".help\" for instructions\n"
"Enter SQL statements terminated with a \";\"\n",
sqlite3_libversion(), sqlite3_sourceid()
+70 -23
View File
@@ -504,7 +504,14 @@ int sqlite3_exec(
** first then the size of the file is extended, never the other
** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
** information is written to disk in the same order as calls
** to xWrite().
** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that
** after reboot following a crash or power loss, the value of
** each byte in a file is a value that was actually written
** into that byte at some point. In other words, a crash will
** not cause unwritten bytes of the file to change nor introduce
** randomness into a file nor zero out parts of the file, and any byte of
** a file that are never written will not change values due to
** writes to nearby bytes.
*/
#define SQLITE_IOCAP_ATOMIC 0x00000001
#define SQLITE_IOCAP_ATOMIC512 0x00000002
@@ -518,6 +525,7 @@ int sqlite3_exec(
#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
/*
** CAPI3REF: File Locking Levels
@@ -767,22 +775,44 @@ struct sqlite3_io_methods {
** WAL mode. If the integer is -1, then it is overwritten with the current
** WAL persistence setting.
**
** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting
** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the
** xDeviceCharacteristics methods. The fourth parameter to
** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage
** mode. If the integer is -1, then it is overwritten with the current
** zero-damage mode setting.
**
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
** a write transaction to indicate that, unless it is rolled back for some
** reason, the entire database file will be overwritten by the current
** transaction. This is used by VACUUM operations.
**
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
** all [VFSes] in the VFS stack. The names are of all VFS shims and the
** final bottom-level VFS are written into memory obtained from
** [sqlite3_malloc()] and the result is stored in the char* variable
** that the fourth parameter of [sqlite3_file_control()] points to.
** The caller is responsible for freeing the memory when done. As with
** all file-control actions, there is no guarantee that this will actually
** do anything. Callers should initialize the char* variable to a NULL
** pointer in case this file-control is not implemented. This file-control
** is intended for diagnostic use only.
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
#define SQLITE_SET_LOCKPROXYFILE 3
#define SQLITE_LAST_ERRNO 4
#define SQLITE_FCNTL_SIZE_HINT 5
#define SQLITE_FCNTL_CHUNK_SIZE 6
#define SQLITE_FCNTL_FILE_POINTER 7
#define SQLITE_FCNTL_SYNC_OMITTED 8
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
#define SQLITE_FCNTL_PERSIST_WAL 10
#define SQLITE_FCNTL_OVERWRITE 11
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
#define SQLITE_SET_LOCKPROXYFILE 3
#define SQLITE_LAST_ERRNO 4
#define SQLITE_FCNTL_SIZE_HINT 5
#define SQLITE_FCNTL_CHUNK_SIZE 6
#define SQLITE_FCNTL_FILE_POINTER 7
#define SQLITE_FCNTL_SYNC_OMITTED 8
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
#define SQLITE_FCNTL_PERSIST_WAL 10
#define SQLITE_FCNTL_OVERWRITE 11
#define SQLITE_FCNTL_VFSNAME 12
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
/*
** CAPI3REF: Mutex Handle
@@ -2596,21 +2626,37 @@ int sqlite3_open_v2(
/*
** CAPI3REF: Obtain Values For URI Parameters
**
** This is a utility routine, useful to VFS implementations, that checks
** These are utility routines, useful to VFS implementations, that check
** to see if a database file was a URI that contained a specific query
** parameter, and if so obtains the value of the query parameter.
** parameter, and if so obtains the value of that query parameter.
**
** The zFilename argument is the filename pointer passed into the xOpen()
** method of a VFS implementation. The zParam argument is the name of the
** query parameter we seek. This routine returns the value of the zParam
** parameter if it exists. If the parameter does not exist, this routine
** returns a NULL pointer.
** If F is the filename pointer passed into the xOpen() method of a VFS
** implementation and P is the name of the query parameter, then
** sqlite3_uri_parameter(F,P) returns the value of the P
** parameter if it exists or a NULL pointer if P does not appear as a
** query parameter on F. If P is a query parameter of F
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
** a pointer to an empty string.
**
** If the zFilename argument to this function is not a pointer that SQLite
** passed into the xOpen VFS method, then the behavior of this routine
** is undefined and probably undesirable.
** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean
** parameter and returns true (1) or false (0) according to the value
** of P. The value of P is true if it is "yes" or "true" or "on" or
** a non-zero number and is false otherwise. If P is not a query parameter
** on F then sqlite3_uri_boolean(F,P,B) returns (B!=0).
**
** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
** 64-bit signed integer and returns that integer, or D if P does not
** exist. If the value of P is something other than an integer, then
** zero is returned.
**
** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and
** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and
** is not a pathname pointer that SQLite passed into the xOpen VFS method,
** then the behavior of this routine is undefined and probably undesirable.
*/
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
/*
@@ -4596,7 +4642,8 @@ int sqlite3_db_release_memory(sqlite3*);
** is advisory only.
**
** ^The return value from sqlite3_soft_heap_limit64() is the size of
** the soft heap limit prior to the call. ^If the argument N is negative
** the soft heap limit prior to the call, or negative in the case of an
** error. ^If the argument N is negative
** then no change is made to the soft heap limit. Hence, the current
** size of the soft heap limit can be determined by invoking
** sqlite3_soft_heap_limit64() with a negative argument.
+14 -6
View File
@@ -125,6 +125,14 @@
#endif
#endif
/*
** Powersafe overwrite is on by default. But can be turned off using
** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option.
*/
#ifndef SQLITE_POWERSAFE_OVERWRITE
# define SQLITE_POWERSAFE_OVERWRITE 1
#endif
/*
** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1.
** It determines whether or not the features related to
@@ -1707,10 +1715,10 @@ struct Expr {
#define EP_FixedDest 0x0200 /* Result needed in a specific register */
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */
#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */
#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
/*
** The following are the meanings of bits in the Expr.flags2 field.
@@ -1772,7 +1780,7 @@ struct ExprList {
char *zSpan; /* Original text of the expression */
u8 sortOrder; /* 1 for DESC or 0 for ASC */
u8 done; /* A flag to indicate when processing is finished */
u16 iCol; /* For ORDER BY, column number in result set */
u16 iOrderByCol; /* For ORDER BY, column number in result set */
u16 iAlias; /* Index into Parse.aAlias[] for zName */
} *a; /* One entry for each expression */
};
@@ -2653,7 +2661,7 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
#endif
/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */
#if defined(SQLITE_DEBUG)
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
void sqlite3ExplainBegin(Vdbe*);
void sqlite3ExplainPrintf(Vdbe*, const char*, ...);
void sqlite3ExplainNL(Vdbe*);
+8
View File
@@ -3001,6 +3001,14 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}else{
flags &= ~SQLITE_OPEN_FULLMUTEX;
}
}else if( strcmp(zArg, "-uri")==0 ){
int b;
if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
if( b ){
flags |= SQLITE_OPEN_URI;
}else{
flags &= ~SQLITE_OPEN_URI;
}
}else{
Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0);
return TCL_ERROR;
+67
View File
@@ -5235,6 +5235,71 @@ static int file_control_persist_wal(
return TCL_OK;
}
/*
** tclcmd: file_control_powersafe_overwrite DB PSOW-FLAG
**
** This TCL command runs the sqlite3_file_control interface with
** the SQLITE_FCNTL_POWERSAFE_OVERWRITE opcode.
*/
static int file_control_powersafe_overwrite(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
int rc;
int b;
char z[100];
if( objc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetStringFromObj(objv[0], 0), " DB FLAG", 0);
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[2], &b) ) return TCL_ERROR;
rc = sqlite3_file_control(db,NULL,SQLITE_FCNTL_POWERSAFE_OVERWRITE,(void*)&b);
sqlite3_snprintf(sizeof(z), z, "%d %d", rc, b);
Tcl_AppendResult(interp, z, (char*)0);
return TCL_OK;
}
/*
** tclcmd: file_control_vfsname DB ?AUXDB?
**
** Return a string that describes the stack of VFSes.
*/
static int file_control_vfsname(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
const char *zDbName = "main";
char *zVfsName = 0;
if( objc!=2 && objc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetStringFromObj(objv[0], 0), " DB ?AUXDB?", 0);
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
return TCL_ERROR;
}
if( objc==3 ){
zDbName = Tcl_GetString(objv[2]);
}
sqlite3_file_control(db, zDbName, SQLITE_FCNTL_VFSNAME,(void*)&zVfsName);
Tcl_AppendResult(interp, zVfsName, (char*)0);
sqlite3_free(zVfsName);
return TCL_OK;
}
/*
** tclcmd: sqlite3_vfs_list
@@ -6060,6 +6125,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
{ "file_control_persist_wal", file_control_persist_wal, 0 },
{ "file_control_powersafe_overwrite",file_control_powersafe_overwrite,0},
{ "file_control_vfsname", file_control_vfsname, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
+12 -11
View File
@@ -705,17 +705,18 @@ static int processDevSymArgs(
char *zName;
int iValue;
} aFlag[] = {
{ "atomic", SQLITE_IOCAP_ATOMIC },
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
{ "atomic", SQLITE_IOCAP_ATOMIC },
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
{ 0, 0 }
};
+1 -1
View File
@@ -391,7 +391,7 @@ static int openTransaction(jt_file *pMain, jt_file *pJournal){
while( rc==SQLITE_OK && iTrunk>0 ){
u32 nLeaf;
u32 iLeaf;
sqlite3_int64 iOff = (iTrunk-1)*pMain->nPagesize;
sqlite3_int64 iOff = (i64)(iTrunk-1)*pMain->nPagesize;
rc = sqlite3OsRead(p, aData, pMain->nPagesize, iOff);
nLeaf = decodeUint32(&aData[4]);
for(iLeaf=0; rc==SQLITE_OK && iLeaf<nLeaf; iLeaf++){
+236 -201
View File
@@ -81,6 +81,9 @@
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */
/* First chunk for rollback journal files */
#define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
/************************ Shim Definitions ******************************/
@@ -97,7 +100,7 @@
#endif
/* This used to be the default limit on number of chunks, but
** it is no longer enforced. There is currently no limit to the
** it is no longer enforced. There is currently no limit to the
** number of chunks.
**
** May be changed by calling the xFileControl() interface.
@@ -130,7 +133,8 @@ struct multiplexGroup {
int nName; /* Length of base filename */
int flags; /* Flags used for original opening */
unsigned int szChunk; /* Chunk size used for this group */
int bEnabled; /* TRUE to use Multiplex VFS for this file */
unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */
unsigned char bTruncate; /* TRUE to enable truncation of databases */
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
};
@@ -214,68 +218,36 @@ static int multiplexStrlen30(const char *z){
}
/*
** Create a temporary file name in zBuf. zBuf must be big enough to
** hold at pOrigVfs->mxPathname characters. This function departs
** from the traditional temporary name generation in the os_win
** and os_unix VFS in several ways, but is necessary so that
** the file name is known for temporary files (like those used
** during vacuum.)
**
** N.B. This routine assumes your underlying VFS is ok with using
** "/" as a directory seperator. This is the default for UNIXs
** and is allowed (even mixed) for most versions of Windows.
** Generate the file-name for chunk iChunk of the group with base name
** zBase. The file-name is written to buffer zOut before returning. Buffer
** zOut must be allocated by the caller so that it is at least (nBase+4)
** bytes in size, where nBase is the length of zBase, not including the
** nul-terminator.
*/
static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
static char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
int i,j;
int attempts = 0;
int exists = 0;
int rc = SQLITE_ERROR;
/* Check that the output buffer is large enough for
** pVfs->mxPathname characters.
*/
if( pOrigVfs->mxPathname <= nBuf ){
char *zTmp = sqlite3_malloc(pOrigVfs->mxPathname);
if( zTmp==0 ) return SQLITE_NOMEM;
/* sqlite3_temp_directory should always be less than
** pVfs->mxPathname characters.
*/
sqlite3_snprintf(pOrigVfs->mxPathname,
zTmp,
"%s/",
sqlite3_temp_directory ? sqlite3_temp_directory : ".");
rc = pOrigVfs->xFullPathname(pOrigVfs, zTmp, nBuf, zBuf);
sqlite3_free(zTmp);
if( rc ) return rc;
/* Check that the output buffer is large enough for the temporary file
** name.
*/
j = multiplexStrlen30(zBuf);
if( (j + 8 + 1 + 3 + 1) <= nBuf ){
/* Make 3 attempts to generate a unique name. */
do {
attempts++;
sqlite3_randomness(8, &zBuf[j]);
for(i=0; i<8; i++){
unsigned char uc = (unsigned char)zBuf[j+i];
zBuf[j+i] = (char)zChars[uc%(sizeof(zChars)-1)];
}
memcpy(&zBuf[j+i], ".tmp", 5);
rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists);
} while ( (rc==SQLITE_OK) && exists && (attempts<3) );
if( rc==SQLITE_OK && exists ){
rc = SQLITE_ERROR;
}
static void multiplexFilename(
const char *zBase, /* Filename for chunk 0 */
int nBase, /* Size of zBase in bytes (without \0) */
int flags, /* Flags used to open file */
int iChunk, /* Chunk to generate filename for */
char *zOut /* Buffer to write generated name to */
){
memcpy(zOut, zBase, nBase+1);
if( iChunk!=0 && iChunk!=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
int n = nBase;
#ifdef SQLITE_ENABLE_8_3_NAMES
int i;
for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}
if( i>=n-4 ) n = i+1;
if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
/* The extensions on overflow files for main databases are 001, 002,
** 003 and so forth. To avoid name collisions, add 400 to the
** extensions of journal files so that they are 401, 402, 403, ....
*/
iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
}
#endif
sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);
}
return rc;
}
/* Compute the filename for the iChunk-th chunk
@@ -291,48 +263,72 @@ static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
pGroup->aReal = p;
pGroup->nReal = iChunk+1;
}
if( pGroup->aReal[iChunk].z==0 ){
if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
char *z;
int n = pGroup->nName;
pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+4 );
if( z==0 ){
return SQLITE_NOMEM;
}
memcpy(z, pGroup->zName, n+1);
if( iChunk>0 ){
#ifdef SQLITE_ENABLE_8_3_NAMES
int i;
for(i=n-1; i>0 && i>=n-4 && z[i]!='.'; i--){}
if( i>=n-4 ) n = i+1;
#endif
sqlite3_snprintf(4,&z[n],"%03d",iChunk);
}
multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z);
}
return SQLITE_OK;
}
/* Translate an sqlite3_file* that is really a multiplexGroup* into
** the sqlite3_file* for the underlying original VFS.
**
** For chunk 0, the pGroup->flags determines whether or not a new file
** is created if it does not already exist. For chunks 1 and higher, the
** file is created only if createFlag is 1.
*/
static sqlite3_file *multiplexSubOpen(
multiplexGroup *pGroup,
int iChunk,
int *rc,
int *pOutFlags
multiplexGroup *pGroup, /* The multiplexor group */
int iChunk, /* Which chunk to open. 0==original file */
int *rc, /* Result code in and out */
int *pOutFlags, /* Output flags */
int createFlag /* True to create if iChunk>0 */
){
sqlite3_file *pSubOpen = 0;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
#ifdef SQLITE_ENABLE_8_3_NAMES
/* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
** part of a database journal are named db.401, db.402, and so on. A
** database may therefore not grow to larger than 400 chunks. Attempting
** to open chunk 401 indicates the database is full. */
if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
*rc = SQLITE_FULL;
return 0;
}
#endif
*rc = multiplexSubFilename(pGroup, iChunk);
if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
int flags, bExists;
createFlag = (pGroup->flags & SQLITE_OPEN_CREATE)!=0;
flags = pGroup->flags;
if( createFlag ){
flags |= SQLITE_OPEN_CREATE;
}else if( iChunk==0 ){
/* Fall through */
}else if( pGroup->aReal[iChunk].z==0 ){
return 0;
}else{
*rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
SQLITE_ACCESS_EXISTS, &bExists);
if( *rc || !bExists ) return 0;
flags &= ~SQLITE_OPEN_CREATE;
}
pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
if( pSubOpen==0 ){
*rc = SQLITE_NOMEM;
*rc = SQLITE_IOERR_NOMEM;
return 0;
}
pGroup->aReal[iChunk].p = pSubOpen;
*rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
pGroup->flags, pOutFlags);
if( *rc!=SQLITE_OK ){
flags, pOutFlags);
if( (*rc)!=SQLITE_OK ){
sqlite3_free(pSubOpen);
pGroup->aReal[iChunk].p = 0;
return 0;
@@ -341,6 +337,25 @@ static sqlite3_file *multiplexSubOpen(
return pSubOpen;
}
/*
** Return the size, in bytes, of chunk number iChunk. If that chunk
** does not exist, then return 0. This function does not distingish between
** non-existant files and zero-length files.
*/
static sqlite3_int64 multiplexSubSize(
multiplexGroup *pGroup, /* The multiplexor group */
int iChunk, /* Which chunk to open. 0==original file */
int *rc /* Result code in and out */
){
sqlite3_file *pSub;
sqlite3_int64 sz = 0;
pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);
if( pSub==0 ) return 0;
*rc = pSub->pMethods->xFileSize(pSub, &sz);
return sz;
}
/*
** This is the implementation of the multiplex_control() SQL function.
*/
@@ -408,7 +423,9 @@ static void multiplexSubClose(
sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
if( pSubOpen ){
pSubOpen->pMethods->xClose(pSubOpen);
if( pOrigVfs ) pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
if( pOrigVfs && pGroup->aReal[iChunk].z ){
pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
}
sqlite3_free(pGroup->aReal[iChunk].p);
}
sqlite3_free(pGroup->aReal[iChunk].z);
@@ -454,6 +471,7 @@ static int multiplexOpen(
UNUSED_PARAMETER(pVfs);
memset(pConn, 0, pVfs->szOsFile);
assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );
/* We need to create a group structure and manage
** access to this group of files.
@@ -461,23 +479,9 @@ static int multiplexOpen(
multiplexEnter();
pMultiplexOpen = (multiplexConn*)pConn;
/* If the second argument to this function is NULL, generate a
** temporary file name to use. This will be handled by the
** original xOpen method. We just need to allocate space for
** it.
*/
if( !zName ){
zName = zToFree = sqlite3_malloc( pOrigVfs->mxPathname + 10 );
if( zName==0 ){
rc = SQLITE_NOMEM;
}else{
rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, zToFree);
}
}
if( rc==SQLITE_OK ){
/* allocate space for group */
nName = multiplexStrlen30(zName);
nName = zName ? multiplexStrlen30(zName) : 0;
sz = sizeof(multiplexGroup) /* multiplexGroup */
+ nName + 1; /* zName */
pGroup = sqlite3_malloc( sz );
@@ -488,62 +492,84 @@ static int multiplexOpen(
if( rc==SQLITE_OK ){
/* assign pointers to extra space allocated */
char *p = (char *)&pGroup[1];
pMultiplexOpen->pGroup = pGroup;
memset(pGroup, 0, sz);
pMultiplexOpen->pGroup = pGroup;
pGroup->bEnabled = -1;
pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;
if( flags & SQLITE_OPEN_URI ){
const char *zChunkSize;
zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
if( zChunkSize ){
unsigned int n = 0;
int i;
for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
n = n*10 + zChunkSize[i] - '0';
}
if( n>0 ){
pGroup->szChunk = (n+0xffff)&~0xffff;
}else{
/* A zero or negative chunksize disabled the multiplexor */
pGroup->bEnabled = 0;
}
pGroup->bTruncate = sqlite3_uri_boolean(zName, "truncate",
(flags & SQLITE_OPEN_MAIN_DB)==0);
pGroup->szChunk = sqlite3_uri_int64(zName, "chunksize",
SQLITE_MULTIPLEX_CHUNK_SIZE);
pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;
if( zName ){
char *p = (char *)&pGroup[1];
pGroup->zName = p;
memcpy(pGroup->zName, zName, nName+1);
pGroup->nName = nName;
}
if( pGroup->bEnabled ){
/* Make sure that the chunksize is such that the pending byte does not
** falls at the end of a chunk. A region of up to 64K following
** the pending byte is never written, so if the pending byte occurs
** near the end of a chunk, that chunk will be too small. */
extern int sqlite3PendingByte;
while( (sqlite3PendingByte % pGroup->szChunk)>=(pGroup->szChunk-65536) ){
pGroup->szChunk += 65536;
}
}
pGroup->zName = p;
/* save off base filename, name length, and original open flags */
memcpy(pGroup->zName, zName, nName+1);
pGroup->nName = nName;
pGroup->flags = flags;
rc = multiplexSubFilename(pGroup, 1);
if( rc==SQLITE_OK ){
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;
}
if( pSubOpen ){
int exists, rc2, rc3;
if( rc==SQLITE_OK ){
sqlite3_int64 sz;
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( rc2==SQLITE_OK ){
/* If the first overflow file exists and if the size of the main file
** is different from the chunk size, that means the chunk size is set
** set incorrectly. So fix it.
**
** Or, if the first overflow file does not exist and the main file is
** larger than the chunk size, that means the chunk size is too small.
** But we have no way of determining the intended chunk size, so
** just disable the multiplexor all togethre.
*/
rc3 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
SQLITE_ACCESS_EXISTS, &exists);
if( rc3==SQLITE_OK && exists && sz==(sz&0xffff0000) && sz>0
&& sz!=pGroup->szChunk ){
pGroup->szChunk = sz;
}else if( rc3==SQLITE_OK && !exists && sz>pGroup->szChunk ){
pGroup->bEnabled = 0;
rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( rc==SQLITE_OK && zName ){
int bExists;
if( sz==0 ){
if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
/* If opening a main journal file and the first chunk is zero
** bytes in size, delete any subsequent chunks from the
** file-system. */
int iChunk = 1;
do {
rc = pOrigVfs->xAccess(pOrigVfs,
pGroup->aReal[iChunk].z, SQLITE_ACCESS_EXISTS, &bExists
);
if( rc==SQLITE_OK && bExists ){
rc = pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
if( rc==SQLITE_OK ){
rc = multiplexSubFilename(pGroup, ++iChunk);
}
}
}while( rc==SQLITE_OK && bExists );
}
}else{
/* If the first overflow file exists and if the size of the main file
** is different from the chunk size, that means the chunk size is set
** set incorrectly. So fix it.
**
** Or, if the first overflow file does not exist and the main file is
** larger than the chunk size, that means the chunk size is too small.
** But we have no way of determining the intended chunk size, so
** just disable the multiplexor all togethre.
*/
rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
SQLITE_ACCESS_EXISTS, &bExists);
bExists = multiplexSubSize(pGroup, 1, &rc)>0;
if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
&& sz!=pGroup->szChunk ){
pGroup->szChunk = sz;
}else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){
pGroup->bEnabled = 0;
}
}
}
}
if( rc==SQLITE_OK ){
if( pSubOpen->pMethods->iVersion==1 ){
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
}else{
@@ -572,8 +598,33 @@ static int multiplexDelete(
const char *zName, /* Name of file to delete */
int syncDir
){
int rc;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
return pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
if( rc==SQLITE_OK ){
/* If the main chunk was deleted successfully, also delete any subsequent
** chunks - starting with the last (highest numbered).
*/
int nName = strlen(zName);
char *z;
z = sqlite3_malloc(nName + 4);
if( z==0 ){
rc = SQLITE_IOERR_NOMEM;
}else{
int iChunk = 0;
int bExists;
do{
multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, ++iChunk, z);
rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);
}while( rc==SQLITE_OK && bExists );
while( rc==SQLITE_OK && iChunk>1 ){
multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);
rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
}
}
sqlite3_free(z);
}
return rc;
}
static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
@@ -650,7 +701,7 @@ static int multiplexRead(
int rc = SQLITE_OK;
multiplexEnter();
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_READ;
}else{
@@ -659,7 +710,7 @@ static int multiplexRead(
}else{
while( iAmt > 0 ){
int i = (int)(iOfst / pGroup->szChunk);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
if( pSubOpen ){
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
if( extra<0 ) extra = 0;
@@ -695,16 +746,16 @@ static int multiplexWrite(
int rc = SQLITE_OK;
multiplexEnter();
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_WRITE;
}else{
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
}
}else{
while( iAmt > 0 ){
while( rc==SQLITE_OK && iAmt>0 ){
int i = (int)(iOfst / pGroup->szChunk);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
if( pSubOpen ){
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
pGroup->szChunk;
@@ -712,13 +763,9 @@ static int multiplexWrite(
iAmt -= extra;
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
iOfst % pGroup->szChunk);
if( rc!=SQLITE_OK ) break;
pBuf = (char *)pBuf + iAmt;
iOfst += iAmt;
iAmt = extra;
}else{
rc = SQLITE_IOERR_WRITE;
break;
}
}
}
@@ -736,28 +783,35 @@ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
int rc = SQLITE_OK;
multiplexEnter();
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_TRUNCATE;
}else{
rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
}
}else{
int rc2;
int i;
int iBaseGroup = (int)(size / pGroup->szChunk);
sqlite3_file *pSubOpen;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
/* delete the chunks above the truncate limit */
for(i=(int)(size / pGroup->szChunk)+1; i<pGroup->nReal; i++){
multiplexSubClose(pGroup, i, pOrigVfs);
for(i = pGroup->nReal-1; i>iBaseGroup && rc==SQLITE_OK; i--){
if( pGroup->bTruncate ){
multiplexSubClose(pGroup, i, pOrigVfs);
}else{
pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);
if( pSubOpen ){
rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);
}
}
}
pSubOpen = multiplexSubOpen(pGroup, (int)(size/pGroup->szChunk), &rc2,0);
if( pSubOpen ){
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
if( rc2!=SQLITE_OK ) rc = rc2;
}else{
rc = SQLITE_IOERR_TRUNCATE;
if( rc==SQLITE_OK ){
pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);
if( pSubOpen ){
rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
}
}
if( rc ) rc = SQLITE_IOERR_TRUNCATE;
}
multiplexLeave();
return rc;
@@ -789,47 +843,21 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
int rc2;
int i;
multiplexEnter();
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_FSTAT;
}else{
rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
}
}else{
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;
*pSize = 0;
for(i=0; 1; i++){
sqlite3_file *pSubOpen = 0;
int exists = 0;
rc = multiplexSubFilename(pGroup, i);
if( rc ) break;
rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[i].z,
SQLITE_ACCESS_EXISTS, &exists);
if( rc2==SQLITE_OK && exists){
/* if it exists, open it */
pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
}else{
/* stop at first "gap" */
break;
}
if( pSubOpen ){
sqlite3_int64 sz;
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
if( rc2!=SQLITE_OK ){
rc = rc2;
}else{
if( sz>pGroup->szChunk ){
rc = SQLITE_IOERR_FSTAT;
}
*pSize += sz;
}
}else{
break;
}
for(i=0; rc==SQLITE_OK; i++){
sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);
if( sz==0 ) break;
*pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
}
}
multiplexLeave();
@@ -841,7 +869,7 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
static int multiplexLock(sqlite3_file *pConn, int lock){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xLock(pSubOpen, lock);
}
@@ -853,7 +881,7 @@ static int multiplexLock(sqlite3_file *pConn, int lock){
static int multiplexUnlock(sqlite3_file *pConn, int lock){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
}
@@ -865,7 +893,7 @@ static int multiplexUnlock(sqlite3_file *pConn, int lock){
static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
}
@@ -913,9 +941,12 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
rc = SQLITE_OK;
break;
default:
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
*(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);
}
}
break;
}
@@ -927,8 +958,8 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
static int multiplexSectorSize(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
if( pSubOpen ){
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
return pSubOpen->pMethods->xSectorSize(pSubOpen);
}
return DEFAULT_SECTOR_SIZE;
@@ -939,7 +970,7 @@ static int multiplexSectorSize(sqlite3_file *pConn){
static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
}
@@ -957,7 +988,7 @@ static int multiplexShmMap(
){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
}
@@ -974,7 +1005,7 @@ static int multiplexShmLock(
){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
}
@@ -986,7 +1017,7 @@ static int multiplexShmLock(
static void multiplexShmBarrier(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
pSubOpen->pMethods->xShmBarrier(pSubOpen);
}
@@ -997,7 +1028,7 @@ static void multiplexShmBarrier(sqlite3_file *pConn){
static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
}
@@ -1179,9 +1210,13 @@ static int test_multiplex_dump(
for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
pGroupTerm = Tcl_NewObj();
pGroup->zName[pGroup->nName] = '\0';
Tcl_ListObjAppendElement(interp, pGroupTerm,
if( pGroup->zName ){
pGroup->zName[pGroup->nName] = '\0';
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewStringObj(pGroup->zName, -1));
}else{
Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj());
}
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(pGroup->nName));
Tcl_ListObjAppendElement(interp, pGroupTerm,
+5 -1
View File
@@ -389,7 +389,11 @@ static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
*/
static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
VfslogFile *p = (VfslogFile *)pFile;
return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
*(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg);
}
return rc;
}
/*
+658 -31
View File
@@ -27,7 +27,7 @@
** files within the group is less than the new quota, then the write
** continues as if nothing had happened.
*/
#include "sqlite3.h"
#include "test_quota.h"
#include <string.h>
#include <assert.h>
@@ -111,6 +111,18 @@ struct quotaConn {
/* The underlying VFS sqlite3_file is appended to this object */
};
/*
** An instance of the following object records the state of an
** open file. This object is opaque to all users - the internal
** structure is only visible to the functions below.
*/
struct quota_FILE {
FILE *f; /* Open stdio file pointer */
sqlite3_int64 iOfst; /* Current offset into the file */
quotaFile *pFile; /* The file record in the quota system */
};
/************************* Global Variables **********************************/
/*
** All global variables used by this file are containing within the following
@@ -225,9 +237,11 @@ static void quotaGroupDeref(quotaGroup *pGroup){
**
** [^...] Matches one character not in the enclosed list.
**
** / Matches "/" or "\\"
**
*/
static int quotaStrglob(const char *zGlob, const char *z){
int c, c2;
int c, c2, cx;
int invert;
int seen;
@@ -244,8 +258,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
}
return (*z)!=0;
}
cx = (c=='/') ? '\\' : c;
while( (c2 = (*(z++)))!=0 ){
while( c2!=c ){
while( c2!=c && c2!=cx ){
c2 = *(z++);
if( c2==0 ) return 0;
}
@@ -283,6 +298,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
c2 = *(zGlob++);
}
if( c2==0 || (seen ^ invert)==0 ) return 0;
}else if( c=='/' ){
if( z[0]!='/' && z[0]!='\\' ) return 0;
z++;
}else{
if( c!=(*(z++)) ) return 0;
}
@@ -313,14 +331,131 @@ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
/* Find a file in a quota group and return a pointer to that file.
** Return NULL if the file is not in the group.
*/
static quotaFile *quotaFindFile(quotaGroup *pGroup, const char *zName){
static quotaFile *quotaFindFile(
quotaGroup *pGroup, /* Group in which to look for the file */
const char *zName, /* Full pathname of the file */
int createFlag /* Try to create the file if not found */
){
quotaFile *pFile = pGroup->pFiles;
while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
pFile = pFile->pNext;
}
if( pFile==0 && createFlag ){
int nName = strlen(zName);
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
if( pFile ){
memset(pFile, 0, sizeof(*pFile));
pFile->zFilename = (char*)&pFile[1];
memcpy(pFile->zFilename, zName, nName+1);
pFile->pNext = pGroup->pFiles;
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
pFile->ppPrev = &pGroup->pFiles;
pGroup->pFiles = pFile;
pFile->pGroup = pGroup;
}
}
return pFile;
}
/*
** Figure out if we are dealing with Unix, Windows, or some other
** operating system. After the following block of preprocess macros,
** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
** will defined to either 1 or 0. One of the four will be 1. The other
** three will be 0.
*/
#if defined(SQLITE_OS_OTHER)
# if SQLITE_OS_OTHER==1
# undef SQLITE_OS_UNIX
# define SQLITE_OS_UNIX 0
# undef SQLITE_OS_WIN
# define SQLITE_OS_WIN 0
# undef SQLITE_OS_OS2
# define SQLITE_OS_OS2 0
# else
# undef SQLITE_OS_OTHER
# endif
#endif
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
# define SQLITE_OS_OTHER 0
# ifndef SQLITE_OS_WIN
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) \
|| defined(__MINGW32__) || defined(__BORLANDC__)
# define SQLITE_OS_WIN 1
# define SQLITE_OS_UNIX 0
# define SQLITE_OS_OS2 0
# elif defined(__EMX__) || defined(_OS2) || defined(OS2) \
|| defined(_OS2_) || defined(__OS2__)
# define SQLITE_OS_WIN 0
# define SQLITE_OS_UNIX 0
# define SQLITE_OS_OS2 1
# else
# define SQLITE_OS_WIN 0
# define SQLITE_OS_UNIX 1
# define SQLITE_OS_OS2 0
# endif
# else
# define SQLITE_OS_UNIX 0
# define SQLITE_OS_OS2 0
# endif
#else
# ifndef SQLITE_OS_WIN
# define SQLITE_OS_WIN 0
# endif
#endif
#if SQLITE_OS_UNIX
# include <unistd.h>
#endif
#if SQLITE_OS_WIN
# include <windows.h>
# include <io.h>
#endif
/*
** Translate UTF8 to MBCS for use in fopen() calls. Return a pointer to the
** translated text.. Call quota_mbcs_free() to deallocate any memory
** used to store the returned pointer when done.
*/
static char *quota_utf8_to_mbcs(const char *zUtf8){
#if SQLITE_OS_WIN
int n; /* Bytes in zUtf8 */
int nWide; /* number of UTF-16 characters */
int nMbcs; /* Bytes of MBCS */
LPWSTR zTmpWide; /* The UTF16 text */
char *zMbcs; /* The MBCS text */
int codepage; /* Code page used by fopen() */
n = strlen(zUtf8);
nWide = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0);
if( nWide==0 ) return 0;
zTmpWide = (LPWSTR)sqlite3_malloc( (nWide+1)*sizeof(zTmpWide[0]) );
if( zTmpWide==0 ) return 0;
MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zTmpWide, nWide);
codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
nMbcs = WideCharToMultiByte(codepage, 0, zTmpWide, nWide, 0, 0, 0, 0);
zMbcs = nMbcs ? (char*)sqlite3_malloc( nMbcs+1 ) : 0;
if( zMbcs ){
WideCharToMultiByte(codepage, 0, zTmpWide, nWide, zMbcs, nMbcs, 0, 0);
}
sqlite3_free(zTmpWide);
return zMbcs;
#else
return (char*)zUtf8; /* No-op on unix */
#endif
}
/*
** Deallocate any memory allocated by quota_utf8_to_mbcs().
*/
static void quota_mbcs_free(char *zOld){
#if SQLITE_OS_WIN
sqlite3_free(zOld);
#else
/* No-op on unix */
#endif
}
/************************* VFS Method Wrappers *****************************/
/*
** This is the xOpen method used for the "quota" VFS.
@@ -364,25 +499,13 @@ static int quotaOpen(
pSubOpen = quotaSubOpen(pConn);
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
if( rc==SQLITE_OK ){
pFile = quotaFindFile(pGroup, zName);
pFile = quotaFindFile(pGroup, zName, 1);
if( pFile==0 ){
int nName = strlen(zName);
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
if( pFile==0 ){
quotaLeave();
pSubOpen->pMethods->xClose(pSubOpen);
return SQLITE_NOMEM;
}
memset(pFile, 0, sizeof(*pFile));
pFile->zFilename = (char*)&pFile[1];
memcpy(pFile->zFilename, zName, nName+1);
pFile->pNext = pGroup->pFiles;
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
pFile->ppPrev = &pGroup->pFiles;
pGroup->pFiles = pFile;
pFile->pGroup = pGroup;
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
quotaLeave();
pSubOpen->pMethods->xClose(pSubOpen);
return SQLITE_NOMEM;
}
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
pFile->nRef++;
pQuotaOpen->pFile = pFile;
if( pSubOpen->pMethods->iVersion==1 ){
@@ -423,7 +546,7 @@ static int quotaDelete(
quotaEnter();
pGroup = quotaGroupFind(zName);
if( pGroup ){
pFile = quotaFindFile(pGroup, zName);
pFile = quotaFindFile(pGroup, zName, 0);
if( pFile ){
if( pFile->nRef ){
pFile->deleteOnClose = 1;
@@ -455,7 +578,10 @@ static int quotaClose(sqlite3_file *pConn){
pFile->nRef--;
if( pFile->nRef==0 ){
quotaGroup *pGroup = pFile->pGroup;
if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
if( pFile->deleteOnClose ){
gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
quotaRemoveFile(pFile);
}
quotaGroupDeref(pGroup);
}
quotaLeave();
@@ -589,7 +715,13 @@ static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
*/
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
#if defined(SQLITE_FCNTL_VFSNAME)
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
*(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg);
}
#endif
return rc;
}
/* Pass xSectorSize requests through to the original VFS unchanged.
@@ -805,7 +937,8 @@ int sqlite3_quota_file(const char *zFilename){
int rc;
int outFlags = 0;
sqlite3_int64 iSize;
fd = sqlite3_malloc(gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+1);
fd = (sqlite3_file*)sqlite3_malloc(gQuota.sThisVfs.szOsFile +
gQuota.sThisVfs.mxPathname+1);
if( fd==0 ) return SQLITE_NOMEM;
zFull = gQuota.sThisVfs.szOsFile + (char*)fd;
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
@@ -823,7 +956,7 @@ int sqlite3_quota_file(const char *zFilename){
quotaEnter();
pGroup = quotaGroupFind(zFull);
if( pGroup ){
pFile = quotaFindFile(pGroup, zFull);
pFile = quotaFindFile(pGroup, zFull, 0);
if( pFile ) quotaRemoveFile(pFile);
}
quotaLeave();
@@ -832,6 +965,220 @@ int sqlite3_quota_file(const char *zFilename){
return rc;
}
/*
** Open a potentially quotaed file for I/O.
*/
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
quota_FILE *p = 0;
char *zFull = 0;
char *zFullTranslated;
int rc;
quotaGroup *pGroup;
quotaFile *pFile;
zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
if( zFull==0 ) return 0;
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
gQuota.sThisVfs.mxPathname+1, zFull);
if( rc ) goto quota_fopen_error;
p = (quota_FILE*)sqlite3_malloc(sizeof(*p));
if( p==0 ) goto quota_fopen_error;
memset(p, 0, sizeof(*p));
zFullTranslated = quota_utf8_to_mbcs(zFull);
if( zFullTranslated==0 ) goto quota_fopen_error;
p->f = fopen(zFullTranslated, zMode);
quota_mbcs_free(zFullTranslated);
if( p->f==0 ) goto quota_fopen_error;
quotaEnter();
pGroup = quotaGroupFind(zFull);
if( pGroup ){
pFile = quotaFindFile(pGroup, zFull, 1);
if( pFile==0 ){
quotaLeave();
goto quota_fopen_error;
}
pFile->nRef++;
p->pFile = pFile;
}
quotaLeave();
sqlite3_free(zFull);
return p;
quota_fopen_error:
sqlite3_free(zFull);
if( p && p->f ) fclose(p->f);
sqlite3_free(p);
return 0;
}
/*
** Read content from a quota_FILE
*/
size_t sqlite3_quota_fread(
void *pBuf, /* Store the content here */
size_t size, /* Size of each element */
size_t nmemb, /* Number of elements to read */
quota_FILE *p /* Read from this quota_FILE object */
){
return fread(pBuf, size, nmemb, p->f);
}
/*
** Write content into a quota_FILE. Invoke the quota callback and block
** the write if we exceed quota.
*/
size_t sqlite3_quota_fwrite(
void *pBuf, /* Take content to write from here */
size_t size, /* Size of each element */
size_t nmemb, /* Number of elements */
quota_FILE *p /* Write to this quota_FILE objecct */
){
sqlite3_int64 iOfst;
sqlite3_int64 iEnd;
sqlite3_int64 szNew;
quotaFile *pFile;
iOfst = ftell(p->f);
iEnd = iOfst + size*nmemb;
pFile = p->pFile;
if( pFile && pFile->iSize<iEnd ){
quotaGroup *pGroup = pFile->pGroup;
quotaEnter();
szNew = pGroup->iSize - pFile->iSize + iEnd;
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
if( pGroup->xCallback ){
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
pGroup->pArg);
}
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
nmemb = (iEnd - iOfst)/size;
iEnd = iOfst + size*nmemb;
szNew = pGroup->iSize - pFile->iSize + iEnd;
}
}
pGroup->iSize = szNew;
pFile->iSize = iEnd;
quotaLeave();
}
return fwrite(pBuf, size, nmemb, p->f);
}
/*
** Close an open quota_FILE stream.
*/
int sqlite3_quota_fclose(quota_FILE *p){
int rc;
quotaFile *pFile;
rc = fclose(p->f);
pFile = p->pFile;
if( pFile ){
quotaEnter();
pFile->nRef--;
if( pFile->nRef==0 ){
quotaGroup *pGroup = pFile->pGroup;
if( pFile->deleteOnClose ){
gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
quotaRemoveFile(pFile);
}
quotaGroupDeref(pGroup);
}
quotaLeave();
}
sqlite3_free(p);
return rc;
}
/*
** Flush memory buffers for a quota_FILE to disk.
*/
int sqlite3_quota_fflush(quota_FILE *p, int doFsync){
int rc;
rc = fflush(p->f);
if( rc==0 && doFsync ){
#if SQLITE_OS_UNIX
rc = fsync(fileno(p->f));
#endif
#if SQLITE_OS_WIN
rc = _commit(_fileno(p->f));
#endif
}
return rc!=0;
}
/*
** Seek on a quota_FILE stream.
*/
int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
return fseek(p->f, offset, whence);
}
/*
** rewind a quota_FILE stream.
*/
void sqlite3_quota_rewind(quota_FILE *p){
rewind(p->f);
}
/*
** Tell the current location of a quota_FILE stream.
*/
long sqlite3_quota_ftell(quota_FILE *p){
return ftell(p->f);
}
/*
** Remove a managed file. Update quotas accordingly.
*/
int sqlite3_quota_remove(const char *zFilename){
char *zFull; /* Full pathname for zFilename */
int nFull; /* Number of bytes in zFilename */
int rc; /* Result code */
quotaGroup *pGroup; /* Group containing zFilename */
quotaFile *pFile; /* A file in the group */
quotaFile *pNextFile; /* next file in the group */
int diff; /* Difference between filenames */
char c; /* First character past end of pattern */
zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
if( zFull==0 ) return SQLITE_NOMEM;
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
gQuota.sThisVfs.mxPathname+1, zFull);
if( rc ){
sqlite3_free(zFull);
return rc;
}
/* Figure out the length of the full pathname. If the name ends with
** / (or \ on windows) then remove the trailing /.
*/
nFull = strlen(zFull);
if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){
nFull--;
zFull[nFull] = 0;
}
quotaEnter();
pGroup = quotaGroupFind(zFull);
if( pGroup ){
for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){
pNextFile = pFile->pNext;
diff = memcmp(zFull, pFile->zFilename, nFull);
if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){
if( pFile->nRef ){
pFile->deleteOnClose = 1;
}else{
rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
quotaRemoveFile(pFile);
quotaGroupDeref(pGroup);
}
}
}
}
quotaLeave();
sqlite3_free(zFull);
return rc;
}
/***************************** Test Code ***********************************/
#ifdef SQLITE_TEST
@@ -1060,9 +1407,13 @@ static int test_quota_dump(
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewWideIntObj(pGroup->iSize));
for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
int i;
char zTemp[1000];
pFileTerm = Tcl_NewObj();
sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename);
for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; }
Tcl_ListObjAppendElement(interp, pFileTerm,
Tcl_NewStringObj(pFile->zFilename, -1));
Tcl_NewStringObj(zTemp, -1));
Tcl_ListObjAppendElement(interp, pFileTerm,
Tcl_NewWideIntObj(pFile->iSize));
Tcl_ListObjAppendElement(interp, pFileTerm,
@@ -1078,6 +1429,272 @@ static int test_quota_dump(
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_fopen FILENAME MODE
*/
static int test_quota_fopen(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zFilename; /* File pattern to configure */
const char *zMode; /* Mode string */
quota_FILE *p; /* Open string object */
char zReturn[50]; /* Name of pointer to return */
/* Process arguments */
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE");
return TCL_ERROR;
}
zFilename = Tcl_GetString(objv[1]);
zMode = Tcl_GetString(objv[2]);
p = sqlite3_quota_fopen(zFilename, zMode);
sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p);
Tcl_SetResult(interp, zReturn, TCL_VOLATILE);
return TCL_OK;
}
/* Defined in test1.c */
extern void *sqlite3TestTextToPtr(const char*);
/*
** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
*/
static int test_quota_fread(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
char *zBuf;
int sz;
int nElem;
int got;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
zBuf = (char*)sqlite3_malloc( sz*nElem + 1 );
if( zBuf==0 ){
Tcl_SetResult(interp, "out of memory", TCL_STATIC);
return TCL_ERROR;
}
got = sqlite3_quota_fread(zBuf, sz, nElem, p);
if( got<0 ) got = 0;
zBuf[got*sz] = 0;
Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
sqlite3_free(zBuf);
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
*/
static int test_quota_fwrite(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
char *zBuf;
int sz;
int nElem;
int got;
if( objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
zBuf = Tcl_GetString(objv[4]);
got = sqlite3_quota_fwrite(zBuf, sz, nElem, p);
Tcl_SetObjResult(interp, Tcl_NewIntObj(got));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_fclose HANDLE
*/
static int test_quota_fclose(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
int rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
rc = sqlite3_quota_fclose(p);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
*/
static int test_quota_fflush(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
int rc;
int doSync = 0;
if( objc!=2 && objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
if( objc==3 ){
if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR;
}
rc = sqlite3_quota_fflush(p, doSync);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
*/
static int test_quota_fseek(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
int ofst;
const char *zWhence;
int whence;
int rc;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR;
zWhence = Tcl_GetString(objv[3]);
if( strcmp(zWhence, "SEEK_SET")==0 ){
whence = SEEK_SET;
}else if( strcmp(zWhence, "SEEK_CUR")==0 ){
whence = SEEK_CUR;
}else if( strcmp(zWhence, "SEEK_END")==0 ){
whence = SEEK_END;
}else{
Tcl_AppendResult(interp,
"WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0);
return TCL_ERROR;
}
rc = sqlite3_quota_fseek(p, ofst, whence);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_rewind HANDLE
*/
static int test_quota_rewind(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
sqlite3_quota_rewind(p);
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_ftell HANDLE
*/
static int test_quota_ftell(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
sqlite3_int64 x;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
x = sqlite3_quota_ftell(p);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_remove FILENAME
*/
static int test_quota_remove(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zFilename; /* File pattern to configure */
int rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
return TCL_ERROR;
}
zFilename = Tcl_GetString(objv[1]);
rc = sqlite3_quota_remove(zFilename);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_glob PATTERN TEXT
**
** Test the glob pattern matching. Return 1 if TEXT matches PATTERN
** and return 0 if it does not.
*/
static int test_quota_glob(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zPattern; /* The glob pattern */
const char *zText; /* Text to compare agains the pattern */
int rc;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT");
return TCL_ERROR;
}
zPattern = Tcl_GetString(objv[1]);
zText = Tcl_GetString(objv[2]);
rc = quotaStrglob(zPattern, zText);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
}
/*
** This routine registers the custom TCL commands defined in this
** module. This should be the only procedure visible from outside
@@ -1089,10 +1706,20 @@ int Sqlitequota_Init(Tcl_Interp *interp){
Tcl_ObjCmdProc *xProc;
} aCmd[] = {
{ "sqlite3_quota_initialize", test_quota_initialize },
{ "sqlite3_quota_shutdown", test_quota_shutdown },
{ "sqlite3_quota_set", test_quota_set },
{ "sqlite3_quota_file", test_quota_file },
{ "sqlite3_quota_dump", test_quota_dump },
{ "sqlite3_quota_shutdown", test_quota_shutdown },
{ "sqlite3_quota_set", test_quota_set },
{ "sqlite3_quota_file", test_quota_file },
{ "sqlite3_quota_dump", test_quota_dump },
{ "sqlite3_quota_fopen", test_quota_fopen },
{ "sqlite3_quota_fread", test_quota_fread },
{ "sqlite3_quota_fwrite", test_quota_fwrite },
{ "sqlite3_quota_fclose", test_quota_fclose },
{ "sqlite3_quota_fflush", test_quota_fflush },
{ "sqlite3_quota_fseek", test_quota_fseek },
{ "sqlite3_quota_rewind", test_quota_rewind },
{ "sqlite3_quota_ftell", test_quota_ftell },
{ "sqlite3_quota_remove", test_quota_remove },
{ "sqlite3_quota_glob", test_quota_glob },
};
int i;
+209
View File
@@ -0,0 +1,209 @@
/*
** 2011 December 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 contains the interface definition for the quota a VFS shim.
**
** This particular shim enforces a quota system on files. One or more
** database files are in a "quota group" that is defined by a GLOB
** pattern. A quota is set for the combined size of all files in the
** the group. A quota of zero means "no limit". If the total size
** of all files in the quota group is greater than the limit, then
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
**
** However, before returning SQLITE_FULL, the write requests invoke
** a callback function that is configurable for each quota group.
** This callback has the opportunity to enlarge the quota. If the
** callback does enlarge the quota such that the total size of all
** files within the group is less than the new quota, then the write
** continues as if nothing had happened.
*/
#ifndef _QUOTA_H_
#include "sqlite3.h"
#include <stdio.h>
/* Make this callable from C++ */
#ifdef __cplusplus
extern "C" {
#endif
/*
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
** as the VFS that does the actual work. Use the default if
** zOrigVfsName==NULL.
**
** The quota VFS shim is named "quota". It will become the default
** VFS if makeDefault is non-zero.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
** during start-up.
*/
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault);
/*
** Shutdown the quota system.
**
** All SQLite database connections must be closed before calling this
** routine.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
** shutting down in order to free all remaining quota groups.
*/
int sqlite3_quota_shutdown(void);
/*
** Create or destroy a quota group.
**
** The quota group is defined by the zPattern. When calling this routine
** with a zPattern for a quota group that already exists, this routine
** merely updates the iLimit, xCallback, and pArg values for that quota
** group. If zPattern is new, then a new quota group is created.
**
** The zPattern is always compared against the full pathname of the file.
** Even if APIs are called with relative pathnames, SQLite converts the
** name to a full pathname before comparing it against zPattern. zPattern
** is a glob pattern with the following matching rules:
**
** '*' Matches any sequence of zero or more characters.
**
** '?' Matches exactly one character.
**
** [...] Matches one character from the enclosed list of
** characters. "]" can be part of the list if it is
** the first character. Within the list "X-Y" matches
** characters X or Y or any character in between the
** two. Ex: "[0-9]" matches any digit.
**
** [^...] Matches one character not in the enclosed list.
**
** / Matches either / or \. This allows glob patterns
** containing / to work on both unix and windows.
**
** Note that, unlike unix shell globbing, the directory separator "/"
** can match a wildcard. So, for example, the pattern "/abc/xyz/" "*"
** matches any files anywhere in the directory hierarchy beneath
** /abc/xyz.
**
** The glob algorithm works on bytes. Multi-byte UTF8 characters are
** matched as if each byte were a separate character.
**
** If the iLimit for a quota group is set to zero, then the quota group
** is disabled and will be deleted when the last database connection using
** the quota group is closed.
**
** Calling this routine on a zPattern that does not exist and with a
** zero iLimit is a no-op.
**
** A quota group must exist with a non-zero iLimit prior to opening
** database connections if those connections are to participate in the
** quota group. Creating a quota group does not affect database connections
** that are already open.
**
** The patterns that define the various quota groups should be distinct.
** If the same filename matches more than one quota group pattern, then
** the behavior of this package is undefined.
*/
int sqlite3_quota_set(
const char *zPattern, /* The filename pattern */
sqlite3_int64 iLimit, /* New quota to set for this quota group */
void (*xCallback)( /* Callback invoked when going over quota */
const char *zFilename, /* Name of file whose size increases */
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
sqlite3_int64 iSize, /* Total size of all files in the group */
void *pArg /* Client data */
),
void *pArg, /* client data passed thru to callback */
void (*xDestroy)(void*) /* Optional destructor for pArg */
);
/*
** Bring the named file under quota management, assuming its name matches
** the glob pattern of some quota group. Or if it is already under
** management, update its size. If zFilename does not match the glob
** pattern of any quota group, this routine is a no-op.
*/
int sqlite3_quota_file(const char *zFilename);
/*
** The following object serves the same role as FILE in the standard C
** library. It represents an open connection to a file on disk for I/O.
**
** A single quota_FILE should not be used by two or more threads at the
** same time. Multiple threads can be using different quota_FILE objects
** simultaneously, but not the same quota_FILE object.
*/
typedef struct quota_FILE quota_FILE;
/*
** Create a new quota_FILE object used to read and/or write to the
** file zFilename. The zMode parameter is as with standard library zMode.
*/
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
/*
** Perform I/O against a quota_FILE object. When doing writes, the
** quota mechanism may result in a short write, in order to prevent
** the sum of sizes of all files from going over quota.
*/
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
/*
** Flush all written content held in memory buffers out to disk.
** This is the equivalent of fflush() in the standard library.
**
** If the hardSync parameter is true (non-zero) then this routine
** also forces OS buffers to disk - the equivalent of fsync().
**
** This routine return zero on success and non-zero if something goes
** wrong.
*/
int sqlite3_quota_fflush(quota_FILE*, int hardSync);
/*
** Close a quota_FILE object and free all associated resources. The
** file remains under quota management.
*/
int sqlite3_quota_fclose(quota_FILE*);
/*
** Move the read/write pointer for a quota_FILE object. Or tell the
** current location of the read/write pointer.
*/
int sqlite3_quota_fseek(quota_FILE*, long, int);
void sqlite3_quota_rewind(quota_FILE*);
long sqlite3_quota_ftell(quota_FILE*);
/*
** Delete a file from the disk, if that file is under quota management.
** Adjust quotas accordingly.
**
** If zFilename is the name of a directory that matches one of the
** quota glob patterns, then all files under quota management that
** are contained within that directory are deleted.
**
** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
** When deleting a directory of files, if the deletion of any one
** file fails (for example due to an I/O error), then this routine
** returns immediately, with the error code, and does not try to
** delete any of the other files in the specified directory.
**
** All files are removed from quota management and deleted from disk.
** However, no attempt is made to remove empty directories.
**
** This routine is a no-op for files that are not under quota management.
*/
int sqlite3_quota_remove(const char *zFilename);
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* _QUOTA_H_ */
+1 -1
View File
@@ -369,7 +369,7 @@ static void statSizeAndOffset(StatCursor *pCsr){
/* The default page size and offset */
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
pCsr->iOffset = pCsr->szPage * (pCsr->iPageno - 1);
pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
/* If connected to a ZIPVFS backend, override the page size and
** offset with actual values obtained from ZIPVFS.
+13 -12
View File
@@ -1162,18 +1162,19 @@ static int testvfs_obj_cmd(
int iValue;
} aFlag[] = {
{ "default", -1 },
{ "atomic", SQLITE_IOCAP_ATOMIC },
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
{ "atomic", SQLITE_IOCAP_ATOMIC },
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
{ "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
{ 0, 0 }
};
Tcl_Obj *pRet;
@@ -1207,7 +1208,7 @@ static int testvfs_obj_cmd(
iNew |= aFlag[idx].iValue;
}
p->iDevchar = iNew;
p->iDevchar = iNew| 0x10000000;
}
pRet = Tcl_NewObj();
+8
View File
@@ -471,6 +471,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
}
case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break;
case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break;
case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break;
case SQLITE_FCNTL_PERSIST_WAL: zOp = "PERSIST_WAL"; break;
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
default: {
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
@@ -482,6 +486,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
pInfo->zVfsName, p->zFName, zOp);
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
*(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
pInfo->zVfsName, *(char**)pArg);
}
return rc;
}
+2 -3
View File
@@ -1169,12 +1169,11 @@ int sqlite3AbsInt32(int x){
** test.db-journal => test.nal
** test.db-wal => test.wal
** test.db-shm => test.shm
** test.db-mj7f3319fa => test.9fa
*/
void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
#if SQLITE_ENABLE_8_3_NAMES<2
const char *zOk;
zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names");
if( zOk && sqlite3GetBoolean(zOk) )
if( sqlite3_uri_boolean(zBaseFilename, "8_3_names") )
#endif
{
int i, sz;
+2
View File
@@ -337,6 +337,8 @@ struct Vdbe {
void *pFree; /* Free this when deleting the vdbe */
#ifdef SQLITE_DEBUG
FILE *trace; /* Write an execution trace here, if not NULL */
#endif
#ifdef SQLITE_ENABLE_TREE_EXPLAIN
Explain *pExplain; /* The explainer */
char *zExplain; /* Explanation of data structures */
#endif
+23 -8
View File
@@ -1833,16 +1833,31 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
sqlite3_file *pMaster = 0;
i64 offset = 0;
int res;
int retryCount = 0;
int nMainFile;
/* Select a master journal file name */
nMainFile = sqlite3Strlen30(zMainFile);
zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XX", zMainFile);
if( zMaster==0 ) return SQLITE_NOMEM;
do {
u32 iRandom;
sqlite3DbFree(db, zMaster);
sqlite3_randomness(sizeof(iRandom), &iRandom);
zMaster = sqlite3MPrintf(db, "%s-mj%08X", zMainFile, iRandom&0x7fffffff);
if( !zMaster ){
return SQLITE_NOMEM;
if( retryCount ){
if( retryCount>100 ){
sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
sqlite3OsDelete(pVfs, zMaster, 0);
break;
}else if( retryCount==1 ){
sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster);
}
}
retryCount++;
sqlite3_randomness(sizeof(iRandom), &iRandom);
sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X",
(iRandom>>8)&0xffffff, iRandom&0xff);
/* The antipenultimate character of the master journal name must
** be "9" to avoid name collisions when using 8+3 filenames. */
assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' );
sqlite3FileSuffix3(zMainFile, zMaster);
rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
}while( rc==SQLITE_OK && res );
@@ -2474,9 +2489,9 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
#if defined(SQLITE_DEBUG)
sqlite3_free(p->zExplain);
sqlite3_free(p->pExplain);
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
sqlite3DbFree(db, p->zExplain);
sqlite3DbFree(db, p->pExplain);
#endif
sqlite3DbFree(db, p);
}
+2 -2
View File
@@ -13,7 +13,7 @@
** This file contains code used to insert the values of host parameters
** (aka "wildcards") into the SQL text output by sqlite3_trace().
**
** The Vdbe explainer is also found here.
** The Vdbe parse-tree explainer is also found here.
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
@@ -160,7 +160,7 @@ char *sqlite3VdbeExpandSql(
** for the Vdbe.
*/
#if defined(SQLITE_DEBUG)
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
/*
** Allocate a new Explain object
+191 -84
View File
@@ -414,13 +414,18 @@ struct Wal {
u32 iCallback; /* Value to pass to log callback (or 0) */
i64 mxWalSize; /* Truncate WAL to this size upon reset */
int nWiData; /* Size of array apWiData */
int szFirstBlock; /* Size of first block written to WAL file */
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
u32 szPage; /* Database page size */
i16 readLock; /* Which read lock is being held. -1 for none */
u8 syncFlags; /* Flags to use to sync header writes */
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
u8 writeLock; /* True if in a write transaction */
u8 ckptLock; /* True if holding a checkpoint lock */
u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
u8 truncateOnCommit; /* True to truncate WAL file on commit */
u8 syncHeader; /* Fsync the WAL header if true */
u8 padToSectorBoundary; /* Pad transactions out to the next sector */
WalIndexHdr hdr; /* Wal-index header for current transaction */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
@@ -1093,6 +1098,7 @@ static int walIndexRecover(Wal *pWal){
int szPage; /* Page size according to the log */
u32 magic; /* Magic value read from WAL header */
u32 version; /* Magic value read from WAL header */
int isValid; /* True if this frame is valid */
/* Read in the WAL header. */
rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
@@ -1151,14 +1157,14 @@ static int walIndexRecover(Wal *pWal){
for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
u32 pgno; /* Database page number for frame */
u32 nTruncate; /* dbsize field from frame header */
int isValid; /* True if this frame is valid */
/* Read and decode the next log frame. */
iFrame++;
rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
if( rc!=SQLITE_OK ) break;
isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
if( !isValid ) break;
rc = walIndexAppend(pWal, ++iFrame, pgno);
rc = walIndexAppend(pWal, iFrame, pgno);
if( rc!=SQLITE_OK ) break;
/* If nTruncate is non-zero, this is a commit record. */
@@ -1281,6 +1287,8 @@ int sqlite3WalOpen(
pRet->readLock = -1;
pRet->mxWalSize = mxWalSize;
pRet->zWalName = zWalName;
pRet->syncHeader = 1;
pRet->padToSectorBoundary = 1;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
/* Open file handle on the write-ahead log file. */
@@ -1295,6 +1303,11 @@ int sqlite3WalOpen(
sqlite3OsClose(pRet->pWalFd);
sqlite3_free(pRet);
}else{
int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
pRet->padToSectorBoundary = 0;
}
*ppWal = pRet;
WALTRACE(("WAL%d: opened\n", pRet));
}
@@ -1782,22 +1795,20 @@ static int walCheckpoint(
}
/*
** Attempt to limit the WAL size to the size limit defined by
** PRAGMA journal_size_limit.
** If the WAL file is currently larger than nMax bytes in size, truncate
** it to exactly nMax bytes. If an error occurs while doing so, ignore it.
*/
static void walLimitSize(Wal *pWal){
if( pWal->mxWalSize>=0 ){
i64 sz;
int rx;
sqlite3BeginBenignMalloc();
rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
}
sqlite3EndBenignMalloc();
if( rx ){
sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
}
static void walLimitSize(Wal *pWal, i64 nMax){
i64 sz;
int rx;
sqlite3BeginBenignMalloc();
rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
if( rx==SQLITE_OK && (sz > nMax ) ){
rx = sqlite3OsTruncate(pWal->pWalFd, nMax);
}
sqlite3EndBenignMalloc();
if( rx ){
sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
}
}
@@ -1824,18 +1835,29 @@ int sqlite3WalClose(
*/
rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
if( rc==SQLITE_OK ){
int bPersistWal = -1;
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
}
rc = sqlite3WalCheckpoint(
pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
);
sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal);
if( rc==SQLITE_OK && bPersistWal!=1 ){
isDelete = 1;
}else{
walLimitSize(pWal);
if( rc==SQLITE_OK ){
int bPersist = -1;
sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist);
if( bPersist!=1 ){
/* Try to delete the WAL file if the checkpoint completed and
** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal
** mode (!bPersist) */
isDelete = 1;
}else if( pWal->mxWalSize>=0 ){
/* Try to truncate the WAL file to zero bytes if the checkpoint
** completed and fsynced (rc==SQLITE_OK) and we are in persistent
** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a
** non-negative value (pWal->mxWalSize>=0). Note that we truncate
** to zero bytes as truncating to the journal_size_limit might
** leave a corrupt WAL file on disk. */
walLimitSize(pWal, 0);
}
}
}
@@ -2444,6 +2466,7 @@ int sqlite3WalEndWriteTransaction(Wal *pWal){
if( pWal->writeLock ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
pWal->truncateOnCommit = 0;
}
return SQLITE_OK;
}
@@ -2578,7 +2601,6 @@ static int walRestartLog(Wal *pWal){
int i; /* Loop counter */
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
walLimitSize(pWal);
pWal->nCkpt++;
pWal->hdr.mxFrame = 0;
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
@@ -2607,6 +2629,74 @@ static int walRestartLog(Wal *pWal){
return rc;
}
/*
** Information about the current state of the WAL file and where
** the next fsync should occur - passed from sqlite3WalFrames() into
** walWriteToLog().
*/
typedef struct WalWriter {
Wal *pWal; /* The complete WAL information */
sqlite3_file *pFd; /* The WAL file to which we write */
sqlite3_int64 iSyncPoint; /* Fsync at this offset */
int syncFlags; /* Flags for the fsync */
int szPage; /* Size of one page */
} WalWriter;
/*
** Write iAmt bytes of content into the WAL file beginning at iOffset.
** Do a sync when crossing the p->iSyncPoint boundary.
**
** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
** first write the part before iSyncPoint, then sync, then write the
** rest.
*/
static int walWriteToLog(
WalWriter *p, /* WAL to write to */
void *pContent, /* Content to be written */
int iAmt, /* Number of bytes to write */
sqlite3_int64 iOffset /* Start writing at this offset */
){
int rc;
if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){
int iFirstAmt = (int)(p->iSyncPoint - iOffset);
rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset);
if( rc ) return rc;
iOffset += iFirstAmt;
iAmt -= iFirstAmt;
pContent = (void*)(iFirstAmt + (char*)pContent);
assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
rc = sqlite3OsSync(p->pFd, p->syncFlags);
if( rc ) return rc;
}
rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
return rc;
}
/*
** Write out a single frame of the WAL
*/
static int walWriteOneFrame(
WalWriter *p, /* Where to write the frame */
PgHdr *pPage, /* The page of the frame to be written */
int nTruncate, /* The commit flag. Usually 0. >0 for commit */
sqlite3_int64 iOffset /* Byte offset at which to write */
){
int rc; /* Result code from subfunctions */
void *pData; /* Data actually written */
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
#if defined(SQLITE_HAS_CODEC)
if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
#else
pData = pPage->pData;
#endif
walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
if( rc ) return rc;
/* Write the page data */
rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
return rc;
}
/*
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
@@ -2621,14 +2711,20 @@ int sqlite3WalFrames(
){
int rc; /* Used to catch return codes */
u32 iFrame; /* Next frame address */
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
PgHdr *p; /* Iterator to run through pList with. */
PgHdr *pLast = 0; /* Last frame in list */
int nLast = 0; /* Number of extra copies of last page */
int nExtra = 0; /* Number of extra copies of last page */
int szFrame; /* The size of a single frame */
i64 iOffset; /* Next byte to write in WAL file */
WalWriter w; /* The writer */
assert( pList );
assert( pWal->writeLock );
/* If this frame set completes a transaction, then nTruncate>0. If
** nTruncate==0 then this frame set does not complete the transaction. */
assert( (isCommit!=0)==(nTruncate!=0) );
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
{ int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}
WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
@@ -2656,7 +2752,7 @@ int sqlite3WalFrames(
sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
sqlite3Put4byte(&aWalHdr[8], szPage);
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
sqlite3_randomness(8, pWal->hdr.aSalt);
if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
@@ -2666,77 +2762,88 @@ int sqlite3WalFrames(
pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
pWal->hdr.aFrameCksum[0] = aCksum[0];
pWal->hdr.aFrameCksum[1] = aCksum[1];
pWal->truncateOnCommit = 1;
rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
if( rc!=SQLITE_OK ){
return rc;
}
/* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless
** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise
** an out-of-order write following a WAL restart could result in
** database corruption. See the ticket:
**
** http://localhost:591/sqlite/info/ff5be73dee
*/
if( pWal->syncHeader && sync_flags ){
rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
if( rc ) return rc;
}
}
assert( (int)pWal->szPage==szPage );
/* Write the log file. */
for(p=pList; p; p=p->pDirty){
u32 nDbsize; /* Db-size field for frame header */
i64 iOffset; /* Write offset in log file */
void *pData;
iOffset = walFrameOffset(++iFrame, szPage);
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
/* Populate and write the frame header */
nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0;
#if defined(SQLITE_HAS_CODEC)
if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
#else
pData = p->pData;
#endif
walEncodeFrame(pWal, p->pgno, nDbsize, pData, aFrame);
rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
if( rc!=SQLITE_OK ){
return rc;
}
/* Setup information needed to write frames into the WAL */
w.pWal = pWal;
w.pFd = pWal->pWalFd;
w.iSyncPoint = 0;
w.syncFlags = sync_flags;
w.szPage = szPage;
iOffset = walFrameOffset(iFrame+1, szPage);
szFrame = szPage + WAL_FRAME_HDRSIZE;
/* Write the page data */
rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset+sizeof(aFrame));
if( rc!=SQLITE_OK ){
return rc;
}
/* Write all frames into the log file exactly once */
for(p=pList; p; p=p->pDirty){
int nDbSize; /* 0 normally. Positive == commit flag */
iFrame++;
assert( iOffset==walFrameOffset(iFrame, szPage) );
nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
rc = walWriteOneFrame(&w, p, nDbSize, iOffset);
if( rc ) return rc;
pLast = p;
iOffset += szFrame;
}
/* Sync the log file if the 'isSync' flag was specified. */
if( sync_flags ){
i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd);
i64 iOffset = walFrameOffset(iFrame+1, szPage);
assert( isCommit );
assert( iSegment>0 );
iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment);
while( iOffset<iSegment ){
void *pData;
#if defined(SQLITE_HAS_CODEC)
if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM;
#else
pData = pLast->pData;
#endif
walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame);
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
if( rc!=SQLITE_OK ){
return rc;
/* If this is the end of a transaction, then we might need to pad
** the transaction and/or sync the WAL file.
**
** Padding and syncing only occur if this set of frames complete a
** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL
** or synchonous==OFF, then no padding or syncing are needed.
**
** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not
** needed and only the sync is done. If padding is needed, then the
** final frame is repeated (with its commit mark) until the next sector
** boundary is crossed. Only the part of the WAL prior to the last
** sector boundary is synced; the part of the last frame that extends
** past the sector boundary is written after the sync.
*/
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
if( pWal->padToSectorBoundary ){
int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
while( iOffset<w.iSyncPoint ){
rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
if( rc ) return rc;
iOffset += szFrame;
nExtra++;
}
iOffset += WAL_FRAME_HDRSIZE;
rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset);
if( rc!=SQLITE_OK ){
return rc;
}
nLast++;
iOffset += szPage;
}
rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
}
rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
/* If this frame set completes the first transaction in the WAL and
** if PRAGMA journal_size_limit is set, then truncate the WAL to the
** journal size limit, if possible.
*/
if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
i64 sz = pWal->mxWalSize;
if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){
sz = walFrameOffset(iFrame+nExtra+1, szPage);
}
walLimitSize(pWal, sz);
pWal->truncateOnCommit = 0;
}
/* Append data to the wal-index. It is not necessary to lock the
@@ -2749,9 +2856,9 @@ int sqlite3WalFrames(
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
while( nLast>0 && rc==SQLITE_OK ){
while( nExtra>0 && rc==SQLITE_OK ){
iFrame++;
nLast--;
nExtra--;
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
}
+6
View File
@@ -87,6 +87,12 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData);
/* Write a frame or frames to the log. */
int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int);
/* Additional values that can be added to the sync_flags argument of
** sqlite3WalFrames():
*/
#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */
#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */
/* Copy pages from the log to the database file */
int sqlite3WalCheckpoint(
Wal *pWal, /* Write-ahead log connection */
+59
View File
@@ -0,0 +1,59 @@
# 2011 December 20
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script testing the ability of SQLite to handle database
# files larger than 4GB.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix bigfile2
# Create a small database.
#
do_execsql_test 1.1 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
}
# Pad the file out to 4GB in size. Then clear the file-size field in the
# db header. This will cause SQLite to assume that the first 4GB of pages
# are actually in use and new pages will be appended to the file.
#
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
}
hexio_write test.db 28 00000000
do_test 1.2 {
file size test.db
} [expr 14 + 4096 * (1<<20)]
# Now insert a large row. The overflow pages will be located past the 4GB
# boundary. Then, after opening and closing the database, test that the row
# can be read back in.
#
set str [string repeat k 30000]
do_test 1.3 {
sqlite3 db test.db
execsql { INSERT INTO t1 VALUES(3, $str) }
db close
sqlite3 db test.db
db one { SELECT b FROM t1 WHERE a = 3 }
} $str
db close
file delete test.db
finish_test
+2 -2
View File
@@ -191,7 +191,7 @@ ifcapable wal {
PRAGMA wal_checkpoint;
}
file size test.db-wal
} {1640}
} [expr {32+2*(512+24)}]
do_test 4.3 {
db close
@@ -205,7 +205,7 @@ ifcapable wal {
if {$newsz>$maxsz} {set maxsz $newsz}
}
set maxsz
} {2176}
} [expr {32+3*(512+24)}]
}
finish_test
+1 -2
View File
@@ -34,7 +34,7 @@ proc a_string {n} {
# characteristics flags to "SAFE_DELETE".
#
testvfs tvfs -default 1
tvfs devchar undeletable_when_open
tvfs devchar {undeletable_when_open powersafe_overwrite}
# Set up a hook so that each time a journal file is opened, closed or
# deleted, the method name ("xOpen", "xClose" or "xDelete") and the final
@@ -231,4 +231,3 @@ ifcapable wal {
tvfs delete
finish_test
+10
View File
@@ -14,6 +14,16 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
# The tests in this file assume that SQLite is compiled without
# ENABLE_8_3_NAMES.
#
ifcapable 8_3_names {
puts -nonewline "SQLite compiled with SQLITE_ENABLE_8_3_NAMES. "
puts "Skipping tests multiplex-*."
finish_test
return
}
set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ]
set g_max_chunks 32
+70
View File
@@ -0,0 +1,70 @@
# 2010 October 29
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
source $testdir/lock_common.tcl
do_multiclient_test tn {
code1 { catch { sqlite3_multiplex_initialize "" 0 } }
code2 { catch { sqlite3_multiplex_initialize "" 0 } }
code1 { db close }
code2 { db2 close }
code1 { sqlite3 db test.db -vfs multiplex }
code2 { sqlite3 db2 test.db -vfs multiplex }
code1 { sqlite3_multiplex_control db main chunk_size [expr 1024*1024] }
code2 { sqlite3_multiplex_control db2 main chunk_size [expr 1024*1024] }
sql1 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(randomblob(10), randomblob(4000)); -- 1
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 2
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 4
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 8
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 16
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 32
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 64
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 128
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 256
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 512
SELECT count(*) FROM t1;
}
do_test multiplex-1.$tn.1 { sql1 { SELECT count(*) FROM t1 } } 512
do_test multiplex-1.$tn.2 { sql2 { SELECT count(*) FROM t1 } } 512
sql2 { DELETE FROM t1 ; VACUUM }
do_test multiplex-1.$tn.3 { sql1 { SELECT count(*) FROM t1 } } 0
sql1 {
INSERT INTO t1 VALUES(randomblob(10), randomblob(4000)); -- 1
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 2
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 4
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 8
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 16
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 32
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 64
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 128
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 256
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 512
SELECT count(*) FROM t1;
}
do_test multiplex-1.$tn.4 { sql2 { SELECT count(*) FROM t1 } } 512
}
catch { sqlite3_multiplex_shutdown }
finish_test
+133
View File
@@ -0,0 +1,133 @@
# 2011 December 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 contains tests for error (IO, OOM etc.) handling when using
# the multiplexor extension with 8.3 filenames.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set ::testprefix multiplex3
ifcapable !8_3_names {
puts -nonewline "SQLite compiled without SQLITE_ENABLE_8_3_NAMES. "
puts "Skipping tests multiplex3-*."
finish_test
return
}
db close
sqlite3_shutdown
sqlite3_config_uri 1
autoinstall_test_functions
sqlite3_multiplex_initialize "" 1
proc destroy_vfs_stack {} {
generic_unregister stack
sqlite3_multiplex_shutdown
}
proc multiplex_delete_db {} {
forcedelete test.db
for {set i 1} {$i <= 1000} {incr i} {
forcedelete test.[format %03d $i]
}
}
# Procs to save and restore the current muliplexed database.
#
proc multiplex_save_db {} {
foreach f [glob -nocomplain sv_test.*] { forcedelete $f }
foreach f [glob -nocomplain test.*] { forcecopy $f "sv_$f" }
}
proc multiplex_restore_db {} {
foreach f [glob -nocomplain test.*] {forcedelete $f}
foreach f [glob -nocomplain sv_test.*] {forcecopy $f [string range $f 3 end]} }
proc setup_and_save_db {} {
multiplex_delete_db
sqlite3 db file:test.db?8_3_names=1
sqlite3_multiplex_control db main chunk_size [expr 256*1024]
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(randomblob(15), randomblob(2000));
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 2
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 4
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 8
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 16
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 32
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 64
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 128
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 256
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 512
}
set ::cksum1 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
db close
multiplex_save_db
}
do_test 1.0 { setup_and_save_db } {}
do_faultsim_test 1 -prep {
multiplex_restore_db
sqlite3 db file:test.db?8_3_names=1
sqlite3_multiplex_control db main chunk_size [expr 256*1024]
} -body {
execsql {
UPDATE t1 SET a=randomblob(12), b=randomblob(1500) WHERE (rowid%32)=0
}
} -test {
faultsim_test_result {0 {}}
if {$testrc!=0} {
set cksum2 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
if {$cksum2 != $::cksum1} { error "data mismatch" }
}
}
#-------------------------------------------------------------------------
# The following tests verify that hot-journal rollback works. As follows:
#
# 1. Create a large database.
# 2. Set the pager cache to be very small.
# 3. Open a transaction.
# 4. Run the following 100 times:
# a. Update a row.
# b. Copy all files on disk to a new db location, including the journal.
# c. Verify that the new db can be opened and that the content matches
# the database created in step 1 (proving the journal was rolled
# back).
do_test 2.0 {
setup_and_save_db
multiplex_restore_db
sqlite3 db file:test.db?8_3_names=1
execsql { PRAGMA cache_size = 10 }
execsql { BEGIN }
} {}
for {set iTest 1} {$iTest<=100} {incr iTest} {
do_test 2.$iTest {
execsql {
UPDATE t1 SET a=randomblob(12), b=randomblob(1400) WHERE rowid=5*$iTest
}
foreach f [glob -nocomplain test.*] {forcecopy $f "xx_$f"}
sqlite3 db2 file:xx_test.db?8_3_names=1
execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a} db2
} $::cksum1
db2 close
}
catch { db close }
sqlite3_multiplex_shutdown
finish_test
+24 -4
View File
@@ -990,8 +990,19 @@ do_test pager1-5.4.1 {
INSERT INTO t2 VALUES(85, 'Gorbachev');
COMMIT;
}
set ::max_journal
} [expr 2615+[string length [pwd]]]
# The size of the journal file is now:
#
# 1) 512 byte header +
# 2) 2 * (1024+8) byte records +
# 3) 20+N bytes of master-journal pointer, where N is the size of
# the master-journal name encoded as utf-8 with no nul term.
#
set mj_pointer [expr {
20 + [string length [pwd]] + [string length "/test.db-mjXXXXXX9XX"]
}]
expr {$::max_journal==(512+2*(1024+8)+$mj_pointer)}
} 1
do_test pager1-5.4.2 {
set ::max_journal 0
execsql {
@@ -1001,8 +1012,16 @@ do_test pager1-5.4.2 {
DELETE FROM t2 WHERE b = 'Lenin';
COMMIT;
}
set ::max_journal
} [expr 3111+[string length [pwd]]]
# In synchronous=full mode, the master-journal pointer is not written
# directly after the last record in the journal file. Instead, it is
# written starting at the next (in this case 512 byte) sector boundary.
#
set mj_pointer [expr {
20 + [string length [pwd]] + [string length "/test.db-mjXXXXXX9XX"]
}]
expr {$::max_journal==(((512+2*(1024+8)+511)/512)*512 + $mj_pointer)}
} 1
db close
tv delete
@@ -1312,6 +1331,7 @@ foreach sectorsize {
4096 8192 16384 32768 65536 131072 262144
} {
tv sectorsize $sectorsize
tv devchar {}
set eff $sectorsize
if {$sectorsize < 512} { set eff 512 }
if {$sectorsize > 65536} { set eff 65536 }
+2 -2
View File
@@ -110,8 +110,8 @@ set allquicktests [test_set $alltests -exclude {
speed4p.test sqllimits1.test tkt2686.test thread001.test thread002.test
thread003.test thread004.test thread005.test trans2.test vacuum3.test
incrvacuum_ioerr.test autovacuum_crash.test btree8.test shared_err.test
vtab_err.test walslow.test walcrash.test
walthread.test rtree3.test indexfault.test
vtab_err.test walslow.test walcrash.test walcrash3.test
walthread.test rtree3.test indexfault.test
}]
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
+87
View File
@@ -0,0 +1,87 @@
# 2011 December 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 for the glob-style string compare operator embedded in the
# quota shim.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
catch { unset testnum }
catch { unset pattern }
catch { unset text }
catch { unset ans }
foreach {testnum pattern text ans} {
1 abcdefg abcdefg 1
2 abcdefG abcdefg 0
3 abcdef abcdefg 0
4 abcdefgh abcdefg 0
5 abcdef? abcdefg 1
6 abcdef? abcdef 0
7 abcdef? abcdefgh 0
8 abcdefg abcdef? 0
9 abcdef? abcdef? 1
10 abc/def abc/def 1
11 abc//def abc/def 0
12 */abc/* x/abc/y 1
13 */abc/* /abc/ 1
16 */abc/* x///a/ab/abc 0
17 */abc/* x//a/ab/abc/ 1
16 */abc/* x///a/ab/abc 0
17 */abc/* x//a/ab/abc/ 1
18 **/abc/** x//a/ab/abc/ 1
19 *?/abc/*? x//a/ab/abc/y 1
20 ?*/abc/?* x//a/ab/abc/y 1
21 {abc[cde]efg} abcbefg 0
22 {abc[cde]efg} abccefg 1
23 {abc[cde]efg} abcdefg 1
24 {abc[cde]efg} abceefg 1
25 {abc[cde]efg} abcfefg 0
26 {abc[^cde]efg} abcbefg 1
27 {abc[^cde]efg} abccefg 0
28 {abc[^cde]efg} abcdefg 0
29 {abc[^cde]efg} abceefg 0
30 {abc[^cde]efg} abcfefg 1
31 {abc[c-e]efg} abcbefg 0
32 {abc[c-e]efg} abccefg 1
33 {abc[c-e]efg} abcdefg 1
34 {abc[c-e]efg} abceefg 1
35 {abc[c-e]efg} abcfefg 0
36 {abc[^c-e]efg} abcbefg 1
37 {abc[^c-e]efg} abccefg 0
38 {abc[^c-e]efg} abcdefg 0
39 {abc[^c-e]efg} abceefg 0
40 {abc[^c-e]efg} abcfefg 1
41 {abc[c-e]efg} abc-efg 0
42 {abc[-ce]efg} abc-efg 1
43 {abc[ce-]efg} abc-efg 1
44 {abc[][*?]efg} {abc]efg} 1
45 {abc[][*?]efg} {abc*efg} 1
46 {abc[][*?]efg} {abc?efg} 1
47 {abc[][*?]efg} {abc[efg} 1
48 {abc[^][*?]efg} {abc]efg} 0
49 {abc[^][*?]efg} {abc*efg} 0
50 {abc[^][*?]efg} {abc?efg} 0
51 {abc[^][*?]efg} {abc[efg} 0
52 {abc[^][*?]efg} {abcdefg} 1
53 {*[xyz]efg} {abcxefg} 1
54 {*[xyz]efg} {abcwefg} 0
} {
do_test quota-glob-$testnum.1 {
sqlite3_quota_glob $::pattern $::text
} $::ans
do_test quota-glob-$testnum.2 {
sqlite3_quota_glob $::pattern [string map {/ \\} $::text]
} $::ans
}
finish_test
+6
View File
@@ -14,6 +14,8 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
unset -nocomplain defaultVfs
set defaultVfs [file_control_vfsname db]
db close
do_test quota-1.1 { sqlite3_quota_initialize nosuchvfs 1 } {SQLITE_ERROR}
@@ -48,6 +50,7 @@ do_test quota-1.8 { sqlite3_quota_shutdown } {SQLITE_OK}
#
sqlite3_quota_initialize "" 1
unset -nocomplain quota_request_ok
proc quota_check {filename limitvar size} {
upvar $limitvar limit
@@ -73,6 +76,9 @@ do_test quota-2.1.2 {
}
set ::quota
} {}
do_test quota-2.1.2.1 {
file_control_vfsname db
} quota/$defaultVfs
do_test quota-2.1.3 { file size test.db } {4096}
do_test quota-2.1.4 {
catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
+236
View File
@@ -0,0 +1,236 @@
# 2011 December 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.
#
#***********************************************************************
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
db close
sqlite3_quota_initialize "" 1
foreach dir {quota2a/x1 quota2a/x2 quota2a quota2b quota2c} {
file delete -force $dir
}
foreach dir {quota2a quota2a/x1 quota2a/x2 quota2b quota2c} {
file mkdir $dir
}
# The standard_path procedure converts a pathname into a standard format
# that is the same across platforms.
#
unset -nocomplain ::quota_pwd ::quota_mapping
set ::quota_pwd [string map {\\ /} [pwd]]
set ::quota_mapping [list $::quota_pwd PWD]
proc standard_path {x} {
set x [string map {\\ /} $x]
return [string map $::quota_mapping $x]
}
# The quota_check procedure is a callback from the quota handler.
# It has three arguments which are (1) the full pathname of the file
# that has gone over quota, (2) the quota limit, (3) the requested
# new quota size to cover the last write. These three values are
# appended to the global variable $::quota. The filename is processed
# to convert every \ character into / and to change the name of the
# working directory to PWD.
#
# The quota is increased to the request if the ::quota_request_ok
# global variable is true.
#
set ::quota {}
set ::quota_request_ok 0
proc quota_check {filename limitvar size} {
upvar $limitvar limit
lappend ::quota [standard_path $filename] [set limit] $size
if {$::quota_request_ok} {set limit $size}
}
sqlite3_quota_set */quota2a/* 4000 quota_check
sqlite3_quota_set */quota2b/* 5000 quota_check
unset -nocomplain bigtext
for {set i 1} {$i<=1000} {incr i} {
if {$i%10==0} {
append bigtext [format "%06d\n" $i]
} else {
append bigtext [format "%06d " $i]
}
}
catch { unset h1 }
catch { unset x }
do_test quota2-1.1 {
set ::h1 [sqlite3_quota_fopen quota2a/xyz.txt w+b]
sqlite3_quota_fwrite $::h1 1 7000 $bigtext
} {4000}
do_test quota2-1.2 {
set ::quota
} {PWD/quota2a/xyz.txt 4000 7000}
do_test quota2-1.3 {
sqlite3_quota_rewind $::h1
set ::x [sqlite3_quota_fread $::h1 1001 7]
string length $::x
} {3003}
do_test quota2-1.4 {
string match $::x [string range $::bigtext 0 3002]
} {1}
do_test quota2-1.5 {
sqlite3_quota_fseek $::h1 0 SEEK_END
sqlite3_quota_ftell $::h1
} {4000}
do_test quota2-1.6 {
sqlite3_quota_fseek $::h1 -100 SEEK_END
sqlite3_quota_ftell $::h1
} {3900}
do_test quota2-1.7 {
sqlite3_quota_fseek $::h1 -100 SEEK_CUR
sqlite3_quota_ftell $::h1
} {3800}
do_test quota2-1.8 {
sqlite3_quota_fseek $::h1 50 SEEK_CUR
sqlite3_quota_ftell $::h1
} {3850}
do_test quota2-1.9 {
sqlite3_quota_fseek $::h1 50 SEEK_SET
sqlite3_quota_ftell $::h1
} {50}
do_test quota2-1.10 {
sqlite3_quota_rewind $::h1
sqlite3_quota_ftell $::h1
} {0}
do_test quota2-1.11 {
standard_path [sqlite3_quota_dump]
} {{*/quota2b/* 5000 0} {*/quota2a/* 4000 4000 {PWD/quota2a/xyz.txt 4000 1 0}}}
do_test quota2-1.12 {
sqlite3_quota_fclose $::h1
standard_path [sqlite3_quota_dump]
} {{*/quota2b/* 5000 0} {*/quota2a/* 4000 4000 {PWD/quota2a/xyz.txt 4000 0 0}}}
do_test quota2-1.13 {
sqlite3_quota_remove quota2a/xyz.txt
standard_path [sqlite3_quota_dump]
} {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}}
set quota {}
do_test quota2-2.1 {
set ::h1 [sqlite3_quota_fopen quota2c/xyz.txt w+b]
sqlite3_quota_fwrite $::h1 1 7000 $bigtext
} {7000}
do_test quota2-2.2 {
set ::quota
} {}
do_test quota2-2.3 {
sqlite3_quota_rewind $::h1
set ::x [sqlite3_quota_fread $::h1 1001 7]
string length $::x
} {6006}
do_test quota2-2.4 {
string match $::x [string range $::bigtext 0 6005]
} {1}
do_test quota2-2.5 {
sqlite3_quota_fseek $::h1 0 SEEK_END
sqlite3_quota_ftell $::h1
} {7000}
do_test quota2-2.6 {
sqlite3_quota_fseek $::h1 -100 SEEK_END
sqlite3_quota_ftell $::h1
} {6900}
do_test quota2-2.7 {
sqlite3_quota_fseek $::h1 -100 SEEK_CUR
sqlite3_quota_ftell $::h1
} {6800}
do_test quota2-2.8 {
sqlite3_quota_fseek $::h1 50 SEEK_CUR
sqlite3_quota_ftell $::h1
} {6850}
do_test quota2-2.9 {
sqlite3_quota_fseek $::h1 50 SEEK_SET
sqlite3_quota_ftell $::h1
} {50}
do_test quota2-2.10 {
sqlite3_quota_rewind $::h1
sqlite3_quota_ftell $::h1
} {0}
do_test quota2-2.11 {
standard_path [sqlite3_quota_dump]
} {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}}
do_test quota2-2.12 {
sqlite3_quota_fclose $::h1
standard_path [sqlite3_quota_dump]
} {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}}
do_test quota2-3.1 {
sqlite3_quota_set */quota2b/* 0 quota_check
set ::h1 [sqlite3_quota_fopen quota2a/x1/a.txt a]
sqlite3_quota_fwrite $::h1 10 10 $bigtext
} {10}
do_test quota2-3.2 {
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}}
do_test quota2-3.3a {
sqlite3_quota_fflush $::h1 0
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}}
do_test quota2-3.3b {
sqlite3_quota_fflush $::h1 1
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}}
do_test quota2-3.3c {
sqlite3_quota_fflush $::h1
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}}
do_test quota2-3.4 {
sqlite3_quota_fclose $::h1
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 0 0}}}
do_test quota2-3.5 {
set ::h2 [sqlite3_quota_fopen quota2a/x2/b.txt a]
sqlite3_quota_fwrite $::h2 10 20 $bigtext
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 300 {PWD/quota2a/x2/b.txt 200 1 0} {PWD/quota2a/x1/a.txt 100 0 0}}}
do_test quota2-3.6 {
set ::h3 [sqlite3_quota_fopen quota2a/x1/c.txt a]
sqlite3_quota_fwrite $::h3 10 50 $bigtext
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 800 {PWD/quota2a/x1/c.txt 500 1 0} {PWD/quota2a/x2/b.txt 200 1 0} {PWD/quota2a/x1/a.txt 100 0 0}}}
do_test quota2-3.7 {
file exists quota2a/x1/a.txt
} {1}
do_test quota2-3.8 {
file exists quota2a/x2/b.txt
} {1}
do_test quota2-3.9 {
file exists quota2a/x1/c.txt
} {1}
do_test quota2-3.10 {
sqlite3_quota_remove quota2a/x1
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 700 {PWD/quota2a/x1/c.txt 500 1 1} {PWD/quota2a/x2/b.txt 200 1 0}}}
do_test quota2-3.11 {
sqlite3_quota_fclose $::h2
sqlite3_quota_fclose $::h3
standard_path [sqlite3_quota_dump]
} {{*/quota2a/* 4000 200 {PWD/quota2a/x2/b.txt 200 0 0}}}
do_test quota2-3.12 {
file exists quota2a/x1/a.txt
} {0}
do_test quota2-3.13 {
file exists quota2a/x2/b.txt
} {1}
do_test quota2-3.14 {
file exists quota2a/x1/c.txt
} {0}
catch { sqlite3_quota_shutdown }
catch { unset quota_request_ok }
finish_test
+58 -13
View File
@@ -194,19 +194,28 @@ do_test selectB-3.0 {
}
} {}
for {set ii 3} {$ii <= 4} {incr ii} {
for {set ii 3} {$ii <= 6} {incr ii} {
if {$ii == 4} {
do_test selectB-4.0 {
execsql {
CREATE INDEX i1 ON t1(a);
CREATE INDEX i2 ON t1(b);
CREATE INDEX i3 ON t1(c);
CREATE INDEX i4 ON t2(d);
CREATE INDEX i5 ON t2(e);
CREATE INDEX i6 ON t2(f);
}
} {}
switch $ii {
4 {
optimization_control db query-flattener off
}
5 {
optimization_control db query-flattener on
do_test selectB-5.0 {
execsql {
CREATE INDEX i1 ON t1(a);
CREATE INDEX i2 ON t1(b);
CREATE INDEX i3 ON t1(c);
CREATE INDEX i4 ON t2(d);
CREATE INDEX i5 ON t2(e);
CREATE INDEX i6 ON t2(f);
}
} {}
}
6 {
optimization_control db query-flattener off
}
}
do_test selectB-$ii.1 {
@@ -371,11 +380,47 @@ for {set ii 3} {$ii <= 4} {incr ii} {
}
} {2 4 6 3 6 9 8 10 12 12 15 18 14 16 18 21 24 27}
do_test selectB-$ii.21 {
do_test selectB-$ii.22 {
execsql {
SELECT * FROM (SELECT 345 UNION ALL SELECT d FROM t2) ORDER BY 1;
}
} {3 12 21 345}
do_test selectB-$ii.23 {
execsql {
SELECT x, y FROM (
SELECT a AS x, b AS y FROM t1
UNION ALL
SELECT a*10 + 0.1, f*10 + 0.1 FROM t1 JOIN t2 ON (c=d)
UNION ALL
SELECT a*100, b*100 FROM t1
) ORDER BY 1;
}
} {2 4 8 10 14 16 80.1 180.1 200 400 800 1000 1400 1600}
do_test selectB-$ii.24 {
execsql {
SELECT x, y FROM (
SELECT a AS x, b AS y FROM t1
UNION ALL
SELECT a*10 + 0.1, f*10 + 0.1 FROM t1 LEFT JOIN t2 ON (c=d)
UNION ALL
SELECT a*100, b*100 FROM t1
) ORDER BY 1;
}
} {2 4 8 10 14 16 20.1 {} 80.1 180.1 140.1 {} 200 400 800 1000 1400 1600}
do_test selectB-$ii.25 {
execsql {
SELECT x+y FROM (
SELECT a AS x, b AS y FROM t1
UNION ALL
SELECT a*10 + 0.1, f*10 + 0.1 FROM t1 LEFT JOIN t2 ON (c=d)
UNION ALL
SELECT a*100, b*100 FROM t1
) WHERE y+x NOT NULL ORDER BY 1;
}
} {6 18 30 260.2 600 1800 3000}
}
finish_test
+4 -1
View File
@@ -76,7 +76,10 @@ do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
do_catchsql_test 3.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}}
do_test 3.6 { unlock } {}
do_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 2 2}
# At this point the WAL file consists of a single frame only - written
# by test case 3.1. If the ZERO_DAMAGE flag were not set, it would consist
# of two frames - the frame written by 3.1 and a padding frame.
do_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 1 1}
do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock}
do_catchsql_test 4.3 { SELECT * FROM t1 } {1 {database is locked}}
+2 -1
View File
@@ -59,7 +59,8 @@ do_test 2.1.2 { test_syscall exists nosuchcall } 0
foreach s {
open close access getcwd stat fstat ftruncate
fcntl read pread write pwrite fchmod fallocate
pread64 pwrite64 unlink openDirectory mkdir rmdir
pread64 pwrite64 unlink openDirectory mkdir rmdir
statvfs
} {
if {[test_syscall exists $s]} {lappend syscall_list $s}
}
+44
View File
@@ -80,4 +80,48 @@ do_multiclient_test tn {
} {0 {hello world}}
}
do_multiclient_test tn {
do_test unixexcl-3.$tn.1 {
code1 { db close; sqlite3 db test.db -vfs unix-excl }
code2 { db2 close; sqlite3 db2 test.db -vfs unix-excl }
sql1 {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
}
} {wal}
if {$tn==1} {
do_test unixexcl-3.$tn.1.multiproc {
csql2 { SELECT * FROM t1; }
} {1 {database is locked}}
} else {
do_test unixexcl-3.$tn.1.singleproc {
sql2 { SELECT * FROM t1; }
} {1 2}
do_test unixexcl-3.$tn.2 {
sql2 {
BEGIN;
SELECT * FROM t1;
}
} {1 2}
do_test unixexcl-3.$tn.3 {
sql1 { PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(3, 4); }
} {0 5 5}
do_test unixexcl-3.$tn.4 {
sql2 { SELECT * FROM t1; }
} {1 2}
do_test unixexcl-3.$tn.5 {
sql1 { SELECT * FROM t1; }
} {1 2 3 4}
do_test unixexcl-3.$tn.6 {
sql2 { COMMIT; SELECT * FROM t1; }
} {1 2 3 4}
do_test unixexcl-3.$tn.7 {
sql1 { PRAGMA wal_checkpoint; }
} {0 7 7}
}
}
finish_test
+20 -15
View File
@@ -546,7 +546,7 @@ do_multiclient_test tn {
} {1 2 3 4 5 6 7 8 9 10}
do_test wal-10.$tn.12 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 13 13}} ;# Reader no longer block checkpoints
} {0 {0 7 7}} ;# Reader no longer block checkpoints
do_test wal-10.$tn.13 {
execsql { INSERT INTO t1 VALUES(11, 12) }
sql2 {SELECT * FROM t1}
@@ -556,7 +556,7 @@ do_multiclient_test tn {
#
do_test wal-10.$tn.14 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 15 13}}
} {0 {0 8 7}}
# The following series of test cases used to verify another blocking
# case in WAL - a case which no longer blocks.
@@ -566,10 +566,10 @@ do_multiclient_test tn {
} {1 2 3 4 5 6 7 8 9 10 11 12}
do_test wal-10.$tn.16 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 15 15}}
} {0 {0 8 8}}
do_test wal-10.$tn.17 {
execsql { PRAGMA wal_checkpoint }
} {0 15 15}
} {0 8 8}
do_test wal-10.$tn.18 {
sql3 { BEGIN; SELECT * FROM t1 }
} {1 2 3 4 5 6 7 8 9 10 11 12}
@@ -592,13 +592,13 @@ do_multiclient_test tn {
#
do_test wal-10.$tn.23 {
execsql { PRAGMA wal_checkpoint }
} {0 17 17}
} {0 9 9}
do_test wal-10.$tn.24 {
sql2 { BEGIN; SELECT * FROM t1; }
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14}
do_test wal-10.$tn.25 {
execsql { PRAGMA wal_checkpoint }
} {0 17 17}
} {0 9 9}
do_test wal-10.$tn.26 {
catchsql { INSERT INTO t1 VALUES(15, 16) }
} {0 {}}
@@ -615,11 +615,11 @@ do_multiclient_test tn {
do_test wal-10.$tn.29 {
execsql { INSERT INTO t1 VALUES(19, 20) }
catchsql { PRAGMA wal_checkpoint }
} {0 {0 6 0}}
} {0 {0 3 0}}
do_test wal-10.$tn.30 {
code3 { sqlite3_finalize $::STMT }
execsql { PRAGMA wal_checkpoint }
} {0 6 0}
} {0 3 0}
# At one point, if a reader failed to upgrade to a writer because it
# was reading an old snapshot, the write-locks were not being released.
@@ -658,7 +658,7 @@ do_multiclient_test tn {
} {a b c d}
do_test wal-10.$tn.36 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 16 16}}
} {0 {0 8 8}}
do_test wal-10.$tn.36 {
sql3 { INSERT INTO t1 VALUES('e', 'f') }
sql2 { SELECT * FROM t1 }
@@ -666,7 +666,7 @@ do_multiclient_test tn {
do_test wal-10.$tn.37 {
sql2 COMMIT
execsql { PRAGMA wal_checkpoint }
} {0 18 18}
} {0 9 9}
}
#-------------------------------------------------------------------------
@@ -1040,7 +1040,7 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
5 {sqlite3_wal_checkpoint db aux} SQLITE_OK 0 1
6 {sqlite3_wal_checkpoint db temp} SQLITE_OK 0 0
7 {db eval "PRAGMA main.wal_checkpoint"} {0 10 10} 1 0
8 {db eval "PRAGMA aux.wal_checkpoint"} {0 16 16} 0 1
8 {db eval "PRAGMA aux.wal_checkpoint"} {0 13 13} 0 1
9 {db eval "PRAGMA temp.wal_checkpoint"} {0 -1 -1} 0 0
} {
do_test wal-16.$tn.1 {
@@ -1054,7 +1054,8 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
PRAGMA aux.auto_vacuum = 0;
PRAGMA main.journal_mode = WAL;
PRAGMA aux.journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA main.synchronous = NORMAL;
PRAGMA aux.synchronous = NORMAL;
}
} {wal wal}
@@ -1072,7 +1073,7 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
} [list [expr 1*1024] [wal_file_size 10 1024]]
do_test wal-16.$tn.3 {
list [file size test2.db] [file size test2.db-wal]
} [list [expr 1*1024] [wal_file_size 16 1024]]
} [list [expr 1*1024] [wal_file_size 13 1024]]
do_test wal-16.$tn.4 [list eval $ckpt_cmd] $ckpt_res
@@ -1082,7 +1083,7 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
do_test wal-16.$tn.6 {
list [file size test2.db] [file size test2.db-wal]
} [list [expr ($ckpt_aux ? 7 : 1)*1024] [wal_file_size 16 1024]]
} [list [expr ($ckpt_aux ? 7 : 1)*1024] [wal_file_size 13 1024]]
catch { db close }
}
@@ -1552,9 +1553,13 @@ ifcapable autovacuum {
}
file size test.db
} [expr 3 * 1024]
# WAL file now contains a single frame - the new root page for table t1.
# It would be two frames (the new root page and a padding frame) if the
# ZERO_DAMAGE flag were not set.
do_test 24.5 {
file size test.db-wal
} 2128
} [wal_file_size 1 1024]
}
db close
+38 -25
View File
@@ -361,7 +361,9 @@ do_test wal2-4.1 {
INSERT INTO data VALUES('need xShmOpen to see this');
PRAGMA wal_checkpoint;
}
} {wal 0 5 5}
# Three pages in the WAL file at this point: One copy of page 1 and two
# of the root page for table "data".
} {wal 0 3 3}
do_test wal2-4.2 {
db close
testvfs tvfs -noshm 1
@@ -730,7 +732,7 @@ do_test wal2-6.5.1 {
INSERT INTO t2 VALUES('I', 'II');
PRAGMA journal_mode;
}
} {wal exclusive 0 3 3 wal}
} {wal exclusive 0 2 2 wal}
do_test wal2-6.5.2 {
execsql {
PRAGMA locking_mode = normal;
@@ -741,7 +743,7 @@ do_test wal2-6.5.2 {
} {normal exclusive I II III IV}
do_test wal2-6.5.3 {
execsql { PRAGMA wal_checkpoint }
} {0 4 4}
} {0 2 2}
db close
proc lock_control {method filename handle spec} {
@@ -1176,14 +1178,15 @@ if {$::tcl_platform(platform) == "unix"} {
# Test that "PRAGMA checkpoint_fullsync" appears to be working.
#
foreach {tn sql reslist} {
1 { } {8 0 3 0 5 0}
2 { PRAGMA checkpoint_fullfsync = 1 } {8 4 3 2 5 2}
3 { PRAGMA checkpoint_fullfsync = 0 } {8 0 3 0 5 0}
1 { } {10 0 4 0 6 0}
2 { PRAGMA checkpoint_fullfsync = 1 } {10 4 4 2 6 2}
3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0}
} {
faultsim_delete_and_reopen
execsql {PRAGMA auto_vacuum = 0}
execsql $sql
do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 } {}
do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}
set sqlite_sync_count 0
@@ -1192,14 +1195,14 @@ foreach {tn sql reslist} {
do_execsql_test wal2-14.$tn.2 {
PRAGMA wal_autocheckpoint = 10;
CREATE TABLE t1(a, b); -- 2 wal syncs
INSERT INTO t1 VALUES(1, 2); -- 1 wal sync
INSERT INTO t1 VALUES(1, 2); -- 2 wal sync
PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync
BEGIN;
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 6);
COMMIT; -- 1 wal sync
COMMIT; -- 2 wal sync
PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync
} {10 0 5 5 0 2 2}
} {10 0 3 3 0 1 1}
do_test wal2-14.$tn.3 {
cond_incr_sync_count 1
@@ -1233,22 +1236,22 @@ catch { db close }
# PRAGMA fullfsync
# PRAGMA synchronous
#
foreach {tn settings commit_sync ckpt_sync} {
1 {0 0 off} {0 0} {0 0}
2 {0 0 normal} {0 0} {2 0}
3 {0 0 full} {1 0} {2 0}
foreach {tn settings restart_sync commit_sync ckpt_sync} {
1 {0 0 off} {0 0} {0 0} {0 0}
2 {0 0 normal} {1 0} {0 0} {2 0}
3 {0 0 full} {2 0} {1 0} {2 0}
4 {0 1 off} {0 0} {0 0}
5 {0 1 normal} {0 0} {0 2}
6 {0 1 full} {0 1} {0 2}
4 {0 1 off} {0 0} {0 0} {0 0}
5 {0 1 normal} {0 1} {0 0} {0 2}
6 {0 1 full} {0 2} {0 1} {0 2}
7 {1 0 off} {0 0} {0 0}
8 {1 0 normal} {0 0} {0 2}
9 {1 0 full} {1 0} {0 2}
7 {1 0 off} {0 0} {0 0} {0 0}
8 {1 0 normal} {1 0} {0 0} {0 2}
9 {1 0 full} {2 0} {1 0} {0 2}
10 {1 1 off} {0 0} {0 0}
11 {1 1 normal} {0 0} {0 2}
12 {1 1 full} {0 1} {0 2}
10 {1 1 off} {0 0} {0 0} {0 0}
11 {1 1 normal} {0 1} {0 0} {0 2}
12 {1 1 full} {0 2} {0 1} {0 2}
} {
forcedelete test.db
@@ -1261,28 +1264,38 @@ foreach {tn settings commit_sync ckpt_sync} {
sqlite3 db test.db
do_execsql_test 15.$tn.1 "
PRAGMA page_size = 4096;
CREATE TABLE t1(x);
PRAGMA wal_autocheckpoint = OFF;
PRAGMA journal_mode = WAL;
PRAGMA checkpoint_fullfsync = [lindex $settings 0];
PRAGMA fullfsync = [lindex $settings 1];
PRAGMA synchronous = [lindex $settings 2];
" {wal}
" {0 wal}
if { $tn==2} breakpoint
do_test 15.$tn.2 {
set sync(normal) 0
set sync(full) 0
execsql { INSERT INTO t1 VALUES('abc') }
list $::sync(normal) $::sync(full)
} $restart_sync
do_test 15.$tn.3 {
set sync(normal) 0
set sync(full) 0
execsql { INSERT INTO t1 VALUES('abc') }
list $::sync(normal) $::sync(full)
} $commit_sync
do_test 15.$tn.3 {
do_test 15.$tn.4 {
set sync(normal) 0
set sync(full) 0
execsql { INSERT INTO t1 VALUES('def') }
list $::sync(normal) $::sync(full)
} $commit_sync
do_test 15.$tn.4 {
do_test 15.$tn.5 {
set sync(normal) 0
set sync(full) 0
execsql { PRAGMA wal_checkpoint }
+6 -5
View File
@@ -198,9 +198,9 @@ foreach {tn syncmode synccount} {
1 off
{}
2 normal
{test.db-wal normal test.db normal}
{test.db-wal normal test.db-wal normal test.db normal}
3 full
{test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}
{test.db-wal normal test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}
} {
proc sync_counter {args} {
@@ -217,6 +217,7 @@ foreach {tn syncmode synccount} {
execsql "PRAGMA synchronous = $syncmode"
execsql { PRAGMA journal_mode = WAL }
execsql { CREATE TABLE filler(a,b,c); }
set ::syncs [list]
T filter xSync
@@ -428,7 +429,7 @@ do_test wal3-6.1.2 {
} {o t t f}
do_test wal3-6.1.3 {
execsql { PRAGMA wal_checkpoint } db2
} {0 7 7}
} {0 4 4}
# At this point the log file has been fully checkpointed. However,
# connection [db3] holds a lock that prevents the log from being wrapped.
@@ -517,7 +518,7 @@ proc lock_callback {method file handle spec} {
}
do_test wal3-6.2.2 {
execsql { PRAGMA wal_checkpoint }
} {0 7 7}
} {0 4 4}
do_test wal3-6.2.3 {
set ::R
} {h h l b}
@@ -627,7 +628,7 @@ do_test wal3-8.1 {
INSERT INTO b VALUES('Markazi');
PRAGMA wal_checkpoint;
}
} {wal 0 9 9}
} {wal 0 5 5}
do_test wal3-8.2 {
execsql { SELECT * FROM b }
} {Tehran Qom Markazi}
+20 -20
View File
@@ -197,9 +197,9 @@ foreach {testprefix do_wal_checkpoint} {
INSERT INTO t2 VALUES(1, 2);
}
} {}
do_test 2.2.$tn.2 { file_page_counts } {1 5 1 5}
do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 5 5}
do_test 2.1.$tn.4 { file_page_counts } {2 5 2 5}
do_test 2.2.$tn.2 { file_page_counts } {1 3 1 3}
do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 3 3}
do_test 2.1.$tn.4 { file_page_counts } {2 3 2 3}
}
do_multiclient_test tn {
@@ -213,10 +213,10 @@ foreach {testprefix do_wal_checkpoint} {
INSERT INTO t2 VALUES(3, 4);
}
} {}
do_test 2.2.$tn.2 { file_page_counts } {1 5 1 7}
do_test 2.2.$tn.2 { file_page_counts } {1 3 1 4}
do_test 2.2.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 5 5}
do_test 2.2.$tn.5 { file_page_counts } {2 5 2 7}
do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 3 3}
do_test 2.2.$tn.5 { file_page_counts } {2 3 2 4}
}
do_multiclient_test tn {
@@ -229,13 +229,13 @@ foreach {testprefix do_wal_checkpoint} {
INSERT INTO t2 VALUES(1, 2);
}
} {}
do_test 2.3.$tn.2 { file_page_counts } {1 5 1 5}
do_test 2.3.$tn.2 { file_page_counts } {1 3 1 3}
do_test 2.3.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
do_test 2.3.$tn.4 { sql1 { INSERT INTO t1 VALUES(3, 4) } } {}
do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {}
do_test 2.3.$tn.6 { file_page_counts } {1 7 1 7}
do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 7 5}
do_test 2.3.$tn.8 { file_page_counts } {1 7 2 7}
do_test 2.3.$tn.6 { file_page_counts } {1 4 1 4}
do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 4 3}
do_test 2.3.$tn.8 { file_page_counts } {1 4 2 4}
}
# Check that checkpoints block on the correct locks. And respond correctly
@@ -256,18 +256,18 @@ foreach {testprefix do_wal_checkpoint} {
# processes holding all three types of locks.
#
foreach {tn1 checkpoint busy_on ckpt_expected expected} {
1 PASSIVE - {0 5 5} -
2 TYPO - {0 5 5} -
1 PASSIVE - {0 3 3} -
2 TYPO - {0 3 3} -
3 FULL - {0 7 7} 2
4 FULL 1 {1 5 5} 1
5 FULL 2 {1 7 5} 2
6 FULL 3 {0 7 7} 2
3 FULL - {0 4 4} 2
4 FULL 1 {1 3 3} 1
5 FULL 2 {1 4 3} 2
6 FULL 3 {0 4 4} 2
7 RESTART - {0 7 7} 3
8 RESTART 1 {1 5 5} 1
9 RESTART 2 {1 7 5} 2
10 RESTART 3 {1 7 7} 3
7 RESTART - {0 4 4} 3
8 RESTART 1 {1 3 3} 1
9 RESTART 2 {1 4 3} 2
10 RESTART 3 {1 4 4} 3
} {
do_multiclient_test tn {
+129
View File
@@ -0,0 +1,129 @@
# 2011 December 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 test simulates an application crash immediately following a
# system call to truncate a file. Specifically, the system call that
# truncates the WAL file if "PRAGMA journal_size_limit" is configured.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
set testprefix walcrash3
db close
testvfs tvfs
tvfs filter {xTruncate xWrite}
tvfs script tvfs_callback
proc tvfs_callback {args} {}
sqlite3 db test.db -vfs tvfs
do_execsql_test 1.1 {
PRAGMA page_size = 1024;
PRAGMA journal_mode = WAL;
PRAGMA wal_autocheckpoint = 128;
PRAGMA journal_size_limit = 16384;
CREATE TABLE t1(a BLOB, b BLOB, UNIQUE(a, b));
INSERT INTO t1 VALUES(randomblob(10), randomblob(1000));
} {wal 128 16384}
proc tvfs_callback {method file arglist} {
if {$::state==1} {
foreach f [glob -nocomplain xx_test.*] { forcedelete $f }
foreach f [glob -nocomplain test.*] { forcecopy $f "xx_$f" }
set ::state 2
}
if {$::state==0 && $method=="xTruncate" && [file tail $file]=="test.db-wal"} {
set ::state 1
}
}
for {set i 2} {$i<1000} {incr i} {
# If the WAL file is truncated within the following, within the following
# xWrite call the [tvfs_callback] makes a copy of the database and WAL
# files set sets $::state to 2. So that the copied files are in the same
# state as the real database and WAL files would be if an application crash
# occurred immediately following the xTruncate().
#
set ::state 0
do_execsql_test 1.$i.1 {
INSERT INTO t1 VALUES(randomblob(10), randomblob(1000));
}
# If a copy was made, open it and run the integrity-check.
#
if {$::state==2} {
sqlite3 db2 xx_test.db
do_test 1.$i.2 { execsql { PRAGMA integrity_check } db2 } "ok"
do_test 1.$i.3 { execsql { SELECT count(*) FROM t1 } db2 } [expr $i-1]
db2 close
}
}
catch { db close }
tvfs delete
#--------------------------------------------------------------------------
#
catch { db close }
forcedelete test.db
do_test 2.1 {
sqlite3 db test.db
execsql {
PRAGMA page_size = 512;
PRAGMA journal_mode = WAL;
PRAGMA wal_autocheckpoint = 128;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(randomblob(25), randomblob(200));
}
for {set i 0} {$i < 1500} {incr i} {
execsql { INSERT INTO t1 VALUES(randomblob(25), randomblob(200)) }
}
db_save
db close
} {}
set nInitialErr [set_test_counter errors]
for {set i 2} {$i<10000 && [set_test_counter errors]==$nInitialErr} {incr i} {
do_test 2.$i.1 {
catch { db close }
db_restore
crashsql -delay 2 -file test.db-wal -seed $i {
SELECT * FROM sqlite_master;
PRAGMA synchronous = full;
PRAGMA wal_checkpoint;
BEGIN;
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
INSERT INTO t1 VALUES(randomblob(26), randomblob(200));
COMMIT;
}
} {1 {child process exited abnormally}}
do_test 2.$i.2 {
sqlite3 db test.db
execsql { PRAGMA integrity_check }
} {ok}
}
finish_test
+38 -3
View File
@@ -68,7 +68,10 @@ do_test walpersist-1.11 {
} {1 1 1}
# Make sure the journal_size_limit works to limit the size of the
# persisted wal file.
# persisted wal file. In persistent-wal mode, any non-negative
# journal_size_limit causes the WAL file to be truncated to zero bytes
# when closing.
#
forcedelete test.db test.db-shm test.db-wal
do_test walpersist-2.1 {
sqlite3 db test.db
@@ -85,7 +88,39 @@ do_test walpersist-2.1 {
do_test walpersist-2.2 {
file_control_persist_wal db 1
db close
file size test.db-wal
} {12000}
concat [file exists test.db-wal] [file size test.db-wal]
} {1 0}
do_test walpersist-2.3 {
sqlite3 db test.db
execsql { PRAGMA integrity_check }
} {ok}
do_test 3.1 {
catch {db close}
forcedelete test.db test.db-shm test.db-wal
sqlite3 db test.db
execsql {
PRAGMA page_size = 1024;
PRAGMA journal_mode = WAL;
PRAGMA wal_autocheckpoint=128;
PRAGMA journal_size_limit=16384;
CREATE TABLE t1(a, b, PRIMARY KEY(a, b));
}
} {wal 128 16384}
do_test 3.2 {
for {set i 0} {$i<200} {incr i} {
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) }
}
file_control_persist_wal db 1
db close
} {}
do_test walpersist-3.3 {
file size test.db-wal
} {0}
do_test walpersist-3.4 {
sqlite3 db test.db
execsql { PRAGMA integrity_check }
} {ok}
finish_test
+112
View File
@@ -0,0 +1,112 @@
# 2011 December 21
#
# 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 of the SQLITE_IOCAP_POWERSAFE_OVERWRITE property
# and the SQLITE_FCNTL_POWERSAFE_OVERWRITE file-control for manipulating it.
#
# The name of this file comes from the fact that we used to call the
# POWERSAFE_OVERWRITE property ZERO_DAMAGE.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix wal5
# POWERSAFE_OVERWRITE defaults to true
#
do_test zerodamage-1.0 {
file_control_powersafe_overwrite db -1
} {0 1}
# Check the ability to turn zero-damage on and off.
#
do_test zerodamage-1.1 {
file_control_powersafe_overwrite db 0
file_control_powersafe_overwrite db -1
} {0 0}
do_test zerodamage-1.2 {
file_control_powersafe_overwrite db 1
file_control_powersafe_overwrite db -1
} {0 1}
# Run a transaction with zero-damage on, a small page size and a much larger
# sectorsize. Verify that the maximum journal size is small - that the
# rollback journal is not being padded.
#
do_test zerodamage-2.0 {
db close
testvfs tv -default 1
tv sectorsize 8192
sqlite3 db file:test.db?psow=TRUE -uri 1
unset -nocomplain ::max_journal_size
set ::max_journal_size 0
proc xDeleteCallback {method file args} {
set sz [file size $file]
if {$sz>$::max_journal_size} {set ::max_journal_size $sz}
}
tv filter xDelete
tv script xDeleteCallback
register_wholenumber_module db
db eval {
PRAGMA page_size=1024;
PRAGMA journal_mode=DELETE;
PRAGMA cache_size=5;
CREATE VIRTUAL TABLE nums USING wholenumber;
CREATE TABLE t1(x, y);
INSERT INTO t1 SELECT value, randomblob(100) FROM nums
WHERE value BETWEEN 1 AND 400;
}
set ::max_journal_size 0
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=123;
}
concat [file_control_powersafe_overwrite db -1] [set ::max_journal_size]
} {0 1 2576}
# Repeat the previous step with zero-damage turned off. This time the
# maximum rollback journal size should be much larger.
#
do_test zerodamage-2.1 {
set ::max_journal_size 0
db close
sqlite3 db file:test.db?psow=FALSE -uri 1
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=124;
}
concat [file_control_powersafe_overwrite db -1] [set ::max_journal_size]
} {0 0 24704}
# Run a WAL-mode transaction with POWERSAFE_OVERWRITE on to verify that the
# WAL file does not get too big.
#
do_test zerodamage-3.0 {
db eval {
PRAGMA journal_mode=WAL;
}
db close
sqlite3 db file:test.db?psow=TRUE -uri 1
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=124;
}
file size test.db-wal
} {1080}
# Repeat the previous with POWERSAFE_OVERWRITE off. Verify that the WAL file
# is padded.
#
do_test zerodamage-3.1 {
db close
sqlite3 db file:test.db?psow=FALSE -uri 1
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=124;
}
file size test.db-wal
} {8416}