Merge the latest changes from trunk.
FossilOrigin-Name: a54be6e041a9185787a22b86603dcb0654e5e4af71225b556d1b6279f8520ad8
This commit is contained in:
commit
3fb406083a
@ -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
|
||||
|
56
README.md
56
README.md
@ -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).
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -294,4 +294,3 @@ do_execsql_test 7.0 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -276,4 +276,3 @@ foreach {tn expr tclexpr} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -242,4 +242,3 @@ foreach {T create} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -309,4 +309,3 @@ foreach {tn q cnt} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -178,4 +178,3 @@ do_execsql_test 5.1 {
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -142,4 +142,3 @@ if {[detail_is_full]} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -55,4 +55,3 @@ do_execsql_test 1.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -66,4 +66,3 @@ do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -147,4 +147,3 @@ do_execsql_test 3.1 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -100,4 +100,3 @@ do_execsql_test 3.3 {
|
||||
} {1 2}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -342,4 +342,3 @@ foreach {tn expr} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -279,4 +279,3 @@ do_execsql_test 9.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -112,4 +112,3 @@ db eval {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -61,4 +61,3 @@ do_test 2.1...slow {
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -64,5 +64,3 @@ foreach_detail_mode $::testprefix {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -84,5 +84,3 @@ foreach_detail_mode $::testprefix {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -66,5 +66,3 @@ do_execsql_test 2.1 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -255,4 +255,3 @@ do_execsql_test 6.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -96,4 +96,3 @@ do_catchsql_test 3.1 {
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -269,4 +269,3 @@ do_catchsql_test 6.2 {
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
@ -409,4 +409,3 @@ do_catchsql_test 9.2.2 {
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
@ -51,4 +51,3 @@ do_test 1.2 {
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -241,4 +241,3 @@ do_execsql_test 5.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -63,5 +63,3 @@ foreach_detail_mode $::testprefix {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -197,4 +197,3 @@ foreach v $vocab {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -44,4 +44,3 @@ do_execsql_test 1.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -81,6 +81,3 @@ do_execsql_test 3.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
|
@ -351,4 +351,3 @@ do_faultsim_test 9.1 -faults oom-* -prep {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -137,4 +137,3 @@ do_faultsim_test 5.0 -faults oom-* -prep {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -110,4 +110,3 @@ do_faultsim_test 3.2 -faults oom-* -prep {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -395,4 +395,3 @@ do_faultsim_test 14.1 -faults oom-t* -prep {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -130,4 +130,3 @@ do_faultsim_test 3.3 -faults oom-t* -body {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -292,4 +292,3 @@ do_faultsim_test 6 -faults oom* -prep {
|
||||
db close
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
@ -116,4 +116,3 @@ do_faultsim_test 2.2 -faults oom-* -body {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -82,4 +82,3 @@ do_faultsim_test 4 -faults oom-* -prep {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -153,4 +153,3 @@ do_faultsim_test 6 -faults oom-* -body {
|
||||
} ;# foreach_detail_mode...
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -61,4 +61,3 @@ do_faultsim_test 2 -faults oom* -prep {
|
||||
faultsim_test_result {0 {1 2}}
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
@ -132,4 +132,3 @@ do_faultsim_test 4.2 -faults oom* -body {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -40,4 +40,3 @@ do_test 1.1 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -90,4 +90,3 @@ do_catchsql_test 4.1 {
|
||||
} {1 {fts5: syntax error near "`"}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -130,4 +130,3 @@ breakpoint
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -210,4 +210,3 @@ foreach {tn pgsz} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -70,4 +70,3 @@ do_execsql_test 1.6 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -41,5 +41,3 @@ do_execsql_test 1.2 {
|
||||
} {1 abc 2 abc}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -492,4 +492,3 @@ do_catchsql_test 14.2 {
|
||||
} {1 {unrecognized matchinfo flag: d}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -241,4 +241,3 @@ do_execsql_test 6.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -55,4 +55,3 @@ do_execsql_test 1.2 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -45,4 +45,3 @@ do_multiclient_test tn {
|
||||
};# do_multiclient_test
|
||||
};# foreach_detail_mode
|
||||
finish_test
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -178,4 +178,3 @@ do_execsql_test 4.3.1 {
|
||||
do_test 4.2.2 { fts5_level_segs ttt } {3}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -106,4 +106,3 @@ foreach {tn nStep} {
|
||||
do_test 2.$tn.6 { fts5_segcount t1 } 1
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
@ -116,4 +116,3 @@ do_execsql_test 2.0 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -64,4 +64,3 @@ do_eqp_test 1.5 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -11803,4 +11803,3 @@ foreach {in out} $test_vocab {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -67,4 +67,3 @@ foreach {in out} $test_vocab {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -341,5 +341,3 @@ foreach {tn create} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -79,5 +79,3 @@ for {set tn 1 ; set pgsz 64} {$tn<32} {incr tn; incr pgsz 16} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -152,4 +152,3 @@ do_execsql_test 4.1 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -149,4 +149,3 @@ do_test 4.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -216,4 +216,3 @@ do_execsql_test 6.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -116,4 +116,3 @@ do_execsql_test 4.6 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -421,4 +421,3 @@ do_execsql_test 7.1.2 {
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -161,4 +161,3 @@ foreach {tn expr} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -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
|
||||
|
@ -302,4 +302,3 @@ do_test 9.5.2 { set ::flags } {query}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -59,4 +59,3 @@ do_execsql_test 2.1 "
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -126,4 +126,3 @@ do_test 1.5 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -76,4 +76,3 @@ do_execsql_test 3.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -117,5 +117,3 @@ do_execsql_test 2.2.integrity {
|
||||
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
||||
|
@ -61,4 +61,3 @@ do_test 1.7 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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){
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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. */
|
||||
|
@ -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) ",
|
||||
|
@ -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;
|
||||
|
@ -176,7 +176,7 @@ int lsmVarintGet32(u8 *z, int *piVal){
|
||||
}
|
||||
|
||||
ret = lsmSqlite4GetVarint64(z, &i);
|
||||
*piVal = i;
|
||||
*piVal = (int)i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
523
ext/misc/completion.c
Normal 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
Loading…
Reference in New Issue
Block a user