Merge the latest changes from trunk.

FossilOrigin-Name: a54be6e041a9185787a22b86603dcb0654e5e4af71225b556d1b6279f8520ad8
This commit is contained in:
drh 2017-07-13 17:34:46 +00:00
commit 3fb406083a
182 changed files with 9614 additions and 1062 deletions

View File

@ -2266,6 +2266,7 @@ clean:
-rmdir /Q/S tsrc 2>NUL
del /Q .target_source 2>NUL
del /Q tclsqlite3.exe $(SQLITETCLH) $(SQLITETCLDECLSH) 2>NUL
del /Q lsm.dll lsmtest.exe 2>NUL
del /Q testloadext.dll 2>NUL
del /Q testfixture.exe test.db 2>NUL
del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL

View File

@ -34,7 +34,9 @@ archives as follows:
If you do want to use Fossil to check out the source tree,
first install Fossil version 2.0 or later.
(Source tarballs and precompiled binaries available
[here](https://www.fossil-scm.org/fossil/uv/download.html).)
[here](https://www.fossil-scm.org/fossil/uv/download.html). Fossil is
a stand-alone program. To install, simply download or build the single
executable file and put that file someplace on your $PATH.)
Then run commands like this:
mkdir ~/sqlite
@ -106,19 +108,22 @@ The makefiles also require AWK.
## Source Code Tour
Most of the core source files are in the **src/** subdirectory. But
src/ also contains files used to build the "testfixture" test harness;
those file all begin with "test". And src/ contains the "shell.c" file
which is the main program for the "sqlite3.exe" command-line shell and
the "tclsqlite.c" file which implements the bindings to SQLite from the
Tcl programming language. (Historical note: SQLite began as a Tcl
Most of the core source files are in the **src/** subdirectory. The
**src/** folder also contains files used to build the "testfixture" test
harness. The names of the source files used by "testfixture" all begin
with "test".
The **src/** also contains the "shell.c" file
which is the main program for the "sqlite3.exe"
[command-line shell](https://sqlite.org/cli.html) and
the "tclsqlite.c" file which implements the
[TCL bindings](https://sqlite.org/tclsqlite.html) for SQLite.
(Historical note: SQLite began as a Tcl
extension and only later escaped to the wild as an independent library.)
Test scripts and programs are found in the **test/** subdirectory.
There are other test suites for SQLite (see
[How SQLite Is Tested](http://www.sqlite.org/testing.html))
but those other test suites are
in separate source repositories.
Addtional test code is found in other source repositories.
See [How SQLite Is Tested](http://www.sqlite.org/testing.html) for
additional information.
The **ext/** subdirectory contains code for extensions. The
Full-text search engine is in **ext/fts3**. The R-Tree engine is in
@ -142,7 +147,7 @@ manually-edited files and automatically-generated files.
The SQLite interface is defined by the **sqlite3.h** header file, which is
generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The
[Tcl script](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion.
The manifest.uuid file contains the SHA1 hash of the particular check-in
The manifest.uuid file contains the SHA3 hash of the particular check-in
and is used to generate the SQLITE\_SOURCE\_ID macro. The VERSION file
contains the current SQLite version number. The sqlite3.h header is really
just a copy of src/sqlite.h.in with the source-id and version number inserted
@ -153,9 +158,8 @@ used to generate that documentation are in a separate source repository.
The SQL language parser is **parse.c** which is generate from a grammar in
the src/parse.y file. The conversion of "parse.y" into "parse.c" is done
by the [lemon](./doc/lemon.html) LALR(1) parser generator. The source code
for lemon is at tool/lemon.c. Lemon uses a
template for generating its parser. A generic template is in tool/lempar.c,
but SQLite uses a slightly modified template found in src/lempar.c.
for lemon is at tool/lemon.c. Lemon uses the tool/lempar.c file as a
template for generating its parser.
Lemon also generates the **parse.h** header file, at the same time it
generates parse.c. But the parse.h header file is
@ -175,6 +179,13 @@ that maps SQL language keywords (ex: "CREATE", "SELECT", "INDEX", etc.) into
the numeric codes used by the parse.c parser. The keywordhash.h file is
generated by a C-language program at tool mkkeywordhash.c.
The **pragma.h** header file contains various definitions used to parse
and implement the PRAGMA statements. The header is generated by a
script **tool/mkpragmatab.tcl**. If you want to add a new PRAGMA, edit
the **tool/mkpragmatab.tcl** file to insert the information needed by the
parser for your new PRAGMA, then run the script to regenerate the
**pragma.h** header file.
### The Amalgamation
All of the individual C source code and header files (both manually-edited
@ -192,7 +203,7 @@ subdirectory (using the equivalent of "make target_source") then the
tool/mksqlite3c.tcl script is run to copy them all together in just the
right order while resolving internal "#include" references.
The amalgamation source file is more than 100K lines long. Some symbolic
The amalgamation source file is more than 200K lines long. Some symbolic
debuggers (most notably MSVC) are unable to deal with files longer than 64K
lines. To work around this, a separate Tcl script, tool/split-sqlite3c.tcl,
can be run on the amalgamation to break it up into a single small C file
@ -209,14 +220,15 @@ See the [architectural description](http://www.sqlite.org/arch.html)
for details. Other documents that are useful in
(helping to understand how SQLite works include the
[file format](http://www.sqlite.org/fileformat2.html) description,
the [virtual machine](http://www.sqlite.org/vdbe.html) that runs
the [virtual machine](http://www.sqlite.org/opcode.html) that runs
prepared statements, the description of
[how transactions work](http://www.sqlite.org/atomiccommit.html), and
the [overview of the query planner](http://www.sqlite.org/optoverview.html).
Unfortunately, years of effort have gone into optimizating SQLite, both
Years of effort have gone into optimizating SQLite, both
for small size and high performance. And optimizations tend to result in
complex code. So there is a lot of complexity in the SQLite implementation.
complex code. So there is a lot of complexity in the current SQLite
implementation. It will not be the easiest library in the world to hack.
Key files:
@ -227,7 +239,7 @@ Key files:
* **sqliteInt.h** - this header file defines many of the data objects
used internally by SQLite.
* **parse.y** - This file describes the LALR(1) grammer that SQLite uses
* **parse.y** - This file describes the LALR(1) grammar that SQLite uses
to parse SQL statements, and the actions that are taken at each step
in the parsing process.
@ -260,13 +272,13 @@ Key files:
is not part of the core SQLite library. But as most of the tests in this
repository are written in Tcl, the Tcl language bindings are important.
There are many other source files. Each has a suscinct header comment that
There are many other source files. Each has a succinct header comment that
describes its purpose and role within the larger system.
## Contacts
The main SQLite webpage is [http://www.sqlite.org/](http://www.sqlite.org/)
with geographically distributed backup servers at
with geographically distributed backups at
[http://www2.sqlite.org/](http://www2.sqlite.org) and
[http://www3.sqlite.org/](http://www3.sqlite.org).

View File

@ -441,7 +441,7 @@ db func funk funk
do_catchsql_test 16.2 {
SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d'
} {0 {{} -1e-06 {}}}
# {1 {SQL logic error or missing database}}
# {1 {SQL logic error}}
#-------------------------------------------------------------------------
#
@ -595,5 +595,3 @@ do_execsql_test 22.1 {
finish_test

View File

@ -294,4 +294,3 @@ do_execsql_test 7.0 {
finish_test

View File

@ -276,4 +276,3 @@ foreach {tn expr tclexpr} {
}
finish_test

View File

@ -242,4 +242,3 @@ foreach {T create} {
}
finish_test

View File

@ -309,4 +309,3 @@ foreach {tn q cnt} {
}
finish_test

View File

@ -178,4 +178,3 @@ do_execsql_test 5.1 {
} ;# foreach_detail_mode
finish_test

View File

@ -142,4 +142,3 @@ if {[detail_is_full]} {
finish_test

View File

@ -167,4 +167,3 @@ do_execsql_test 1.8.2 {
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
finish_test

View File

@ -55,4 +55,3 @@ do_execsql_test 1.2 {
finish_test

View File

@ -66,4 +66,3 @@ do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') }
finish_test

View File

@ -147,4 +147,3 @@ do_execsql_test 3.1 {
}
finish_test

View File

@ -77,7 +77,7 @@ foreach {tn defn} {
} {
do_test 2.2.$tn {
catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) }
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
}
#-------------------------------------------------------------------------
@ -297,4 +297,3 @@ do_catchsql_test 4.4.4 {
finish_test

View File

@ -100,4 +100,3 @@ do_execsql_test 3.3 {
} {1 2}
finish_test

View File

@ -342,4 +342,3 @@ foreach {tn expr} {
finish_test

View File

@ -279,4 +279,3 @@ do_execsql_test 9.3 {
finish_test

View File

@ -112,4 +112,3 @@ db eval {
}
finish_test

View File

@ -61,4 +61,3 @@ do_test 2.1...slow {
} {}
finish_test

View File

@ -64,5 +64,3 @@ foreach_detail_mode $::testprefix {
}
finish_test

View File

@ -84,5 +84,3 @@ foreach_detail_mode $::testprefix {
finish_test

View File

@ -66,7 +66,7 @@ foreach {tn val} {
} {
do_catchsql_test 3.$tn {
INSERT INTO t1(t1, rank) VALUES('rank', $val);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
}
#-------------------------------------------------------------------------
@ -169,33 +169,33 @@ do_execsql_test 9.0 {
} {}
do_catchsql_test 9.1.1 {
INSERT INTO abc(abc, rank) VALUES('pgsz', -5);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.1.2 {
INSERT INTO abc(abc, rank) VALUES('pgsz', 50000000);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.1.3 {
INSERT INTO abc(abc, rank) VALUES('pgsz', 66.67);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.2.1 {
INSERT INTO abc(abc, rank) VALUES('automerge', -5);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.2.2 {
INSERT INTO abc(abc, rank) VALUES('automerge', 50000000);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.2.3 {
INSERT INTO abc(abc, rank) VALUES('automerge', 66.67);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_execsql_test 9.2.4 {
INSERT INTO abc(abc, rank) VALUES('automerge', 1);
} {}
do_catchsql_test 9.3.1 {
INSERT INTO abc(abc, rank) VALUES('crisismerge', -5);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.3.2 {
INSERT INTO abc(abc, rank) VALUES('crisismerge', 66.67);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_execsql_test 9.3.3 {
INSERT INTO abc(abc, rank) VALUES('crisismerge', 1);
} {}
@ -205,14 +205,14 @@ do_execsql_test 9.3.4 {
do_catchsql_test 9.4.1 {
INSERT INTO abc(abc, rank) VALUES('nosuchoption', 1);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.5.1 {
INSERT INTO abc(abc, rank) VALUES('hashsize', 'not an integer');
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.5.2 {
INSERT INTO abc(abc, rank) VALUES('hashsize', -500000);
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
do_catchsql_test 9.5.3 {
INSERT INTO abc(abc, rank) VALUES('hashsize', 500000);
} {0 {}}
@ -245,7 +245,7 @@ foreach {tn opt} {
do_catchsql_test 12.1 {
INSERT INTO t1(t1, rank) VALUES('rank', NULL);;
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
#-------------------------------------------------------------------------
# errors in the 'usermerge' option
@ -260,8 +260,7 @@ foreach {tn val} {
4 1
} {
set sql "INSERT INTO tt(tt, rank) VALUES('usermerge', $val)"
do_catchsql_test 13.$tn $sql {1 {SQL logic error or missing database}}
do_catchsql_test 13.$tn $sql {1 {SQL logic error}}
}
finish_test

View File

@ -66,5 +66,3 @@ do_execsql_test 2.1 {
}
finish_test

View File

@ -255,4 +255,3 @@ do_execsql_test 6.2 {
finish_test

View File

@ -96,4 +96,3 @@ do_catchsql_test 3.1 {
} {1 {database disk image is malformed}}
finish_test

View File

@ -269,4 +269,3 @@ do_catchsql_test 6.2 {
sqlite3_fts5_may_be_corrupt 0
finish_test

View File

@ -409,4 +409,3 @@ do_catchsql_test 9.2.2 {
sqlite3_fts5_may_be_corrupt 0
finish_test

View File

@ -51,4 +51,3 @@ do_test 1.2 {
} {}
finish_test

View File

@ -241,4 +241,3 @@ do_execsql_test 5.3 {
finish_test

View File

@ -63,5 +63,3 @@ foreach_detail_mode $::testprefix {
finish_test

View File

@ -197,4 +197,3 @@ foreach v $vocab {
finish_test

View File

@ -44,4 +44,3 @@ do_execsql_test 1.2 {
finish_test

View File

@ -81,6 +81,3 @@ do_execsql_test 3.3 {
finish_test

View File

@ -351,4 +351,3 @@ do_faultsim_test 9.1 -faults oom-* -prep {
finish_test

View File

@ -137,4 +137,3 @@ do_faultsim_test 5.0 -faults oom-* -prep {
}
finish_test

View File

@ -110,4 +110,3 @@ do_faultsim_test 3.2 -faults oom-* -prep {
finish_test

View File

@ -395,4 +395,3 @@ do_faultsim_test 14.1 -faults oom-t* -prep {
}
finish_test

View File

@ -130,4 +130,3 @@ do_faultsim_test 3.3 -faults oom-t* -body {
finish_test

View File

@ -292,4 +292,3 @@ do_faultsim_test 6 -faults oom* -prep {
db close
}
finish_test

View File

@ -116,4 +116,3 @@ do_faultsim_test 2.2 -faults oom-* -body {
}
finish_test

View File

@ -82,4 +82,3 @@ do_faultsim_test 4 -faults oom-* -prep {
finish_test

View File

@ -153,4 +153,3 @@ do_faultsim_test 6 -faults oom-* -body {
} ;# foreach_detail_mode...
finish_test

View File

@ -61,4 +61,3 @@ do_faultsim_test 2 -faults oom* -prep {
faultsim_test_result {0 {1 2}}
}
finish_test

View File

@ -132,4 +132,3 @@ do_faultsim_test 4.2 -faults oom* -body {
finish_test

View File

@ -40,4 +40,3 @@ do_test 1.1 {
finish_test

View File

@ -90,4 +90,3 @@ do_catchsql_test 4.1 {
} {1 {fts5: syntax error near "`"}}
finish_test

View File

@ -130,4 +130,3 @@ breakpoint
} ;# foreach_detail_mode
finish_test

View File

@ -210,4 +210,3 @@ foreach {tn pgsz} {
}
finish_test

View File

@ -70,4 +70,3 @@ do_execsql_test 1.6 {
finish_test

View File

@ -41,5 +41,3 @@ do_execsql_test 1.2 {
} {1 abc 2 abc}
finish_test

View File

@ -492,4 +492,3 @@ do_catchsql_test 14.2 {
} {1 {unrecognized matchinfo flag: d}}
finish_test

View File

@ -241,4 +241,3 @@ do_execsql_test 6.3 {
finish_test

View File

@ -55,4 +55,3 @@ do_execsql_test 1.2 {
}
finish_test

View File

@ -45,4 +45,3 @@ do_multiclient_test tn {
};# do_multiclient_test
};# foreach_detail_mode
finish_test

View File

@ -68,4 +68,3 @@ do_near_test 1.25 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 4) } 0
finish_test

View File

@ -178,4 +178,3 @@ do_execsql_test 4.3.1 {
do_test 4.2.2 { fts5_level_segs ttt } {3}
finish_test

View File

@ -106,4 +106,3 @@ foreach {tn nStep} {
do_test 2.$tn.6 { fts5_segcount t1 } 1
}
finish_test

View File

@ -116,4 +116,3 @@ do_execsql_test 2.0 {
}
finish_test

View File

@ -64,4 +64,3 @@ do_eqp_test 1.5 {
finish_test

View File

@ -11803,4 +11803,3 @@ foreach {in out} $test_vocab {
finish_test

View File

@ -67,4 +67,3 @@ foreach {in out} $test_vocab {
finish_test

View File

@ -341,5 +341,3 @@ foreach {tn create} {
}
finish_test

View File

@ -79,5 +79,3 @@ for {set tn 1 ; set pgsz 64} {$tn<32} {incr tn; incr pgsz 16} {
finish_test

View File

@ -152,4 +152,3 @@ do_execsql_test 4.1 {
finish_test

View File

@ -64,4 +64,3 @@ do_catchsql_test 2.2 {
INSERT INTO nc(nc) VALUES('rebuild');
} {1 {'rebuild' may not be used with a contentless fts5 table}}
finish_test

View File

@ -149,4 +149,3 @@ do_test 4.3 {
finish_test

View File

@ -216,4 +216,3 @@ do_execsql_test 6.2 {
finish_test

View File

@ -370,4 +370,3 @@ do_execsql_test 17.6 {
#db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM t2_data} {puts $r}
finish_test

View File

@ -116,4 +116,3 @@ do_execsql_test 4.6 {
finish_test

View File

@ -421,4 +421,3 @@ do_execsql_test 7.1.2 {
} ;# foreach_detail_mode
finish_test

View File

@ -161,4 +161,3 @@ foreach {tn expr} {
}
finish_test

View File

@ -109,7 +109,7 @@ do_catchsql_test 2.0 {
do_catchsql_test 2.1 {
CREATE VIRTUAL TABLE t4 USING fts5tokenize;
SELECT * FROM t4;
} {1 {SQL logic error or missing database}}
} {1 {SQL logic error}}
finish_test

View File

@ -302,4 +302,3 @@ do_test 9.5.2 { set ::flags } {query}
finish_test

View File

@ -59,4 +59,3 @@ do_execsql_test 2.1 "
finish_test

View File

@ -126,4 +126,3 @@ do_test 1.5 {
finish_test

View File

@ -76,4 +76,3 @@ do_execsql_test 3.2 {
finish_test

View File

@ -117,5 +117,3 @@ do_execsql_test 2.2.integrity {
}
finish_test

View File

@ -61,4 +61,3 @@ do_test 1.7 {
finish_test

View File

@ -1,9 +1,9 @@
#
# This Makefile is designed for use with main.mk in the root directory of
# this project. After including main.mk, the users makefile should contain:
# This Makefile is designed for use with Makefile.msc in the root directory
# of this project. The Makefile.msc should contain:
#
# LSMDIR=$(TOP)\ext\lsm1\
# include $(LSMDIR)\Makefile.msc
# LSMDIR=$(TOP)\ext\lsm1
# !INCLUDE $(LSMDIR)\Makefile.msc
#
# The most useful targets are [lsmtest.exe] and [lsm.dll].
#
@ -39,9 +39,17 @@ LSMTESTSRC = $(LSMDIR)\lsm-test\lsmtest1.c $(LSMDIR)\lsm-test\lsmtest2.c \
$(LSMDIR)\lsm-test\lsmtest_tdb.c $(LSMDIR)\lsm-test\lsmtest_tdb3.c \
$(LSMDIR)\lsm-test\lsmtest_util.c $(LSMDIR)\lsm-test\lsmtest_win32.c
# all: lsm.dll
# all: lsm.dll lsmtest.exe
LSMOPTS = -DLSM_MUTEX_WIN32=1 -I$(LSMDIR)
LSMOPTS = $(NO_WARN) -DLSM_MUTEX_WIN32=1 -I$(LSMDIR)
!IF $(DEBUG)>2
LSMOPTS = $(LSMOPTS) -DLSM_DEBUG=1
!ENDIF
!IF $(MEMDEBUG)!=0
LSMOPTS = $(LSMOPTS) -DLSM_DEBUG_MEM=1
!ENDIF
lsm_ckpt.lo: $(LSMDIR)\lsm_ckpt.c $(LSMHDR) $(SQLITE3H)
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_ckpt.c
@ -88,5 +96,5 @@ lsm_vtab.lo: $(LSMDIR)\lsm_vtab.c $(LSMHDR) $(SQLITE3H)
lsm.dll: $(LSMOBJ)
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ $(LSMOBJ)
lsmtest.exe: $(LSMOBJ) $(LSMTESTSRC) $(LSMTESTHDR) $(LIBOBJS1)
$(LTLINK) $(LSMOPTS) $(LSMTESTSRC) /link $(LSMOBJ) $(LIBOBJS1)
lsmtest.exe: $(LSMOBJ) $(LSMTESTSRC) $(LSMTESTHDR) $(LIBOBJ)
$(LTLINK) $(LSMOPTS) $(LSMTESTSRC) /link $(LSMOBJ) $(LIBOBJ)

View File

@ -7,9 +7,19 @@
#include "lsm.h"
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef _WIN32
# include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#ifdef __cplusplus
extern "C" {
@ -20,7 +30,7 @@ extern "C" {
# define gettimeofday win32GetTimeOfDay
# define F_OK (0)
# define sleep(sec) Sleep(1000 * (sec))
# define usleep(usec) Sleep((usec) / 1000)
# define usleep(usec) Sleep(((usec) + 999) / 1000)
# ifdef _MSC_VER
# include <io.h>
# define snprintf _snprintf
@ -52,6 +62,10 @@ typedef unsigned long long int u64;
#define TESTDB_DEFAULT_PAGE_SIZE 4096
#define TESTDB_DEFAULT_CACHE_SIZE 2048
#ifndef _O_BINARY
# define _O_BINARY (0)
#endif
/*
** Ideally, these should be in wrapper.c. But they are here instead so that
** they can be used by the C++ database wrappers in wrapper2.cc.

View File

@ -96,7 +96,7 @@ int testControlDb(TestDb **ppDb){
#ifdef HAVE_KYOTOCABINET
return tdb_open("kyotocabinet", "tmp.db", 1, ppDb);
#else
return tdb_open("sqlite3", ":memory:", 1, ppDb);
return tdb_open("sqlite3", "", 1, ppDb);
#endif
}

View File

@ -200,7 +200,7 @@ static void testOomScan(
int rc;
int iScan = 0;
lsm_cursor *pCsr;
int (*xAdvance)(lsm_cursor *);
int (*xAdvance)(lsm_cursor *) = 0;
rc = lsm_csr_open(pDb, &pCsr);
@ -240,13 +240,6 @@ static void testOomScan(
#define LSMTEST6_TESTDB "testdb.lsm"
#ifndef _WIN32
# include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void testDeleteLsmdb(const char *zFile){
char *zLog = testMallocPrintf("%s-log", zFile);
char *zShm = testMallocPrintf("%s-shm", zFile);
@ -257,7 +250,7 @@ void testDeleteLsmdb(const char *zFile){
testFree(zShm);
}
static void copy_file(const char *zFrom, const char *zTo){
static void copy_file(const char *zFrom, const char *zTo, int isDatabase){
if( access(zFrom, F_OK) ){
unlink(zTo);
@ -269,8 +262,8 @@ static void copy_file(const char *zFrom, const char *zTo){
struct stat buf;
u8 *aBuf;
fd1 = open(zFrom, O_RDONLY, 0644);
fd2 = open(zTo, O_RDWR | O_CREAT, 0644);
fd1 = open(zFrom, O_RDONLY | _O_BINARY, 0644);
fd2 = open(zTo, O_RDWR | O_CREAT | _O_BINARY, 0644);
fstat(fd1, &buf);
sz = buf.st_size;
@ -278,9 +271,15 @@ static void copy_file(const char *zFrom, const char *zTo){
aBuf = testMalloc(4096);
for(i=0; i<sz; i+=4096){
int nByte = MIN(4096, sz - i);
int bLockPage = isDatabase && i == 0;
int nByte = MIN((bLockPage ? 4066 : 4096), sz - i);
memset(aBuf, 0, 4096);
read(fd1, aBuf, nByte);
write(fd2, aBuf, nByte);
if( bLockPage ){
lseek(fd1, 4096, SEEK_SET);
lseek(fd2, 4096, SEEK_SET);
}
}
testFree(aBuf);
@ -298,9 +297,9 @@ void testCopyLsmdb(const char *zFrom, const char *zTo){
unlink(zShm2);
unlink(zLog2);
unlink(zTo);
copy_file(zFrom, zTo);
copy_file(zLog1, zLog2);
copy_file(zShm1, zShm2);
copy_file(zFrom, zTo, 1);
copy_file(zLog1, zLog2, 0);
copy_file(zShm1, zShm2, 0);
testFree(zLog1); testFree(zLog2); testFree(zShm1); testFree(zShm2);
}
@ -322,8 +321,8 @@ void testSaveDb(const char *zFile, const char *zAux){
unlink(zFileSave);
unlink(zLogSave);
copy_file(zFile, zFileSave);
copy_file(zLog, zLogSave);
copy_file(zFile, zFileSave, 1);
copy_file(zLog, zLogSave, 0);
testFree(zLog); testFree(zFileSave); testFree(zLogSave);
}
@ -341,8 +340,8 @@ void testRestoreDb(const char *zFile, const char *zAux){
char *zFileSave = testMallocPrintf("%s-save", zFile);
char *zLogSave = testMallocPrintf("%s-%s-save", zFile, zAux);
copy_file(zFileSave, zFile);
copy_file(zLogSave, zLog);
copy_file(zFileSave, zFile, 1);
copy_file(zLogSave, zLog, 0);
testFree(zLog); testFree(zFileSave); testFree(zLogSave);
}
@ -354,7 +353,7 @@ static int lsmWriteStr(lsm_db *pDb, const char *zKey, const char *zVal){
return lsm_insert(pDb, (void *)zKey, nKey, (void *)zVal, nVal);
}
static void setup_delete_db(){
static void setup_delete_db(void){
testDeleteLsmdb(LSMTEST6_TESTDB);
}
@ -370,7 +369,7 @@ static void setup_delete_db(){
** "seven" -> "fourtynine"
** "eight" -> "sixtyfour"
*/
static void setup_populate_db(){
static void setup_populate_db(void){
const char *azStr[] = {
"one", "one",
"two", "four",
@ -412,7 +411,7 @@ static Datasource *getDatasource(void){
** * Contains 5000 key-value pairs starting at 0 from the
** datasource returned getDatasource().
*/
static void setup_populate_db2(){
static void setup_populate_db2(void){
Datasource *pData;
int ii;
int rc;

View File

@ -85,14 +85,15 @@ static void setupDatabase1(TestDb *pDb, Datasource **ppData){
void testReadFile(const char *zFile, int iOff, void *pOut, int nByte, int *pRc){
if( *pRc==0 ){
FILE *fd;
fd = fopen(zFile, "r");
fd = fopen(zFile, "rb");
if( fd==0 ){
*pRc = 1;
}else{
if( 0!=fseek(fd, iOff, SEEK_SET) ){
*pRc = 1;
}else{
if( nByte!=fread(pOut, 1, nByte, fd) ){
assert( nByte>=0 );
if( (size_t)nByte!=fread(pOut, 1, nByte, fd) ){
*pRc = 1;
}
}
@ -110,14 +111,15 @@ void testWriteFile(
){
if( *pRc==0 ){
FILE *fd;
fd = fopen(zFile, "r+");
fd = fopen(zFile, "r+b");
if( fd==0 ){
*pRc = 1;
}else{
if( 0!=fseek(fd, iOff, SEEK_SET) ){
*pRc = 1;
}else{
if( nByte!=fwrite(pOut, 1, nByte, fd) ){
assert( nByte>=0 );
if( (size_t)nByte!=fwrite(pOut, 1, nByte, fd) ){
*pRc = 1;
}
}
@ -155,50 +157,52 @@ static ShmHeader *getShmHeader(const char *zDb){
** 5) Check once more that the checksum is still zCksum.
*/
static void doLiveRecovery(const char *zDb, const char *zCksum, int *pRc){
const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 20, 25, 100, 500};
Datasource *pData;
const char *zCopy = "testcopy.lsm";
char zCksum2[TEST_CKSUM_BYTES];
TestDb *pDb = 0;
int rc;
if( *pRc==LSM_OK ){
const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 20, 25, 100, 500};
Datasource *pData;
const char *zCopy = "testcopy.lsm";
char zCksum2[TEST_CKSUM_BYTES];
TestDb *pDb = 0;
int rc;
pData = testDatasourceNew(&defn);
testCopyLsmdb(zDb, zCopy);
rc = tdb_lsm_open("test_no_recovery=1", zCopy, 0, &pDb);
if( rc==0 ){
ShmHeader *pHdr;
lsm_db *db;
testCksumDatabase(pDb, zCksum2);
testCompareStr(zCksum, zCksum2, &rc);
testWriteDatasourceRange(pDb, pData, 1, 10, &rc);
testDeleteDatasourceRange(pDb, pData, 1, 10, &rc);
/* Test that the two tree-headers are now consistent. */
pHdr = getShmHeader(zCopy);
if( rc==0 && memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(pHdr->hdr1)) ){
rc = 1;
}
testFree(pHdr);
pData = testDatasourceNew(&defn);
testCopyLsmdb(zDb, zCopy);
rc = tdb_lsm_open("test_no_recovery=1", zCopy, 0, &pDb);
if( rc==0 ){
int nBuf = 64;
db = tdb_lsm(pDb);
lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
lsm_begin(db, 1);
lsm_commit(db, 0);
rc = lsm_work(db, 0, 0, 0);
ShmHeader *pHdr;
lsm_db *db;
testCksumDatabase(pDb, zCksum2);
testCompareStr(zCksum, zCksum2, &rc);
testWriteDatasourceRange(pDb, pData, 1, 10, &rc);
testDeleteDatasourceRange(pDb, pData, 1, 10, &rc);
/* Test that the two tree-headers are now consistent. */
pHdr = getShmHeader(zCopy);
if( rc==0 && memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(pHdr->hdr1)) ){
rc = 1;
}
testFree(pHdr);
if( rc==0 ){
int nBuf = 64;
db = tdb_lsm(pDb);
lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
lsm_begin(db, 1);
lsm_commit(db, 0);
rc = lsm_work(db, 0, 0, 0);
}
testCksumDatabase(pDb, zCksum2);
testCompareStr(zCksum, zCksum2, &rc);
}
testCksumDatabase(pDb, zCksum2);
testCompareStr(zCksum, zCksum2, &rc);
testDatasourceFree(pData);
testClose(&pDb);
testDeleteLsmdb(zCopy);
*pRc = rc;
}
testDatasourceFree(pData);
testClose(&pDb);
testDeleteLsmdb(zCopy);
*pRc = rc;
}
static void doWriterCrash1(int *pRc){

View File

@ -55,14 +55,6 @@
#include "lsmtest.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef _WIN32
# include <unistd.h>
#endif
#include <ctype.h>
typedef struct IoContext IoContext;
struct IoContext {
@ -155,7 +147,7 @@ static int doOneCmd(
int nPg;
int iPg;
nByte = getNextSize(z, &z, &rc);
nByte = (int)getNextSize(z, &z, &rc);
if( rc || *z!='@' ) goto bad_command;
z++;
iOff = getNextSize(z, &z, &rc);
@ -163,7 +155,7 @@ static int doOneCmd(
if( pzOut ) *pzOut = z;
nPg = (nByte+pgsz-1) / pgsz;
lseek(pCtx->fd, iOff, SEEK_SET);
lseek(pCtx->fd, (off_t)iOff, SEEK_SET);
for(iPg=0; iPg<nPg; iPg++){
write(pCtx->fd, aData, pgsz);
}
@ -219,7 +211,7 @@ int do_io(int nArg, char **azArg){
zFile = azArg[0];
zPgsz = azArg[1];
pgsz = getNextSize(zPgsz, 0, &rc);
pgsz = (int)getNextSize(zPgsz, 0, &rc);
if( pgsz<=0 ){
testPrintError("Ridiculous page size: %d", pgsz);
return -1;
@ -227,7 +219,7 @@ int do_io(int nArg, char **azArg){
aData = malloc(pgsz);
memset(aData, 0x77, pgsz);
ctx.fd = open(zFile, O_RDWR|O_CREAT, 0644);
ctx.fd = open(zFile, O_RDWR|O_CREAT|_O_BINARY, 0644);
if( ctx.fd<0 ){
perror("open: ");
return -1;

View File

@ -1,22 +1,7 @@
#include "stdarg.h"
#include "lsmtest.h"
#include "stdio.h"
#include "assert.h"
#include "string.h"
#include "stdlib.h"
#include <sqlite3.h>
#ifndef _WIN32
# include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void test_failed(){
assert( 0 );
return;
@ -917,7 +902,7 @@ int do_speed_tests(int nArg, char **azArg){
testTimeInit();
for(i=0; i<nRow; i+=nStep){
int iStep;
int nWrite1, nWrite2;
int nWrite1 = 0, nWrite2 = 0;
testCaseProgress(i, nRow, testCaseNDot(), &iDot);
if( pLsm ) lsm_info(pLsm, LSM_INFO_NWRITE, &nWrite1);
for(iStep=0; iStep<nStep; iStep++){
@ -1239,7 +1224,7 @@ static int do_writer_test(int nArg, char **azArg){
printf("Preparing file... ");
fflush(stdout);
unlink("writer.out");
fd = open("writer.out", O_RDWR|O_CREAT, 0664);
fd = open("writer.out", O_RDWR|O_CREAT|_O_BINARY, 0664);
if( fd<0 ){
testPrintError("open(): %d - %s\n", errno, strerror(errno));
return -1;

View File

@ -45,9 +45,9 @@ struct TmGlobal {
void (*xDelMutex)(TmGlobal*); /* Call this to delete mutex */
void *pMutex; /* Mutex handle */
void *xSaveMalloc;
void *xSaveRealloc;
void *xSaveFree;
void *(*xSaveMalloc)(void *, size_t);
void *(*xSaveRealloc)(void *, void *, size_t);
void (*xSaveFree)(void *, void *);
/* OOM injection scheduling. If nCountdown is greater than zero when a
** malloc attempt is made, it is decremented. If this means nCountdown
@ -183,7 +183,7 @@ static void tmFree(TmGlobal *pTm, void *p){
u8 *pUser = (u8 *)p;
tmEnterMutex(pTm);
pHdr = (TmBlockHdr *)&pUser[BLOCK_HDR_SIZE * -1];
pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE);
assert( pHdr->iForeGuard==FOREGUARD );
assert( 0==memcmp(&pUser[pHdr->nByte], &rearguard, 4) );
@ -218,7 +218,7 @@ static void *tmRealloc(TmGlobal *pTm, void *p, int nByte){
if( pNew && p ){
TmBlockHdr *pHdr;
u8 *pUser = (u8 *)p;
pHdr = (TmBlockHdr *)&pUser[BLOCK_HDR_SIZE * -1];
pHdr = (TmBlockHdr *)(pUser - BLOCK_HDR_SIZE);
memcpy(pNew, p, MIN(nByte, pHdr->nByte));
tmFree(pTm, p);
}
@ -355,9 +355,9 @@ void testMallocInstall(lsm_env *pEnv){
pGlobal->xDelMutex = tmLsmMutexDel;
pGlobal->pMutex = (void *)pMutex;
pGlobal->xSaveMalloc = (void *)pEnv->xMalloc;
pGlobal->xSaveRealloc = (void *)pEnv->xRealloc;
pGlobal->xSaveFree = (void *)pEnv->xFree;
pGlobal->xSaveMalloc = pEnv->xMalloc;
pGlobal->xSaveRealloc = pEnv->xRealloc;
pGlobal->xSaveFree = pEnv->xFree;
/* Set up pEnv to the use the new TmGlobal */
pEnv->pMemCtx = (void *)pGlobal;

View File

@ -767,7 +767,8 @@ int tdb_open(const char *zLib, const char *zDb, int bClear, TestDb **ppDb){
if( *zSpec=='\0' ) zSpec = 0;
for(i=0; i<ArraySize(aLib); i++){
if( strlen(aLib[i].zName)==nLib && 0==memcmp(zLib, aLib[i].zName, nLib) ){
if( (int)strlen(aLib[i].zName)==nLib
&& 0==memcmp(zLib, aLib[i].zName, nLib) ){
rc = aLib[i].xOpen(zSpec, (zDb ? zDb : aLib[i].zDefaultDb), bClear, ppDb);
if( rc==0 ){
(*ppDb)->zLibrary = aLib[i].zName;

View File

@ -199,33 +199,33 @@ static int testEnvWrite(lsm_file *pFile, lsm_i64 iOff, void *pData, int nData){
if( pDb->bCrashed ) return LSM_IOERR;
if( pDb->bPrepareCrash ){
FileData *pData = &pDb->aFile[p->bLog];
FileData *pData2 = &pDb->aFile[p->bLog];
int iFirst;
int iLast;
int iSector;
iFirst = (iOff / pDb->szSector);
iLast = ((iOff + nData - 1) / pDb->szSector);
iFirst = (int)(iOff / pDb->szSector);
iLast = (int)((iOff + nData - 1) / pDb->szSector);
if( pData->nSector<(iLast+1) ){
if( pData2->nSector<(iLast+1) ){
int nNew = ( ((iLast + 1) + 63) / 64 ) * 64;
assert( nNew>iLast );
pData->aSector = (FileSector *)testRealloc(
pData->aSector, nNew*sizeof(FileSector)
pData2->aSector = (FileSector *)testRealloc(
pData2->aSector, nNew*sizeof(FileSector)
);
memset(&pData->aSector[pData->nSector],
0, (nNew - pData->nSector) * sizeof(FileSector)
memset(&pData2->aSector[pData2->nSector],
0, (nNew - pData2->nSector) * sizeof(FileSector)
);
pData->nSector = nNew;
pData2->nSector = nNew;
}
for(iSector=iFirst; iSector<=iLast; iSector++){
if( pData->aSector[iSector].aOld==0 ){
if( pData2->aSector[iSector].aOld==0 ){
u8 *aOld = (u8 *)testMalloc(pDb->szSector);
pRealEnv->xRead(
p->pReal, (lsm_i64)iSector*pDb->szSector, aOld, pDb->szSector
);
pData->aSector[iSector].aOld = aOld;
pData2->aSector[iSector].aOld = aOld;
}
}
}
@ -547,9 +547,9 @@ static int waitOnWorker(LsmDb *pDb){
rc = lsm_config(pDb->db, LSM_CONFIG_AUTOFLUSH, &nLimit);
do {
int nOld, nNew, rc;
rc = lsm_info(pDb->db, LSM_INFO_TREE_SIZE, &nOld, &nNew);
if( rc!=LSM_OK ) return rc;
int nOld, nNew, rc2;
rc2 = lsm_info(pDb->db, LSM_INFO_TREE_SIZE, &nOld, &nNew);
if( rc2!=LSM_OK ) return rc2;
if( nOld==0 || nNew<(nLimit/2) ) break;
#ifdef LSM_MUTEX_PTHREADS
mt_signal_worker(pDb, 0);
@ -888,7 +888,9 @@ int test_lsm_config_str(
int tdb_lsm_config_str(TestDb *pDb, const char *zStr){
int rc = 0;
if( tdb_lsm(pDb) ){
#ifdef LSM_MUTEX_PTHREADS
int i;
#endif
LsmDb *pLsm = (LsmDb *)pDb;
rc = test_lsm_config_str(pLsm, pLsm->db, 0, zStr, 0);

View File

@ -12,19 +12,19 @@ int win32GetTimeOfDay(
void *tzp
){
FILETIME fileTime;
ULARGE_INTEGER largeInteger;
ULONGLONG ticks;
ULONGLONG unixTicks;
unused_parameter(tzp);
memset(&fileTime, 0, sizeof(FILETIME));
GetSystemTimeAsFileTime(&fileTime);
memset(&largeInteger, 0, sizeof(ULARGE_INTEGER));
largeInteger.LowPart = fileTime.dwLowDateTime;
largeInteger.HighPart = fileTime.dwHighDateTime;
ticks = largeInteger.QuadPart - TICKS_UNIX_EPOCH;
tp->tv_sec = (long)(ticks / TICKS_PER_SECOND);
ticks -= ((ULONGLONG)tp->tv_sec * TICKS_PER_SECOND);
tp->tv_usec = (long)(ticks / TICKS_PER_MICROSECOND);
ticks = (ULONGLONG)fileTime.dwHighDateTime << 32;
ticks |= (ULONGLONG)fileTime.dwLowDateTime;
unixTicks = ticks - TICKS_UNIX_EPOCH;
tp->tv_sec = (long)(unixTicks / TICKS_PER_SECOND);
unixTicks -= ((ULONGLONG)tp->tv_sec * TICKS_PER_SECOND);
tp->tv_usec = (long)(unixTicks / TICKS_PER_MICROSECOND);
return 0;
}
#endif

View File

@ -589,7 +589,7 @@ struct Snapshot {
/*
** Functions from file "lsm_ckpt.c".
*/
int lsmCheckpointWrite(lsm_db *, int, u32 *);
int lsmCheckpointWrite(lsm_db *, u32 *);
int lsmCheckpointLevels(lsm_db *, int, void **, int *);
int lsmCheckpointLoadLevels(lsm_db *pDb, void *pVal, int nVal);
@ -696,6 +696,8 @@ int lsmFsOpenLog(lsm_db *, int *);
void lsmFsCloseLog(lsm_db *);
void lsmFsClose(FileSystem *);
int lsmFsUnmap(FileSystem *);
int lsmFsConfigure(lsm_db *db);
int lsmFsBlockSize(FileSystem *);

View File

@ -222,7 +222,7 @@ struct CkptBuffer {
** at aCkpt[].
*/
static void ckptChecksum(u32 *aCkpt, u32 nCkpt, u32 *piCksum1, u32 *piCksum2){
int i;
u32 i;
u32 cksum1 = 1;
u32 cksum2 = 2;
@ -511,7 +511,7 @@ static void ckptNewSegment(
pSegment->iFirst = ckptGobble64(aIn, piIn);
pSegment->iLastPg = ckptGobble64(aIn, piIn);
pSegment->iRoot = ckptGobble64(aIn, piIn);
pSegment->nSize = ckptGobble64(aIn, piIn);
pSegment->nSize = (int)ckptGobble64(aIn, piIn);
assert( pSegment->iFirst );
}
@ -1017,9 +1017,9 @@ int lsmCheckpointDeserialize(
pDb->pEnv, sizeof(FreelistEntry)*nFree, &rc
);
if( rc==LSM_OK ){
int i;
for(i=0; i<nFree; i++){
FreelistEntry *p = &pNew->freelist.aEntry[i];
int j;
for(j=0; j<nFree; j++){
FreelistEntry *p = &pNew->freelist.aEntry[j];
p->iBlk = aCkpt[iIn++];
p->iId = ((i64)(aCkpt[iIn])<<32) + aCkpt[iIn+1];
iIn += 2;

View File

@ -540,7 +540,7 @@ static int fsMmapPage(FileSystem *pFS, Pgno iReal){
** Given that there are currently nHash slots in the hash table, return
** the hash key for file iFile, page iPg.
*/
static int fsHashKey(int nHash, int iPg){
static int fsHashKey(int nHash, Pgno iPg){
return (iPg % nHash);
}
@ -922,9 +922,9 @@ static Pgno fsLastPageOnBlock(FileSystem *pFS, int iBlock){
*/
static int fsPageToBlock(FileSystem *pFS, Pgno iPg){
if( pFS->pCompress ){
return (iPg / pFS->nBlocksize) + 1;
return (int)((iPg / pFS->nBlocksize) + 1);
}else{
return 1 + ((iPg-1) / (pFS->nBlocksize / pFS->nPagesize));
return (int)(1 + ((iPg-1) / (pFS->nBlocksize / pFS->nPagesize)));
}
}
@ -1150,6 +1150,17 @@ static void fsGrowMapping(
}
}
/*
** If it is mapped, unmap the database file.
*/
int lsmFsUnmap(FileSystem *pFS){
int rc = LSM_OK;
if( pFS ){
rc = lsmEnvRemap(pFS->pEnv, pFS->fdDb, -1, &pFS->pMap, &pFS->nMap);
}
return rc;
}
/*
** fsync() the database file.
*/
@ -1286,7 +1297,7 @@ static int fsReadData(
assert( pFS->pCompress );
iEob = fsLastPageOnPagesBlock(pFS, iOff) + 1;
nRead = LSM_MIN(iEob - iOff, nData);
nRead = (int)LSM_MIN(iEob - iOff, nData);
rc = lsmEnvRead(pFS->pEnv, pFS->fdDb, iOff, aData, nRead);
if( rc==LSM_OK && nRead!=nData ){
@ -1722,8 +1733,8 @@ static int fsFreeBlock(
int iBlk /* Block number of block to free */
){
int rc = LSM_OK; /* Return code */
int iFirst; /* First page on block iBlk */
int iLast; /* Last page on block iBlk */
Pgno iFirst; /* First page on block iBlk */
Pgno iLast; /* Last page on block iBlk */
Level *pLevel; /* Used to iterate through levels */
int iIn; /* Used to iterate through append points */
@ -1856,7 +1867,7 @@ void lsmFsGobble(
assert( nPgno>0 && 0==fsPageRedirects(pFS, pRun, aPgno[0]) );
iBlk = fsPageToBlock(pFS, pRun->iFirst);
pRun->nSize += (pRun->iFirst - fsFirstPageOnBlock(pFS, iBlk));
pRun->nSize += (int)(pRun->iFirst - fsFirstPageOnBlock(pFS, iBlk));
while( rc==LSM_OK ){
int iNext = 0;
@ -1867,13 +1878,13 @@ void lsmFsGobble(
}
rc = fsBlockNext(pFS, pRun, iBlk, &iNext);
if( rc==LSM_OK ) rc = fsFreeBlock(pFS, pSnapshot, pRun, iBlk);
pRun->nSize -= (
pRun->nSize -= (int)(
1 + fsLastPageOnBlock(pFS, iBlk) - fsFirstPageOnBlock(pFS, iBlk)
);
iBlk = iNext;
}
pRun->nSize -= (pRun->iFirst - fsFirstPageOnBlock(pFS, iBlk));
pRun->nSize -= (int)(pRun->iFirst - fsFirstPageOnBlock(pFS, iBlk));
assert( pRun->nSize>0 );
}
@ -2087,10 +2098,10 @@ int lsmFsSortedAppend(
){
int rc = LSM_OK;
Page *pPg = 0;
int iApp = 0;
int iNext = 0;
Pgno iApp = 0;
Pgno iNext = 0;
Segment *p = &pLvl->lhs;
int iPrev = p->iLastPg;
Pgno iPrev = p->iLastPg;
*ppOut = 0;
assert( p->pRedirect==0 );
@ -2116,10 +2127,10 @@ int lsmFsSortedAppend(
if( iPrev==0 ){
iApp = findAppendPoint(pFS, pLvl);
}else if( fsIsLast(pFS, iPrev) ){
int iNext;
rc = fsBlockNext(pFS, 0, fsPageToBlock(pFS, iPrev), &iNext);
int iNext2;
rc = fsBlockNext(pFS, 0, fsPageToBlock(pFS, iPrev), &iNext2);
if( rc!=LSM_OK ) return rc;
iApp = fsFirstPageOnBlock(pFS, iNext);
iApp = fsFirstPageOnBlock(pFS, iNext2);
}else{
iApp = iPrev + 1;
}
@ -2457,8 +2468,8 @@ static Pgno fsAppendData(
int rc = *pRc;
assert( pFS->pCompress );
if( rc==LSM_OK ){
int nRem;
int nWrite;
int nRem = 0;
int nWrite = 0;
Pgno iLastOnBlock;
Pgno iApp = pSeg->iLastPg+1;
@ -2477,7 +2488,7 @@ static Pgno fsAppendData(
/* Write as much data as is possible at iApp (usually all of it). */
iLastOnBlock = fsLastPageOnPagesBlock(pFS, iApp);
if( rc==LSM_OK ){
int nSpace = iLastOnBlock - iApp + 1;
int nSpace = (int)(iLastOnBlock - iApp + 1);
nWrite = LSM_MIN(nData, nSpace);
nRem = nData - nWrite;
assert( nWrite>=0 );
@ -2800,7 +2811,7 @@ int lsmFsSortedPadding(
iLast2 = (1 + iLast/pFS->szSector) * pFS->szSector - 1;
assert( fsPageToBlock(pFS, iLast)==fsPageToBlock(pFS, iLast2) );
nPad = iLast2 - iLast;
nPad = (int)(iLast2 - iLast);
if( iLast2>fsLastPageOnPagesBlock(pFS, iLast) ){
nPad -= 4;
@ -3257,10 +3268,10 @@ int lsmFsIntegrityCheck(lsm_db *pDb){
}
for(pLevel=pWorker->pLevel; pLevel; pLevel=pLevel->pNext){
int i;
int j;
checkBlocks(pFS, &pLevel->lhs, (pLevel->nRight!=0), nBlock, aUsed);
for(i=0; i<pLevel->nRight; i++){
checkBlocks(pFS, &pLevel->aRhs[i], 0, nBlock, aUsed);
for(j=0; j<pLevel->nRight; j++){
checkBlocks(pFS, &pLevel->aRhs[j], 0, nBlock, aUsed);
}
}

View File

@ -545,7 +545,7 @@ static int jumpIfRequired(
aPad[0] = LSM_LOG_PAD1;
}else{
aPad[0] = LSM_LOG_PAD2;
aPad[1] = (nPad-2);
aPad[1] = (u8)(nPad-2);
}
rc = lsmStringBinAppend(&pLog->buf, aPad, nPad);
if( rc!=LSM_OK ) return rc;
@ -627,7 +627,7 @@ static int logFlush(lsm_db *pDb, int eType){
}else{
int n = LSM_MIN(200, nPad-2);
pLog->buf.z[pLog->buf.n++] = LSM_LOG_PAD2;
pLog->buf.z[pLog->buf.n++] = n;
pLog->buf.z[pLog->buf.n++] = (char)n;
nPad -= 2;
memset(&pLog->buf.z[pLog->buf.n], 0x2B, n);
pLog->buf.n += n;
@ -640,7 +640,7 @@ static int logFlush(lsm_db *pDb, int eType){
** record. Then add the first byte of it. */
rc = lsmStringExtend(&pLog->buf, 9);
if( rc!=LSM_OK ) return rc;
pLog->buf.z[pLog->buf.n++] = eType;
pLog->buf.z[pLog->buf.n++] = (char)eType;
memset(&pLog->buf.z[pLog->buf.n], 0, 8);
rc = logCksumAndFlush(pDb);
@ -772,7 +772,7 @@ void lsmLogSeek(
assert( pMark->iOff<=pLog->iOff+pLog->buf.n );
if( (pMark->iOff & 0xFFFFFFF8)>=pLog->iOff ){
pLog->buf.n = pMark->iOff - pLog->iOff;
pLog->buf.n = (int)(pMark->iOff - pLog->iOff);
pLog->iCksumBuf = (pLog->buf.n & 0xFFFFFFF8);
}else{
pLog->buf.n = pMark->nBuf;

View File

@ -97,7 +97,7 @@ static void assertNotInFreelist(Freelist *p, int iBlk){
/*
** Append an entry to the free-list. If (iId==-1), this is a delete.
*/
int freelistAppend(lsm_db *db, int iBlk, i64 iId){
int freelistAppend(lsm_db *db, u32 iBlk, i64 iId){
lsm_env *pEnv = db->pEnv;
Freelist *p;
int i;
@ -230,7 +230,7 @@ static int dbTruncateFile(lsm_db *pDb){
/* If the last block that contains data is not already the last block in
** the database file, truncate the database file so that it is. */
if( rc==LSM_OK && ctx.nBlock!=pDb->pWorker->nBlock ){
if( rc==LSM_OK ){
rc = lsmFsTruncateDb(
pDb->pFS, (i64)ctx.nBlock*lsmFsBlockSize(pDb->pFS)
);
@ -253,6 +253,8 @@ static void doDbDisconnect(lsm_db *pDb){
rc = lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_EXCL, 1);
if( rc==LSM_OK ){
lsmShmLock(pDb, LSM_LOCK_DMS2, LSM_LOCK_UNLOCK, 0);
/* Try an exclusive lock on DMS2. If successful, this is the last
** connection to the database. In this case flush the contents of the
** in-memory tree to disk and write a checkpoint. */
@ -291,7 +293,7 @@ static void doDbDisconnect(lsm_db *pDb){
/* Write a checkpoint to disk. */
if( rc==LSM_OK ){
rc = lsmCheckpointWrite(pDb, (bReadonly==0), 0);
rc = lsmCheckpointWrite(pDb, 0);
}
/* If the checkpoint was written successfully, delete the log file
@ -310,6 +312,7 @@ static void doDbDisconnect(lsm_db *pDb){
/* The database may only be truncated if there exist no read-only
** clients - either connected or running rotrans transactions. */
if( bReadonly==0 && bRotrans==0 ){
lsmFsUnmap(pDb->pFS);
dbTruncateFile(pDb);
if( p->pFile && p->bMultiProc ){
lsmEnvShmUnmap(pDb->pEnv, p->pFile, 1);
@ -324,7 +327,6 @@ static void doDbDisconnect(lsm_db *pDb){
pDb->iRwclient = -1;
}
lsmShmLock(pDb, LSM_LOCK_DMS2, LSM_LOCK_UNLOCK, 0);
lsmShmLock(pDb, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK, 0);
}
pDb->pShmhdr = 0;
@ -477,8 +479,15 @@ int lsmDbDatabaseConnect(
}
if( rc==LSM_OK && p->bMultiProc==0 ){
/* Hold an exclusive lock DMS1 while grabbing DMS2. This ensures
** that any ongoing call to doDbDisconnect() (even one in another
** process) is finished before proceeding. */
assert( p->bReadonly==0 );
rc = lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS2, LSM_LOCK_EXCL);
rc = lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS1, LSM_LOCK_EXCL);
if( rc==LSM_OK ){
rc = lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS2, LSM_LOCK_EXCL);
lsmEnvLock(pDb->pEnv, p->pFile, LSM_LOCK_DMS1, LSM_LOCK_UNLOCK);
}
}
if( rc==LSM_OK ){
@ -560,6 +569,7 @@ void lsmDbDatabaseRelease(lsm_db *pDb){
doDbDisconnect(pDb);
}
lsmFsUnmap(pDb->pFS);
lsmMutexEnter(pDb->pEnv, p->pClientMutex);
for(ppDb=&p->pConn; *ppDb!=pDb; ppDb=&((*ppDb)->pNext));
*ppDb = pDb->pNext;
@ -631,11 +641,12 @@ static int walkFreelistCb(void *pCtx, int iBlk, i64 iSnapshot){
Freelist *pFree = p->pFreelist;
assert( p->bDone==0 );
assert( iBlk>=0 );
if( pFree ){
while( (p->iFree < pFree->nEntry) && p->iFree>=0 ){
FreelistEntry *pEntry = &pFree->aEntry[p->iFree];
if( (p->bReverse==0 && pEntry->iBlk>iBlk)
|| (p->bReverse!=0 && pEntry->iBlk<iBlk)
if( (p->bReverse==0 && pEntry->iBlk>(u32)iBlk)
|| (p->bReverse!=0 && pEntry->iBlk<(u32)iBlk)
){
break;
}else{
@ -646,7 +657,7 @@ static int walkFreelistCb(void *pCtx, int iBlk, i64 iSnapshot){
p->bDone = 1;
return 1;
}
if( pEntry->iBlk==iBlk ) return 0;
if( pEntry->iBlk==(u32)iBlk ) return 0;
}
}
}
@ -905,7 +916,7 @@ int lsmBlockRefree(lsm_db *pDb, int iBlk){
** not be held that long (in case it is required by a client flushing an
** in-memory tree to disk).
*/
int lsmCheckpointWrite(lsm_db *pDb, int bTruncate, u32 *pnWrite){
int lsmCheckpointWrite(lsm_db *pDb, u32 *pnWrite){
int rc; /* Return Code */
u32 nWrite = 0;
@ -929,7 +940,7 @@ int lsmCheckpointWrite(lsm_db *pDb, int bTruncate, u32 *pnWrite){
u8 *aData; /* Meta-page data buffer */
int nData; /* Size of aData[] in bytes */
i64 iCkpt; /* Id of checkpoint just loaded */
i64 iDisk; /* Id of checkpoint already stored in db */
i64 iDisk = 0; /* Id of checkpoint already stored in db */
iCkpt = lsmCheckpointId(pDb->aSnapshot, 0);
rc = lsmFsMetaPageGet(pDb->pFS, 0, pShm->iMetaPage, &pPg);
if( rc==LSM_OK ){
@ -960,10 +971,6 @@ int lsmCheckpointWrite(lsm_db *pDb, int bTruncate, u32 *pnWrite){
);
#endif
}
if( rc==LSM_OK && bTruncate && nBlock>0 ){
rc = lsmFsTruncateDb(pDb->pFS, (i64)nBlock*lsmFsBlockSize(pDb->pFS));
}
}
lsmShmLock(pDb, LSM_LOCK_CHECKPOINTER, LSM_LOCK_UNLOCK, 0);
@ -1747,7 +1754,10 @@ int lsmShmTestLock(
lsmMutexEnter(db->pEnv, p->pClientMutex);
for(pIter=p->pConn; pIter; pIter=pIter->pNext){
if( pIter!=db && (pIter->mLock & mask) ) break;
if( pIter!=db && (pIter->mLock & mask) ){
assert( pIter!=db );
break;
}
}
if( pIter ){
@ -1875,7 +1885,7 @@ int shmLockType(lsm_db *db, int iLock){
** (eOp==LSM_LOCK_EXCL) -> true if db has an EXCLUSIVE lock on iLock.
*/
int lsmShmAssertLock(lsm_db *db, int iLock, int eOp){
int ret;
int ret = 0;
int eHave;
assert( iLock>=1 && iLock<=LSM_LOCK_READER(LSM_LOCK_NREADER-1) );
@ -1954,7 +1964,7 @@ int lsm_checkpoint(lsm_db *pDb, int *pnKB){
/* Attempt the checkpoint. If successful, nWrite is set to the number of
** pages written between this and the previous checkpoint. */
rc = lsmCheckpointWrite(pDb, 0, &nWrite);
rc = lsmCheckpointWrite(pDb, &nWrite);
/* If required, calculate the output variable (KB of data checkpointed).
** Set it to zero if an error occured. */

View File

@ -662,9 +662,9 @@ static int btreeCursorPtr(u8 *aData, int nData, int iCell){
nCell = pageGetNRec(aData, nData);
if( iCell>=nCell ){
return pageGetPtr(aData, nData);
return (int)pageGetPtr(aData, nData);
}
return pageGetRecordPtr(aData, nData, iCell);
return (int)pageGetRecordPtr(aData, nData, iCell);
}
static int btreeCursorNext(BtreeCursor *pCsr){
@ -751,7 +751,7 @@ static int btreeCursorFirst(BtreeCursor *pCsr){
Page *pPg = 0;
FileSystem *pFS = pCsr->pFS;
int iPg = pCsr->pSeg->iRoot;
int iPg = (int)pCsr->pSeg->iRoot;
do {
rc = lsmFsDbPageGet(pFS, pCsr->pSeg, iPg, &pPg);
@ -779,7 +779,7 @@ static int btreeCursorFirst(BtreeCursor *pCsr){
assert( pCsr->aPg[pCsr->nDepth].iCell==0 );
pCsr->aPg[pCsr->nDepth].pPage = pPg;
pCsr->nDepth++;
iPg = pageGetRecordPtr(aData, nData, 0);
iPg = (int)pageGetRecordPtr(aData, nData, 0);
}
}
}while( rc==LSM_OK );
@ -871,7 +871,7 @@ static int btreeCursorRestore(
int nSeek;
int iTopicSeek;
int iPg = 0;
int iLoad = pSeg->iRoot;
int iLoad = (int)pSeg->iRoot;
Page *pPg = pCsr->aPg[nDepth-1].pPage;
if( pageObjGetNRec(pPg)==0 ){
@ -890,22 +890,22 @@ static int btreeCursorRestore(
}
do {
Page *pPg;
rc = lsmFsDbPageGet(pCsr->pFS, pSeg, iLoad, &pPg);
assert( rc==LSM_OK || pPg==0 );
Page *pPg2;
rc = lsmFsDbPageGet(pCsr->pFS, pSeg, iLoad, &pPg2);
assert( rc==LSM_OK || pPg2==0 );
if( rc==LSM_OK ){
u8 *aData; /* Buffer containing page data */
int nData; /* Size of aData[] in bytes */
int iMin;
int iMax;
int iCell;
int iCell2;
aData = fsPageData(pPg, &nData);
aData = fsPageData(pPg2, &nData);
assert( (pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG) );
iLoad = pageGetPtr(aData, nData);
iCell = pageGetNRec(aData, nData);
iMax = iCell-1;
iLoad = (int)pageGetPtr(aData, nData);
iCell2 = pageGetNRec(aData, nData);
iMax = iCell2-1;
iMin = 0;
while( iMax>=iMin ){
@ -916,7 +916,7 @@ static int btreeCursorRestore(
int res; /* (pSeek - pKeyT) */
rc = pageGetBtreeKey(
pSeg, pPg, iTry, &iPtr, &iTopic, &pKey, &nKey, &blob
pSeg, pPg2, iTry, &iPtr, &iTopic, &pKey, &nKey, &blob
);
if( rc!=LSM_OK ) break;
@ -926,16 +926,16 @@ static int btreeCursorRestore(
assert( res!=0 );
if( res<0 ){
iLoad = iPtr;
iCell = iTry;
iLoad = (int)iPtr;
iCell2 = iTry;
iMax = iTry-1;
}else{
iMin = iTry+1;
}
}
pCsr->aPg[iPg].pPage = pPg;
pCsr->aPg[iPg].iCell = iCell;
pCsr->aPg[iPg].pPage = pPg2;
pCsr->aPg[iPg].iCell = iCell2;
iPg++;
assert( iPg!=nDepth-1
|| lsmFsRedirectPage(pCsr->pFS, pSeg->pRedirect, iLoad)==iLeaf
@ -1001,7 +1001,7 @@ static void segmentPtrSetPage(SegmentPtr *pPtr, Page *pNext){
int nData;
u8 *aData = fsPageData(pNext, &nData);
pPtr->nCell = pageGetNRec(aData, nData);
pPtr->flags = pageGetFlags(aData, nData);
pPtr->flags = (u16)pageGetFlags(aData, nData);
pPtr->iPtr = pageGetPtr(aData, nData);
}
pPtr->pPg = pNext;
@ -1637,7 +1637,7 @@ static int segmentPtrSeek(
int *pbStop
){
int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp;
int res; /* Result of comparison operation */
int res = 0; /* Result of comparison operation */
int rc = LSM_OK;
int iMin;
int iMax;
@ -1759,7 +1759,7 @@ static int segmentPtrSeek(
}
assert( rc!=LSM_OK || assertSeekResult(pCsr,pPtr,iTopic,pKey,nKey,eSeek) );
*piPtr = iPtrOut;
*piPtr = (int)iPtrOut;
return rc;
}
@ -1777,7 +1777,7 @@ static int seekInBtree(
Page *pPg = 0;
Blob blob = {0, 0, 0};
iPg = pSeg->iRoot;
iPg = (int)pSeg->iRoot;
do {
Pgno *piFirst = 0;
if( aPg ){
@ -1799,7 +1799,7 @@ static int seekInBtree(
flags = pageGetFlags(aData, nData);
if( (flags & SEGMENT_BTREE_FLAG)==0 ) break;
iPg = pageGetPtr(aData, nData);
iPg = (int)pageGetPtr(aData, nData);
nRec = pageGetNRec(aData, nData);
iMin = 0;
@ -1825,7 +1825,7 @@ static int seekInBtree(
pCsr->pDb->xCmp, iTopic, pKey, nKey, iTopicT, pKeyT, nKeyT
);
if( res<0 ){
iPg = iPtr;
iPg = (int)iPtr;
iMax = iTry-1;
}else{
iMin = iTry+1;
@ -1866,7 +1866,7 @@ static int seekInSegment(
if( rc==LSM_OK ) segmentPtrSetPage(pPtr, pPg);
}else{
if( iPtr==0 ){
iPtr = pPtr->pSeg->iFirst;
iPtr = (int)pPtr->pSeg->iFirst;
}
if( rc==LSM_OK ){
rc = segmentPtrLoadPage(pCsr->pDb->pFS, pPtr, iPtr);
@ -1923,7 +1923,7 @@ static int seekInLevel(
** left-hand-side of the level in this case. */
if( res<0 ){
int iPtr = 0;
if( nRhs==0 ) iPtr = *piPgno;
if( nRhs==0 ) iPtr = (int)*piPgno;
rc = seekInSegment(
pCsr, &aPtr[0], iTopic, pKey, nKey, iPtr, eSeek, &iOut, &bStop
@ -1935,7 +1935,7 @@ static int seekInLevel(
if( res>=0 ){
int bHit = 0; /* True if at least one rhs is not EOF */
int iPtr = *piPgno;
int iPtr = (int)*piPgno;
int i;
for(i=1; rc==LSM_OK && i<=nRhs && bStop==0; i++){
SegmentPtr *pPtr = &aPtr[i];
@ -2007,37 +2007,37 @@ static void multiCursorGetKey(
if( pCsr->iFree < (nEntry*2) ){
FreelistEntry *aEntry = pWorker->freelist.aEntry;
int i = nEntry - 1 - (pCsr->iFree / 2);
u32 iKey = 0;
u32 iKey2 = 0;
if( (pCsr->iFree % 2) ){
eType = LSM_END_DELETE|LSM_SYSTEMKEY;
iKey = aEntry[i].iBlk-1;
iKey2 = aEntry[i].iBlk-1;
}else if( aEntry[i].iId>=0 ){
eType = LSM_INSERT|LSM_SYSTEMKEY;
iKey = aEntry[i].iBlk;
iKey2 = aEntry[i].iBlk;
/* If the in-memory entry immediately before this one was a
** DELETE, and the block number is one greater than the current
** block number, mark this entry as an "end-delete-range". */
if( i<(nEntry-1) && aEntry[i+1].iBlk==iKey+1 && aEntry[i+1].iId<0 ){
if( i<(nEntry-1) && aEntry[i+1].iBlk==iKey2+1 && aEntry[i+1].iId<0 ){
eType |= LSM_END_DELETE;
}
}else{
eType = LSM_START_DELETE|LSM_SYSTEMKEY;
iKey = aEntry[i].iBlk + 1;
iKey2 = aEntry[i].iBlk + 1;
}
/* If the in-memory entry immediately after this one is a
** DELETE, and the block number is one less than the current
** key, mark this entry as an "start-delete-range". */
if( i>0 && aEntry[i-1].iBlk==iKey-1 && aEntry[i-1].iId<0 ){
if( i>0 && aEntry[i-1].iBlk==iKey2-1 && aEntry[i-1].iId<0 ){
eType |= LSM_START_DELETE;
}
pKey = pCsr->pSystemVal;
nKey = 4;
lsmPutU32(pKey, ~iKey);
lsmPutU32(pKey, ~iKey2);
}
}
break;
@ -2617,7 +2617,7 @@ int lsmSortedWalkFreelist(
while( rc==LSM_OK && lsmMCursorValid(pCsr) && rtIsSystem(pCsr->eType) ){
void *pKey; int nKey;
void *pVal; int nVal;
void *pVal = 0; int nVal = 0;
rc = lsmMCursorKey(pCsr, &pKey, &nKey);
if( rc==LSM_OK ) rc = lsmMCursorValue(pCsr, &pVal, &nVal);
@ -3475,7 +3475,7 @@ static int mergeWorkerLoadHierarchy(MergeWorker *pMW){
lsm_env *pEnv = pMW->pDb->pEnv;
Page **apHier = 0;
int nHier = 0;
int iPg = pSeg->iRoot;
int iPg = (int)pSeg->iRoot;
do {
Page *pPg = 0;
@ -3501,7 +3501,7 @@ static int mergeWorkerLoadHierarchy(MergeWorker *pMW){
nHier++;
apHier[0] = pPg;
iPg = pageGetPtr(aData, nData);
iPg = (int)pageGetPtr(aData, nData);
}else{
lsmFsPageRelease(pPg);
break;
@ -3620,9 +3620,9 @@ static int mergeWorkerBtreeWrite(
assert( lsmFsPageWritable(pOld) );
aData = fsPageData(pOld, &nData);
if( eType==0 ){
nByte = 2 + 1 + lsmVarintLen32(iPtr) + lsmVarintLen32(iKeyPg);
nByte = 2 + 1 + lsmVarintLen32((int)iPtr) + lsmVarintLen32((int)iKeyPg);
}else{
nByte = 2 + 1 + lsmVarintLen32(iPtr) + lsmVarintLen32(nKey) + nKey;
nByte = 2 + 1 + lsmVarintLen32((int)iPtr) + lsmVarintLen32(nKey) + nKey;
}
nRec = pageGetNRec(aData, nData);
nFree = SEGMENT_EOF(nData, nRec) - mergeWorkerPageOffset(aData, nData);
@ -3663,15 +3663,15 @@ static int mergeWorkerBtreeWrite(
aData = fsPageData(p->apHier[iLevel], &nData);
iOff = mergeWorkerPageOffset(aData, nData);
nRec = pageGetNRec(aData, nData);
lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], iOff);
lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], nRec+1);
lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], (u16)iOff);
lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], (u16)(nRec+1));
if( eType==0 ){
aData[iOff++] = 0x00;
iOff += lsmVarintPut32(&aData[iOff], iPtr);
iOff += lsmVarintPut32(&aData[iOff], iKeyPg);
iOff += lsmVarintPut32(&aData[iOff], (int)iPtr);
iOff += lsmVarintPut32(&aData[iOff], (int)iKeyPg);
}else{
aData[iOff++] = eType;
iOff += lsmVarintPut32(&aData[iOff], iPtr);
iOff += lsmVarintPut32(&aData[iOff], (int)iPtr);
iOff += lsmVarintPut32(&aData[iOff], nKey);
memcpy(&aData[iOff], pKey, nKey);
}
@ -3918,7 +3918,7 @@ static int mergeWorkerFirstPage(MergeWorker *pMW){
if( pCsr->pBtCsr ){
rc = LSM_OK;
iFPtr = pMW->pLevel->pNext->lhs.iFirst;
iFPtr = (int)pMW->pLevel->pNext->lhs.iFirst;
}else if( pCsr->nPtr>0 ){
Segment *pSeg;
pSeg = pCsr->aPtr[pCsr->nPtr-1].pSeg;
@ -3927,7 +3927,7 @@ static int mergeWorkerFirstPage(MergeWorker *pMW){
u8 *aData; /* Buffer for page pPg */
int nData; /* Size of aData[] in bytes */
aData = fsPageData(pPg, &nData);
iFPtr = pageGetPtr(aData, nData);
iFPtr = (int)pageGetPtr(aData, nData);
lsmFsPageRelease(pPg);
}
}
@ -3953,11 +3953,11 @@ static int mergeWorkerWrite(
int nHdr; /* Space required for this record header */
Page *pPg; /* Page to write to */
u8 *aData; /* Data buffer for page pWriter->pPage */
int nData; /* Size of buffer aData[] in bytes */
int nRec; /* Number of records on page pPg */
int iFPtr; /* Value of pointer in footer of pPg */
int nData = 0; /* Size of buffer aData[] in bytes */
int nRec = 0; /* Number of records on page pPg */
int iFPtr = 0; /* Value of pointer in footer of pPg */
int iRPtr = 0; /* Value of pointer written into record */
int iOff; /* Current write offset within page pPg */
int iOff = 0; /* Current write offset within page pPg */
Segment *pSeg; /* Segment being written */
int flags = 0; /* If != 0, flags value for page footer */
int bFirst = 0; /* True for first key of output run */
@ -3973,7 +3973,7 @@ static int mergeWorkerWrite(
if( pPg ){
aData = fsPageData(pPg, &nData);
nRec = pageGetNRec(aData, nData);
iFPtr = pageGetPtr(aData, nData);
iFPtr = (int)pageGetPtr(aData, nData);
iRPtr = iPtr - iFPtr;
}
@ -3999,7 +3999,7 @@ static int mergeWorkerWrite(
** marked read-only, advance to the next page of the output run. */
iOff = pMerge->iOutputOff;
if( iOff<0 || pPg==0 || iOff+nHdr > SEGMENT_EOF(nData, nRec+1) ){
iFPtr = *pMW->pCsr->pPrevMergePtr;
iFPtr = (int)*pMW->pCsr->pPrevMergePtr;
iRPtr = iPtr - iFPtr;
iOff = 0;
nRec = 0;
@ -4033,12 +4033,12 @@ static int mergeWorkerWrite(
aData = fsPageData(pPg, &nData);
/* Update the page footer. */
lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], nRec+1);
lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], iOff);
if( flags ) lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], flags);
lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], (u16)(nRec+1));
lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], (u16)iOff);
if( flags ) lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], (u16)flags);
/* Write the entry header into the current page. */
aData[iOff++] = eType; /* 1 */
aData[iOff++] = (u8)eType; /* 1 */
iOff += lsmVarintPut32(&aData[iOff], iRPtr); /* 2 */
iOff += lsmVarintPut32(&aData[iOff], nKey); /* 3 */
if( rtIsWrite(eType) ) iOff += lsmVarintPut32(&aData[iOff], nVal); /* 4 */
@ -4260,7 +4260,7 @@ static int mergeWorkerStep(MergeWorker *pMW){
pVal = pCsr->val.pData;
}
if( rc==LSM_OK ){
rc = mergeWorkerWrite(pMW, eType, pKey, nKey, pVal, nVal, iPtr);
rc = mergeWorkerWrite(pMW, eType, pKey, nKey, pVal, nVal, (int)iPtr);
}
}
}
@ -4581,7 +4581,7 @@ static int mergeWorkerInit(
SegmentPtr *pPtr;
assert( pCsr->aPtr[i].pPg==0 );
pPtr = &pCsr->aPtr[i];
rc = segmentPtrLoadPage(pDb->pFS, pPtr, pInput->iPg);
rc = segmentPtrLoadPage(pDb->pFS, pPtr, (int)pInput->iPg);
if( rc==LSM_OK && pPtr->nCell>0 ){
rc = segmentPtrLoadCell(pPtr, pInput->iCell);
}
@ -5184,7 +5184,7 @@ static int doLsmSingleWork(
nUnsync = lsmCheckpointNWrite(pDb->pShmhdr->aSnap1, 0);
nPgsz = lsmCheckpointPgsz(pDb->pShmhdr->aSnap1);
nMax = LSM_MIN(nMax, (pDb->nAutockpt/nPgsz) - (int)(nUnsync-nSync));
nMax = (int)LSM_MIN(nMax, (pDb->nAutockpt/nPgsz) - (int)(nUnsync-nSync));
if( nMax<nRem ){
bCkpt = 1;
nRem = LSM_MAX(nMax, 0);
@ -5522,7 +5522,7 @@ void sortedDumpPage(lsm_db *pDb, Segment *pRun, Page *pPg, int bVals){
aData = fsPageData(pPg, &nData);
nRec = pageGetNRec(aData, nData);
iPtr = pageGetPtr(aData, nData);
iPtr = (int)pageGetPtr(aData, nData);
flags = pageGetFlags(aData, nData);
lsmStringInit(&s, pDb->pEnv);
@ -5533,7 +5533,7 @@ void sortedDumpPage(lsm_db *pDb, Segment *pRun, Page *pPg, int bVals){
Page *pRef = 0; /* Pointer to page iRef */
int iChar;
u8 *aKey; int nKey = 0; /* Key */
u8 *aVal; int nVal = 0; /* Value */
u8 *aVal = 0; int nVal = 0; /* Value */
int iTopic;
u8 *aCell;
int iPgPtr;
@ -5593,7 +5593,7 @@ static void infoCellDump(
){
u8 *aData; int nData; /* Page data */
u8 *aKey; int nKey = 0; /* Key */
u8 *aVal; int nVal = 0; /* Value */
u8 *aVal = 0; int nVal = 0; /* Value */
int eType;
int iPgPtr;
Page *pRef = 0; /* Pointer to page iRef */
@ -5699,20 +5699,20 @@ static int infoPageDump(
LsmString str;
int nRec;
int iPtr;
int flags;
int flags2;
int iCell;
u8 *aData; int nData; /* Page data and size thereof */
aData = fsPageData(pPg, &nData);
nRec = pageGetNRec(aData, nData);
iPtr = pageGetPtr(aData, nData);
flags = pageGetFlags(aData, nData);
iPtr = (int)pageGetPtr(aData, nData);
flags2 = pageGetFlags(aData, nData);
lsmStringInit(&str, pDb->pEnv);
lsmStringAppendf(&str, "Page : %lld (%d bytes)\n", iPg, nData);
lsmStringAppendf(&str, "nRec : %d\n", nRec);
lsmStringAppendf(&str, "iPtr : %d\n", iPtr);
lsmStringAppendf(&str, "flags: %04x\n", flags);
lsmStringAppendf(&str, "flags: %04x\n", flags2);
lsmStringAppendf(&str, "\n");
for(iCell=0; iCell<nRec; iCell++){
@ -5735,7 +5735,7 @@ static int infoPageDump(
infoCellDump(pDb, pSeg, bIndirect, pPg, iCell, &eType, &iPgPtr,
&aKey, &nKey, &aVal, &nVal, &blob
);
iAbsPtr = iPgPtr + ((flags & SEGMENT_BTREE_FLAG) ? 0 : iPtr);
iAbsPtr = iPgPtr + ((flags2 & SEGMENT_BTREE_FLAG) ? 0 : iPtr);
lsmFlagsToString(eType, zFlags);
lsmStringAppendf(&str, "%s %d (%s) ",

View File

@ -278,8 +278,9 @@ static int treeKeycmp(void *p1, int n1, void *p2, int n2){
** sub-tree of the node.
*/
static u32 getChildPtr(TreeNode *p, int iVersion, int iCell){
assert( iVersion>=0 );
assert( iCell>=0 && iCell<=array_size(p->aiChildPtr) );
if( p->iV2 && p->iV2<=iVersion && iCell==p->iV2Child ) return p->iV2Ptr;
if( p->iV2 && p->iV2<=(u32)iVersion && iCell==p->iV2Child ) return p->iV2Ptr;
return p->aiChildPtr[iCell];
}
@ -300,7 +301,7 @@ static int treeOffsetToChunk(u32 iOff){
*/
static void *treeShmptr(lsm_db *pDb, u32 iPtr){
assert( (iPtr>>15)<pDb->nShm );
assert( (iPtr>>15)<(u32)pDb->nShm );
assert( pDb->apShm[iPtr>>15] );
return iPtr ? treeShmptrUnsafe(pDb, iPtr) : 0;
@ -519,7 +520,7 @@ void dump_node_contents(
}else{
for(i=0; i<4 && nHeight>0; i++){
u32 iPtr = getChildPtr(pNode, pDb->treehdr.root.iTransId, i);
zPath[nPath] = i+'0';
zPath[nPath] = (char)(i+'0');
zPath[nPath+1] = '/';
if( iPtr ){
@ -644,7 +645,7 @@ static u32 treeShmalloc(lsm_db *pDb, int bAlign, int nByte, int *pRc){
assert( iWrite );
iChunk = treeOffsetToChunk(iWrite-1);
iEof = (iChunk+1) * CHUNK_SIZE;
assert( iEof>=iWrite && (iEof-iWrite)<CHUNK_SIZE );
assert( iEof>=iWrite && (iEof-iWrite)<(u32)CHUNK_SIZE );
if( (iWrite+nByte)>iEof ){
ShmChunk *pHdr; /* Header of chunk just finished (iChunk) */
ShmChunk *pFirst; /* Header of chunk treehdr.iFirst */
@ -757,7 +758,7 @@ static TreeKey *newTreeKey(
iWrite = (pDb->treehdr.iWrite & (LSM_SHM_CHUNK_SIZE-1));
iWrite = LSM_MAX(iWrite, LSM_SHM_CHUNK_HDR);
nAlloc = LSM_MIN((LSM_SHM_CHUNK_SIZE-iWrite), nRem);
nAlloc = LSM_MIN((LSM_SHM_CHUNK_SIZE-iWrite), (u32)nRem);
aAlloc = treeShmptr(pDb, treeShmalloc(pDb, 0, nAlloc, pRc));
if( aAlloc==0 ) break;
@ -1209,7 +1210,7 @@ static int treeCheckLinkedList(lsm_db *db){
nVisit++;
}
if( rc==LSM_OK && nVisit!=db->treehdr.nChunk-1 ){
if( rc==LSM_OK && (u32)nVisit!=db->treehdr.nChunk-1 ){
rc = LSM_CORRUPT_BKPT;
}
return rc;
@ -1259,7 +1260,7 @@ static int treeRepairList(lsm_db *db){
/* Iterate through all shm chunks. Find the smallest shm-id present in
** the shared-memory region. */
for(i=1; rc==LSM_OK && i<db->treehdr.nChunk; i++){
for(i=1; rc==LSM_OK && (u32)i<db->treehdr.nChunk; i++){
p = treeShmChunkRc(db, i, &rc);
if( p && (pMin==0 || shm_sequence_ge(pMin->iShmid, p->iShmid)) ){
pMin = p;
@ -1279,7 +1280,7 @@ static int treeRepairList(lsm_db *db){
/* Allocate space for a merge sort. */
nSort = 1;
while( nSort < (db->treehdr.nChunk-1) ) nSort = nSort * 2;
while( (u32)nSort < (db->treehdr.nChunk-1) ) nSort = nSort * 2;
nByte = sizeof(ShmChunkLoc) * nSort * 2;
aSort = lsmMallocZeroRc(db->pEnv, nByte, &rc);
iPrevShmid = pMin->iShmid;
@ -1287,11 +1288,11 @@ static int treeRepairList(lsm_db *db){
/* Fix all shm-ids, if required. */
if( rc==LSM_OK ){
iPrevShmid = pMin->iShmid-1;
for(i=1; i<db->treehdr.nChunk; i++){
for(i=1; (u32)i<db->treehdr.nChunk; i++){
p = treeShmChunk(db, i);
aSort[i-1].pShm = p;
aSort[i-1].iLoc = i;
if( i!=db->treehdr.iFirst ){
if( (u32)i!=db->treehdr.iFirst ){
if( shm_sequence_ge(p->iShmid, db->treehdr.iNextShmid) ){
p->iShmid = iPrevShmid--;
}
@ -1375,7 +1376,7 @@ static void treeOverwriteKey(lsm_db *db, TreeCursor *pCsr, u32 iKey, int *pRc){
int iCell = pCsr->aiCell[pCsr->iNode];
/* Create a copy of this node */
if( (pCsr->iNode>0 && pCsr->iNode==(p->nHeight-1)) ){
if( (pCsr->iNode>0 && (u32)pCsr->iNode==(p->nHeight-1)) ){
pNew = copyTreeLeaf(db, (TreeLeaf *)pNode, &iNew, pRc);
}else{
pNew = copyTreeNode(db, pNode, &iNew, pRc);
@ -1398,7 +1399,7 @@ static int treeNextIsEndDelete(lsm_db *db, TreeCursor *pCsr){
int iCell = pCsr->aiCell[iNode]+1;
/* Cursor currently points to a leaf node. */
assert( pCsr->iNode==(db->treehdr.root.nHeight-1) );
assert( (u32)pCsr->iNode==(db->treehdr.root.nHeight-1) );
while( iNode>=0 ){
TreeNode *pNode = pCsr->apTreeNode[iNode];
@ -1419,7 +1420,7 @@ static int treePrevIsStartDelete(lsm_db *db, TreeCursor *pCsr){
int iNode = pCsr->iNode;
/* Cursor currently points to a leaf node. */
assert( pCsr->iNode==(db->treehdr.root.nHeight-1) );
assert( (u32)pCsr->iNode==(db->treehdr.root.nHeight-1) );
while( iNode>=0 ){
TreeNode *pNode = pCsr->apTreeNode[iNode];
@ -1450,7 +1451,7 @@ static int treeInsertEntry(
u32 iTreeKey;
TreeRoot *p = &pDb->treehdr.root;
TreeCursor csr; /* Cursor to seek to pKey/nKey */
int res; /* Result of seek operation on csr */
int res = 0; /* Result of seek operation on csr */
assert( nVal>=0 || pVal==0 );
assert_tree_looks_ok(LSM_OK, pTree);
@ -1597,9 +1598,9 @@ static int treeDeleteEntry(lsm_db *db, TreeCursor *pCsr, u32 iNewptr){
assert( pNode->aiKeyPtr[1] );
assert( pNode->aiKeyPtr[iSlot] );
assert( iSlot==0 || iSlot==1 || iSlot==2 );
assert( (pCsr->iNode==(db->treehdr.root.nHeight-1))==(iNewptr==0) );
assert( ((u32)pCsr->iNode==(db->treehdr.root.nHeight-1))==(iNewptr==0) );
bLeaf = (pCsr->iNode==(p->nHeight-1) && p->nHeight>1);
bLeaf = ((u32)pCsr->iNode==(p->nHeight-1) && p->nHeight>1);
if( pNode->aiKeyPtr[0] || pNode->aiKeyPtr[2] ){
/* There are currently at least 2 keys on this node. So just create
@ -1764,7 +1765,7 @@ static int treeDeleteEntry(lsm_db *db, TreeCursor *pCsr, u32 iNewptr){
iPSlot--;
pNew1->aiKeyPtr[iKOut++] = pParent->aiKeyPtr[iPSlot];
if( bLeaf==0 ) pNew1->aiChildPtr[iPOut++] = iNewptr;
pCsr->aiCell[pCsr->iNode] = iPSlot;
pCsr->aiCell[pCsr->iNode] = (u8)iPSlot;
}
rc = treeDeleteEntry(db, pCsr, iNew1);
@ -1839,7 +1840,7 @@ int lsmTreeDelete(
}
if( bDone==0 ){
if( csr.iNode==(p->nHeight-1) ){
if( (u32)csr.iNode==(p->nHeight-1) ){
/* The element to delete already lies on a leaf node */
rc = treeDeleteEntry(db, &csr, 0);
}else{
@ -1854,7 +1855,7 @@ int lsmTreeDelete(
TreeKey *pKey;
int iNode = csr.iNode;
lsmTreeCursorNext(&csr);
assert( csr.iNode==(p->nHeight-1) );
assert( (u32)csr.iNode==(p->nHeight-1) );
iKey = csr.apTreeNode[csr.iNode]->aiKeyPtr[csr.aiCell[csr.iNode]];
lsmTreeCursorPrev(&csr);
@ -2028,19 +2029,19 @@ int lsmTreeCursorSeek(TreeCursor *pCsr, void *pKey, int nKey, int *pRes){
}
res = treeKeycmp((void *)&pTreeKey[1], pTreeKey->nKey, pKey, nKey);
if( res==0 ){
pCsr->aiCell[iNode] = iTest;
pCsr->aiCell[iNode] = (u8)iTest;
break;
}
}else{
iTest = 1;
}
if( iNode<(pRoot->nHeight-1) ){
if( (u32)iNode<(pRoot->nHeight-1) ){
iNodePtr = getChildPtr(pNode, pRoot->iTransId, iTest + (res<0));
}else{
iNodePtr = 0;
}
pCsr->aiCell[iNode] = iTest + (iNodePtr && (res<0));
pCsr->aiCell[iNode] = (u8)(iTest + (iNodePtr && (res<0)));
}
*pRes = res;
@ -2167,7 +2168,7 @@ int lsmTreeCursorPrev(TreeCursor *pCsr){
if( rc!=LSM_OK ) break;
pCsr->apTreeNode[pCsr->iNode] = pNode;
iCell = 1 + (pNode->aiKeyPtr[2]!=0) + (pCsr->iNode < iLeaf);
pCsr->aiCell[pCsr->iNode] = iCell;
pCsr->aiCell[pCsr->iNode] = (u8)iCell;
}while( pCsr->iNode < iLeaf );
}
@ -2179,7 +2180,7 @@ int lsmTreeCursorPrev(TreeCursor *pCsr){
iCell = pCsr->aiCell[pCsr->iNode]-1;
if( iCell>=0 && pCsr->apTreeNode[pCsr->iNode]->aiKeyPtr[iCell] ) break;
}while( (--pCsr->iNode)>=0 );
pCsr->aiCell[pCsr->iNode] = iCell;
pCsr->aiCell[pCsr->iNode] = (u8)iCell;
}
#ifndef NDEBUG
@ -2224,12 +2225,12 @@ int lsmTreeCursorEnd(TreeCursor *pCsr, int bLast){
pCsr->iNode++;
pCsr->apTreeNode[pCsr->iNode] = pNode;
if( pCsr->iNode<pRoot->nHeight-1 ){
if( (u32)pCsr->iNode<pRoot->nHeight-1 ){
iNodePtr = getChildPtr(pNode, pRoot->iTransId, iCell);
}else{
iNodePtr = 0;
}
pCsr->aiCell[pCsr->iNode] = iCell - (iNodePtr==0 && bLast);
pCsr->aiCell[pCsr->iNode] = (u8)(iCell - (iNodePtr==0 && bLast));
}
return rc;

View File

@ -176,7 +176,7 @@ int lsmVarintGet32(u8 *z, int *piVal){
}
ret = lsmSqlite4GetVarint64(z, &i);
*piVal = i;
*piVal = (int)i;
return ret;
}

View File

@ -157,7 +157,7 @@ static int lsm1Close(sqlite3_vtab_cursor *cur){
*/
static int lsm1Next(sqlite3_vtab_cursor *cur){
lsm1_cursor *pCur = (lsm1_cursor*)cur;
int rc;
int rc = LSM_OK;
if( pCur->bUnique ){
pCur->atEof = 1;
}else{
@ -368,7 +368,7 @@ static int lsm1EncodeKey(
pSpace = sqlite3_malloc( nVal+1 );
if( pSpace==0 ) return SQLITE_NOMEM;
}
pSpace[0] = eType;
pSpace[0] = (unsigned char)eType;
memcpy(&pSpace[1], pVal, nVal);
*ppKey = pSpace;
*pnKey = nVal+1;
@ -385,7 +385,7 @@ static int lsm1EncodeKey(
uVal = iVal;
eType = LSM1_TYPE_POSITIVE;
}
pSpace[0] = eType;
pSpace[0] = (unsigned char)eType;
*ppKey = pSpace;
*pnKey = 1 + lsm1PutVarint64(&pSpace[1], uVal);
}
@ -597,7 +597,7 @@ int lsm1Update(
const void *pKey;
int nKey;
int eType;
int rc;
int rc = LSM_OK;
sqlite3_value *pValue;
const unsigned char *pVal;
unsigned char *pData;
@ -654,7 +654,7 @@ int lsm1Update(
if( pData==0 ){
rc = SQLITE_NOMEM;
}else{
pData[0] = eType;
pData[0] = (unsigned char)eType;
memcpy(&pData[1], pVal, nVal);
rc = lsm_insert(p->pDb, pKey, nKey, pData, nVal+1);
sqlite3_free(pData);
@ -677,7 +677,7 @@ int lsm1Update(
aVal[i] = x & 0xff;
x >>= 8;
}
aVal[i] = eType;
aVal[i] = (unsigned char)eType;
rc = lsm_insert(p->pDb, pKey, nKey, &aVal[i], 9-i);
break;
}

View File

@ -47,12 +47,12 @@ struct Win32File {
LPVOID *apShm; /* Array of 32K shared memory segments */
};
static char *win32ShmFile(Win32File *p){
static char *win32ShmFile(Win32File *pWin32File){
char *zShm;
int nName = strlen(p->zName);
zShm = (char *)lsmMallocZero(p->pEnv, nName+4+1);
int nName = strlen(pWin32File->zName);
zShm = (char *)lsmMallocZero(pWin32File->pEnv, nName+4+1);
if( zShm ){
memcpy(zShm, p->zName, nName);
memcpy(zShm, pWin32File->zName, nName);
memcpy(&zShm[nName], "-shm", 5);
}
return zShm;
@ -253,7 +253,7 @@ static int lsmWin32OsOpen(
if( pWin32File==0 ){
rc = LSM_NOMEM_BKPT;
}else{
HANDLE hFile;
HANDLE hFile = NULL;
rc = win32Open(pEnv, zFile, flags, &hFile);
if( rc==LSM_OK ){
@ -380,6 +380,18 @@ static int lsmWin32OsSectorSize(lsm_file *pFile){
return 512;
}
static void win32Unmap(Win32File *pWin32File){
if( pWin32File->pMap!=NULL ){
UnmapViewOfFile(pWin32File->pMap);
pWin32File->pMap = NULL;
pWin32File->nMap = 0;
}
if( pWin32File->hMap!=NULL ){
CloseHandle(pWin32File->hMap);
pWin32File->hMap = NULL;
}
}
static int lsmWin32OsRemap(
lsm_file *pFile,
lsm_i64 iMin,
@ -393,15 +405,10 @@ static int lsmWin32OsRemap(
const int aIncrSz[] = {256*1024, 1024*1024};
int nIncrSz = aIncrSz[iMin>(2*1024*1024)];
if( pWin32File->pMap!=NULL ){
UnmapViewOfFile(pWin32File->pMap);
*ppOut = pWin32File->pMap = NULL;
*pnOut = pWin32File->nMap = 0;
}
if( pWin32File->hMap!=NULL ){
CloseHandle(pWin32File->hMap);
pWin32File->hMap = NULL;
}
*ppOut = NULL;
*pnOut = 0;
win32Unmap(pWin32File);
if( iMin>=0 ){
LARGE_INTEGER fileSize;
DWORD dwSizeHigh;
@ -580,22 +587,27 @@ static int lsmWin32OsUnlink(lsm_env *pEnv, const char *zFile){
((a)==ERROR_IO_PENDING))
#endif
static int lsmWin32OsLock(lsm_file *pFile, int iLock, int eType){
Win32File *pWin32File = (Win32File *)pFile;
static int win32LockFile(
Win32File *pWin32File,
int iLock,
int nLock,
int eType
){
OVERLAPPED ovlp;
assert( LSM_LOCK_UNLOCK==0 );
assert( LSM_LOCK_SHARED==1 );
assert( LSM_LOCK_EXCL==2 );
assert( eType>=LSM_LOCK_UNLOCK && eType<=LSM_LOCK_EXCL );
assert( nLock>=0 );
assert( iLock>0 && iLock<=32 );
memset(&ovlp, 0, sizeof(OVERLAPPED));
ovlp.Offset = (4096-iLock);
ovlp.Offset = (4096-iLock-nLock+1);
if( eType>LSM_LOCK_UNLOCK ){
DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
if( eType>=LSM_LOCK_EXCL ) flags |= LOCKFILE_EXCLUSIVE_LOCK;
if( !LockFileEx(pWin32File->hFile, flags, 0, 1, 0, &ovlp) ){
if( !LockFileEx(pWin32File->hFile, flags, 0, (DWORD)nLock, 0, &ovlp) ){
if( win32IsLockBusy(GetLastError()) ){
return LSM_BUSY;
}else{
@ -603,36 +615,24 @@ static int lsmWin32OsLock(lsm_file *pFile, int iLock, int eType){
}
}
}else{
if( !UnlockFileEx(pWin32File->hFile, 0, 1, 0, &ovlp) ){
if( !UnlockFileEx(pWin32File->hFile, 0, (DWORD)nLock, 0, &ovlp) ){
return LSM_IOERR_BKPT;
}
}
return LSM_OK;
}
static int lsmWin32OsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){
static int lsmWin32OsLock(lsm_file *pFile, int iLock, int eType){
Win32File *pWin32File = (Win32File *)pFile;
DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
OVERLAPPED ovlp;
return win32LockFile(pWin32File, iLock, 1, eType);
}
assert( LSM_LOCK_UNLOCK==0 );
assert( LSM_LOCK_SHARED==1 );
assert( LSM_LOCK_EXCL==2 );
assert( eType==LSM_LOCK_SHARED || eType==LSM_LOCK_EXCL );
assert( nLock>=0 );
assert( iLock>0 && iLock<=32 );
if( eType>=LSM_LOCK_EXCL ) flags |= LOCKFILE_EXCLUSIVE_LOCK;
memset(&ovlp, 0, sizeof(OVERLAPPED));
ovlp.Offset = (4096-iLock-nLock+1);
if( !LockFileEx(pWin32File->hFile, flags, 0, (DWORD)nLock, 0, &ovlp) ){
if( win32IsLockBusy(GetLastError()) ){
return LSM_BUSY;
}else{
return LSM_IOERR_BKPT;
}
}
UnlockFileEx(pWin32File->hFile, 0, (DWORD)nLock, 0, &ovlp);
static int lsmWin32OsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){
int rc;
Win32File *pWin32File = (Win32File *)pFile;
rc = win32LockFile(pWin32File, iLock, nLock, eType);
if( rc!=LSM_OK ) return rc;
win32LockFile(pWin32File, iLock, nLock, LSM_LOCK_UNLOCK);
return LSM_OK;
}
@ -648,7 +648,6 @@ static int lsmWin32OsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){
assert( sz>=0 );
assert( sz==LSM_SHM_CHUNK_SIZE );
if( iChunk>=pWin32File->nShm ){
int i;
LPHANDLE ahNew;
LPVOID *apNew;
LARGE_INTEGER fileSize;
@ -678,20 +677,18 @@ static int lsmWin32OsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){
}
}
ahNew = (LPHANDLE)lsmRealloc(pWin32File->pEnv, pWin32File->ahShm,
sizeof(LPHANDLE) * nNew);
ahNew = (LPHANDLE)lsmMallocZero(pWin32File->pEnv, sizeof(HANDLE) * nNew);
if( !ahNew ) return LSM_NOMEM_BKPT;
apNew = (LPVOID *)lsmRealloc(pWin32File->pEnv, pWin32File->apShm,
sizeof(LPVOID) * nNew);
apNew = (LPVOID *)lsmMallocZero(pWin32File->pEnv, sizeof(LPVOID) * nNew);
if( !apNew ){
lsmFree(pWin32File->pEnv, ahNew);
return LSM_NOMEM_BKPT;
}
for(i=pWin32File->nShm; i<nNew; i++){
ahNew[i] = NULL;
apNew[i] = NULL;
}
memcpy(ahNew, pWin32File->ahShm, sizeof(HANDLE) * pWin32File->nShm);
memcpy(apNew, pWin32File->apShm, sizeof(LPVOID) * pWin32File->nShm);
lsmFree(pWin32File->pEnv, pWin32File->ahShm);
pWin32File->ahShm = ahNew;
lsmFree(pWin32File->pEnv, pWin32File->apShm);
pWin32File->apShm = apNew;
pWin32File->nShm = nNew;
}
@ -761,15 +758,7 @@ static int lsmWin32OsClose(lsm_file *pFile){
int nRetry = 0;
Win32File *pWin32File = (Win32File *)pFile;
lsmWin32OsShmUnmap(pFile, 0);
if( pWin32File->pMap!=NULL ){
UnmapViewOfFile(pWin32File->pMap);
pWin32File->pMap = NULL;
pWin32File->nMap = 0;
}
if( pWin32File->hMap!=NULL ){
CloseHandle(pWin32File->hMap);
pWin32File->hMap = NULL;
}
win32Unmap(pWin32File);
do{
if( pWin32File->hFile==NULL ){
rc = LSM_IOERR_BKPT;
@ -954,7 +943,7 @@ static int lsmWin32OsMutexNotHeld(lsm_mutex *p){
}
#endif
/*
** End of pthreads mutex implementation.
** End of Win32 mutex implementation.
*************************************************************************/
#else
/*************************************************************************

523
ext/misc/completion.c Normal file
View File

@ -0,0 +1,523 @@
/*
** 2017-07-10
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements an eponymous virtual table that returns suggested
** completions for a partial SQL input.
**
** Suggested usage:
**
** SELECT DISTINCT candidate COLLATE nocase
** FROM completion($prefix,$wholeline)
** ORDER BY 1;
**
** The two query parameters are optional. $prefix is the text of the
** current word being typed and that is to be completed. $wholeline is
** the complete input line, used for context.
**
** The raw completion() table might return the same candidate multiple
** times, for example if the same column name is used to two or more
** tables. And the candidates are returned in an arbitrary order. Hence,
** the DISTINCT and ORDER BY are recommended.
**
** This virtual table operates at the speed of human typing, and so there
** is no attempt to make it fast. Even a slow implementation will be much
** faster than any human can type.
**
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* completion_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a completion virtual table
*/
typedef struct completion_vtab completion_vtab;
struct completion_vtab {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db; /* Database connection for this completion vtab */
};
/* completion_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct completion_cursor completion_cursor;
struct completion_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3 *db; /* Database connection for this cursor */
int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */
char *zPrefix; /* The prefix for the word we want to complete */
char *zLine; /* The whole that we want to complete */
const char *zCurrentRow; /* Current output row */
sqlite3_stmt *pStmt; /* Current statement */
sqlite3_int64 iRowid; /* The rowid */
int ePhase; /* Current phase */
int j; /* inter-phase counter */
};
/* Values for ePhase:
*/
#define COMPLETION_FIRST_PHASE 1
#define COMPLETION_KEYWORDS 1
#define COMPLETION_PRAGMAS 2
#define COMPLETION_FUNCTIONS 3
#define COMPLETION_COLLATIONS 4
#define COMPLETION_INDEXES 5
#define COMPLETION_TRIGGERS 6
#define COMPLETION_DATABASES 7
#define COMPLETION_TABLES 8
#define COMPLETION_COLUMNS 9
#define COMPLETION_MODULES 10
#define COMPLETION_EOF 11
/*
** The completionConnect() method is invoked to create a new
** completion_vtab that describes the completion virtual table.
**
** Think of this routine as the constructor for completion_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the completion_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against completion will look like.
*/
static int completionConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
completion_vtab *pNew;
int rc;
(void)(pAux); /* Unused parameter */
(void)(argc); /* Unused parameter */
(void)(argv); /* Unused parameter */
(void)(pzErr); /* Unused parameter */
/* Column numbers */
#define COMPLETION_COLUMN_CANDIDATE 0 /* Suggested completion of the input */
#define COMPLETION_COLUMN_PREFIX 1 /* Prefix of the word to be completed */
#define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */
#define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x("
" candidate TEXT,"
" prefix TEXT HIDDEN,"
" wholeline TEXT HIDDEN,"
" phase INT HIDDEN" /* Used for debugging only */
")");
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
pNew->db = db;
}
return rc;
}
/*
** This method is the destructor for completion_cursor objects.
*/
static int completionDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** Constructor for a new completion_cursor object.
*/
static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
completion_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((completion_vtab*)p)->db;
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Reset the completion_cursor.
*/
static void completionCursorReset(completion_cursor *pCur){
sqlite3_free(pCur->zPrefix); pCur->zPrefix = 0; pCur->nPrefix = 0;
sqlite3_free(pCur->zLine); pCur->zLine = 0; pCur->nLine = 0;
sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0;
pCur->j = 0;
}
/*
** Destructor for a completion_cursor.
*/
static int completionClose(sqlite3_vtab_cursor *cur){
completionCursorReset((completion_cursor*)cur);
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** All SQL keywords understood by SQLite
*/
static const char *completionKwrds[] = {
"ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
"ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
"CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
"CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
"CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
"DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
"ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
"FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
"IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
"INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
"LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
"NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
"PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
"REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
"ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
"TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
"UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
"WITH", "WITHOUT",
};
#define completionKwCount \
(int)(sizeof(completionKwrds)/sizeof(completionKwrds[0]))
/*
** Advance a completion_cursor to its next row of output.
**
** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object
** record the current state of the scan. This routine sets ->zCurrentRow
** to the current row of output and then returns. If no more rows remain,
** then ->ePhase is set to COMPLETION_EOF which will signal the virtual
** table that has reached the end of its scan.
**
** The current implementation just lists potential identifiers and
** keywords and filters them by zPrefix. Future enhancements should
** take zLine into account to try to restrict the set of identifiers and
** keywords based on what would be legal at the current point of input.
*/
static int completionNext(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
int eNextPhase = 0; /* Next phase to try if current phase reaches end */
int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */
pCur->iRowid++;
while( pCur->ePhase!=COMPLETION_EOF ){
switch( pCur->ePhase ){
case COMPLETION_KEYWORDS: {
if( pCur->j >= completionKwCount ){
pCur->zCurrentRow = 0;
pCur->ePhase = COMPLETION_DATABASES;
}else{
pCur->zCurrentRow = completionKwrds[pCur->j++];
}
iCol = -1;
break;
}
case COMPLETION_DATABASES: {
if( pCur->pStmt==0 ){
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1,
&pCur->pStmt, 0);
}
iCol = 1;
eNextPhase = COMPLETION_TABLES;
break;
}
case COMPLETION_TABLES: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
zSql = sqlite3_mprintf(
"%z%s"
"SELECT name FROM \"%w\".sqlite_master"
" WHERE type='table'",
zSql, zSep, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
sqlite3_finalize(pS2);
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
sqlite3_free(zSql);
}
iCol = 0;
eNextPhase = COMPLETION_COLUMNS;
break;
}
case COMPLETION_COLUMNS: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
zSql = sqlite3_mprintf(
"%z%s"
"SELECT pti.name FROM \"%w\".sqlite_master AS sm"
" JOIN pragma_table_info(sm.name,%Q) AS pti"
" WHERE sm.type='table'",
zSql, zSep, zDb, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
sqlite3_finalize(pS2);
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
sqlite3_free(zSql);
}
iCol = 0;
eNextPhase = COMPLETION_EOF;
break;
}
}
if( iCol<0 ){
/* This case is when the phase presets zCurrentRow */
if( pCur->zCurrentRow==0 ) continue;
}else{
if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){
/* Extract the next row of content */
pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol);
}else{
/* When all rows are finished, advance to the next phase */
sqlite3_finalize(pCur->pStmt);
pCur->pStmt = 0;
pCur->ePhase = eNextPhase;
continue;
}
}
if( pCur->nPrefix==0 ) break;
if( sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 ){
break;
}
}
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the completion_cursor
** is currently pointing.
*/
static int completionColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
completion_cursor *pCur = (completion_cursor*)cur;
switch( i ){
case COMPLETION_COLUMN_CANDIDATE: {
sqlite3_result_text(ctx, pCur->zCurrentRow, -1, SQLITE_TRANSIENT);
break;
}
case COMPLETION_COLUMN_PREFIX: {
sqlite3_result_text(ctx, pCur->zPrefix, -1, SQLITE_TRANSIENT);
break;
}
case COMPLETION_COLUMN_WHOLELINE: {
sqlite3_result_text(ctx, pCur->zLine, -1, SQLITE_TRANSIENT);
break;
}
case COMPLETION_COLUMN_PHASE: {
sqlite3_result_int(ctx, pCur->ePhase);
break;
}
}
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
completion_cursor *pCur = (completion_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int completionEof(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
return pCur->ePhase >= COMPLETION_EOF;
}
/*
** This method is called to "rewind" the completion_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to completionColumn() or completionRowid() or
** completionEof().
*/
static int completionFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
completion_cursor *pCur = (completion_cursor *)pVtabCursor;
int iArg = 0;
(void)(idxStr); /* Unused parameter */
(void)(argc); /* Unused parameter */
completionCursorReset(pCur);
if( idxNum & 1 ){
pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
}
iArg++;
}
if( idxNum & 2 ){
pCur->nLine = sqlite3_value_bytes(argv[iArg]);
if( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zLine==0 ) return SQLITE_NOMEM;
}
iArg++;
}
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
int i = pCur->nLine;
while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
i--;
}
pCur->nPrefix = pCur->nLine - i;
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
}
}
pCur->iRowid = 0;
pCur->ePhase = COMPLETION_FIRST_PHASE;
return completionNext(pVtabCursor);
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the completion virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** There are two hidden parameters that act as arguments to the table-valued
** function: "prefix" and "wholeline". Bit 0 of idxNum is set if "prefix"
** is available and bit 1 is set if "wholeline" is available.
*/
static int completionBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idxNum = 0; /* The query plan bitmask */
int prefixIdx = -1; /* Index of the start= constraint, or -1 if none */
int wholelineIdx = -1; /* Index of the stop= constraint, or -1 if none */
int nArg = 0; /* Number of arguments that completeFilter() expects */
const struct sqlite3_index_constraint *pConstraint;
(void)(tab); /* Unused parameter */
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
switch( pConstraint->iColumn ){
case COMPLETION_COLUMN_PREFIX:
prefixIdx = i;
idxNum |= 1;
break;
case COMPLETION_COLUMN_WHOLELINE:
wholelineIdx = i;
idxNum |= 2;
break;
}
}
if( prefixIdx>=0 ){
pIdxInfo->aConstraintUsage[prefixIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[prefixIdx].omit = 1;
}
if( wholelineIdx>=0 ){
pIdxInfo->aConstraintUsage[wholelineIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[wholelineIdx].omit = 1;
}
pIdxInfo->idxNum = idxNum;
pIdxInfo->estimatedCost = (double)5000 - 1000*nArg;
pIdxInfo->estimatedRows = 500 - 100*nArg;
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** completion virtual table.
*/
static sqlite3_module completionModule = {
0, /* iVersion */
0, /* xCreate */
completionConnect, /* xConnect */
completionBestIndex, /* xBestIndex */
completionDisconnect, /* xDisconnect */
0, /* xDestroy */
completionOpen, /* xOpen - open a cursor */
completionClose, /* xClose - close a cursor */
completionFilter, /* xFilter - configure scan constraints */
completionNext, /* xNext - advance a cursor */
completionEof, /* xEof - check for end of scan */
completionColumn, /* xColumn - read data */
completionRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0 /* xRollbackTo */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
int sqlite3CompletionVtabInit(sqlite3 *db){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3_create_module(db, "completion", &completionModule, 0);
#endif
return rc;
}
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_completion_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)(pzErrMsg); /* Unused parameter */
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3CompletionVtabInit(db);
#endif
return rc;
}

Some files were not shown because too many files have changed in this diff Show More