Compare commits
No commits in common. "evanhahn/f_barrierfsync" and "master" have entirely different histories.
evanhahn/f
...
master
15
CHANGELOG.md
15
CHANGELOG.md
@ -3,17 +3,6 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
## [unreleased] - (? 2024 - [unreleased changes])
|
||||
|
||||
## [4.6.1] - (August 2024 - [4.6.1 changes])
|
||||
- Updates baseline to upstream SQLite 3.46.1
|
||||
- Significant refactor to merge `crypto.h`, `crypto.c`, and `crypto_impl.c` into a single `sqlcipher.c` source file for simplicity.
|
||||
- Updates minimum working set size on windows to increase lockable pages
|
||||
- Adds new `PRAGMA cipher_log_source` for filtering log output on higher verbosity levels
|
||||
- Improves log output by including the log level and source prior to message
|
||||
- Improves error logging in `PRAGMA cipher_migrate`
|
||||
- Fixes issue where log level and target would be overwritten if set prior to initialization
|
||||
- Corrects Podspec license element to use specific BSD 3 Clause
|
||||
- Fixes default log output to console for macOS
|
||||
|
||||
## [4.6.0] - (May 2024 - [4.6.0 changes])
|
||||
- Sets default log level to WARN
|
||||
- Sends default log output to: logcat for Android; Console for iOS and macOS; and stderr for all other platforms
|
||||
@ -260,9 +249,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Change KDF iteration length from 4,000 to 64,000
|
||||
|
||||
[unreleased]: https://github.com/sqlcipher/sqlcipher/tree/prerelease
|
||||
[unreleased changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.6.1...prerelease
|
||||
[4.6.1]: https://github.com/sqlcipher/sqlcipher/tree/v4.6.1
|
||||
[4.6.1 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.6.0...v4.6.1
|
||||
[unreleased changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.6.0...prerelease
|
||||
[4.6.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.6.0
|
||||
[4.6.0 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.5.7...v4.6.0
|
||||
[4.5.7]: https://github.com/sqlcipher/sqlcipher/tree/v4.5.7
|
||||
|
||||
81
Makefile.in
81
Makefile.in
@ -145,15 +145,18 @@ LTLINK_EXTRAS += $(GCOV_LDFLAGS$(USE_GCOV))
|
||||
|
||||
# BEGIN CRYPTO
|
||||
CRYPTOLIBOBJ = \
|
||||
sqlcipher.lo \
|
||||
crypto.lo \
|
||||
crypto_impl.lo \
|
||||
crypto_openssl.lo \
|
||||
crypto_libtomcrypt.lo \
|
||||
crypto_nss.lo \
|
||||
crypto_cc.lo
|
||||
|
||||
CRYPTOSRC = \
|
||||
$(TOP)/src/crypto.h \
|
||||
$(TOP)/src/sqlcipher.h \
|
||||
$(TOP)/src/sqlcipher.c \
|
||||
$(TOP)/src/crypto.c \
|
||||
$(TOP)/src/crypto_impl.c \
|
||||
$(TOP)/src/crypto_libtomcrypt.c \
|
||||
$(TOP)/src/crypto_nss.c \
|
||||
$(TOP)/src/crypto_openssl.c \
|
||||
@ -437,8 +440,6 @@ TESTSRC = \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/test_recover.c \
|
||||
$(TOP)/ext/intck/test_intck.c \
|
||||
$(TOP)/ext/intck/sqlite3intck.c \
|
||||
$(TOP)/ext/rbu/test_rbu.c
|
||||
|
||||
# Statically linked extensions
|
||||
@ -836,7 +837,7 @@ has_tclsh85:
|
||||
touch .target_source
|
||||
|
||||
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify has_tclsh84
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC)
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS)
|
||||
cp tsrc/sqlite3ext.h .
|
||||
cp $(TOP)/ext/session/sqlite3session.h .
|
||||
|
||||
@ -847,7 +848,7 @@ sqlite3r.c: sqlite3.c sqlite3r.h has_tclsh84
|
||||
cp $(TOP)/ext/recover/sqlite3recover.c tsrc/
|
||||
cp $(TOP)/ext/recover/sqlite3recover.h tsrc/
|
||||
cp $(TOP)/ext/recover/dbdata.c tsrc/
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC)
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover $(AMALGAMATION_LINE_MACROS)
|
||||
|
||||
sqlite3ext.h: .target_source
|
||||
cp tsrc/sqlite3ext.h .
|
||||
@ -890,8 +891,10 @@ opcodes.lo: opcodes.c
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c opcodes.c
|
||||
|
||||
# BEGIN CRYPTO
|
||||
sqlcipher.lo: $(TOP)/src/sqlcipher.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/sqlcipher.c
|
||||
crypto.lo: $(TOP)/src/crypto.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/crypto.c
|
||||
crypto_impl.lo: $(TOP)/src/crypto_impl.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/crypto_impl.c
|
||||
crypto_openssl.lo: $(TOP)/src/crypto_openssl.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)/src/crypto_openssl.c
|
||||
crypto_nss.lo: $(TOP)/src/crypto_nss.c $(HDR)
|
||||
@ -1186,37 +1189,35 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
./mkkeywordhash$(BEXE) >keywordhash.h
|
||||
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/consio/console_io.c \
|
||||
$(TOP)/ext/consio/console_io.h \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/intck/sqlite3intck.c \
|
||||
$(TOP)/ext/intck/sqlite3intck.h \
|
||||
$(TOP)/ext/misc/appendvfs.c \
|
||||
$(TOP)/ext/misc/base64.c \
|
||||
$(TOP)/ext/misc/base85.c \
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/memtrace.c \
|
||||
$(TOP)/ext/misc/pcachetrace.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/misc/uint.c \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.h \
|
||||
$(TOP)/src/test_windirent.c \
|
||||
$(TOP)/src/test_windirent.h
|
||||
# Source files that go into making shell.c
|
||||
SHELL_SRC = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/consio/console_io.c \
|
||||
$(TOP)/ext/consio/console_io.h \
|
||||
$(TOP)/ext/misc/appendvfs.c \
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
$(TOP)/ext/misc/basexx.c \
|
||||
$(TOP)/ext/misc/base64.c \
|
||||
$(TOP)/ext/misc/base85.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/misc/uint.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/misc/memtrace.c \
|
||||
$(TOP)/ext/misc/pcachetrace.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.h \
|
||||
$(TOP)/src/test_windirent.c
|
||||
|
||||
shell.c: $(SHELL_DEP) $(TOP)/tool/mkshellc.tcl has_tclsh84
|
||||
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl has_tclsh84
|
||||
$(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c
|
||||
|
||||
|
||||
@ -1344,9 +1345,9 @@ testfixture$(TEXE): has_tclsh85 $(TESTFIXTURE_SRC)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \
|
||||
-o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
|
||||
|
||||
coretestprogs: testfixture$(BEXE) sqlite3$(BEXE)
|
||||
coretestprogs: $(TESTPROGS)
|
||||
|
||||
testprogs: $(TESTPROGS) srcck1$(BEXE) fuzzcheck$(TEXE) sessionfuzz$(TEXE)
|
||||
testprogs: coretestprogs srcck1$(BEXE) fuzzcheck$(TEXE) sessionfuzz$(TEXE)
|
||||
|
||||
# A very detailed test running most or all test cases
|
||||
fulltest: alltest fuzztest
|
||||
|
||||
80
Makefile.msc
80
Makefile.msc
@ -18,13 +18,6 @@ USE_AMALGAMATION = 1
|
||||
!ENDIF
|
||||
# <</mark>>
|
||||
|
||||
# Optionally set EXTRA_SRC to a list of C files to append to
|
||||
# the generated sqlite3.c.
|
||||
#
|
||||
!IFNDEF EXTRA_SRC
|
||||
EXTRA_SRC =
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable full warnings (-W4, etc) when compiling.
|
||||
#
|
||||
!IFNDEF USE_FULLWARN
|
||||
@ -1327,11 +1320,13 @@ LIBRESOBJS =
|
||||
# Core source code files, part 1.
|
||||
#
|
||||
SRC00 = \
|
||||
$(TOP)\src\sqlcipher.c \
|
||||
$(TOP)\src\crypto.c \
|
||||
$(TOP)\src\crypto_cc.c \
|
||||
$(TOP)\src\crypto_impl.c \
|
||||
$(TOP)\src\crypto_libtomcrypt.c \
|
||||
$(TOP)\src\crypto_nss.c \
|
||||
$(TOP)\src\crypto_openssl.c \
|
||||
$(TOP)\src\crypto.h \
|
||||
$(TOP)\src\sqlcipher.h \
|
||||
$(TOP)\src\alter.c \
|
||||
$(TOP)\src\analyze.c \
|
||||
@ -1608,8 +1603,6 @@ TESTEXT = \
|
||||
$(TOP)\ext\rtree\test_rtreedoc.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
$(TOP)\ext\recover\test_recover.c \
|
||||
$(TOP)\ext\intck\test_intck.c \
|
||||
$(TOP)\ext\intck\sqlite3intck.c \
|
||||
$(TOP)\ext\recover\dbdata.c
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
@ -1928,7 +1921,7 @@ mptest: mptester.exe
|
||||
echo > .target_source
|
||||
|
||||
sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-verify.exe
|
||||
$(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS) $(EXTRA_SRC)
|
||||
$(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS)
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
|
||||
@ -2278,44 +2271,39 @@ mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
|
||||
keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
.\mkkeywordhash.exe > keywordhash.h
|
||||
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\consio\console_io.c \
|
||||
$(TOP)\ext\consio\console_io.h \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\intck\sqlite3intck.c \
|
||||
$(TOP)\ext\intck\sqlite3intck.h \
|
||||
$(TOP)\ext\misc\appendvfs.c \
|
||||
$(TOP)\ext\misc\base64.c \
|
||||
$(TOP)\ext\misc\base85.c \
|
||||
$(TOP)\ext\misc\completion.c \
|
||||
$(TOP)\ext\misc\decimal.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\memtrace.c \
|
||||
$(TOP)\ext\misc\pcachetrace.c \
|
||||
$(TOP)\ext\misc\regexp.c \
|
||||
$(TOP)\ext\misc\series.c \
|
||||
$(TOP)\ext\misc\shathree.c \
|
||||
$(TOP)\ext\misc\sqlar.c \
|
||||
$(TOP)\ext\misc\uint.c \
|
||||
$(TOP)\ext\misc\zipfile.c \
|
||||
$(TOP)\ext\recover\dbdata.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.h \
|
||||
$(TOP)\src\test_windirent.c \
|
||||
$(TOP)\src\test_windirent.h
|
||||
# Source files that go into making shell.c
|
||||
SHELL_SRC = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\consio\console_io.c \
|
||||
$(TOP)\ext\consio\console_io.h \
|
||||
$(TOP)\ext\misc\appendvfs.c \
|
||||
$(TOP)\ext\misc\completion.c \
|
||||
$(TOP)\ext\misc\base64.c \
|
||||
$(TOP)\ext\misc\base85.c \
|
||||
$(TOP)\ext\misc\decimal.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\regexp.c \
|
||||
$(TOP)\ext\misc\series.c \
|
||||
$(TOP)\ext\misc\shathree.c \
|
||||
$(TOP)\ext\misc\uint.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\misc\memtrace.c \
|
||||
$(TOP)\ext\misc\pcachetrace.c \
|
||||
$(TOP)\ext\recover\dbdata.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.h \
|
||||
$(TOP)\src\test_windirent.c
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
#
|
||||
!IF $(USE_ZLIB)!=0
|
||||
SHELL_DEP = $(SHELL_DEP) $(TOP)\ext\misc\sqlar.c
|
||||
SHELL_DEP = $(SHELL_DEP) $(TOP)\ext\misc\zipfile.c
|
||||
SHELL_SRC = $(SHELL_SRC) $(TOP)\ext\misc\sqlar.c
|
||||
SHELL_SRC = $(SHELL_SRC) $(TOP)\ext\misc\zipfile.c
|
||||
!ENDIF
|
||||
|
||||
shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl
|
||||
shell.c: $(SHELL_SRC) $(TOP)\tool\mkshellc.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkshellc.tcl > shell.c
|
||||
|
||||
zlib:
|
||||
@ -2502,9 +2490,9 @@ extensiontest: testfixture.exe testloadext.dll
|
||||
tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe $(TOP)\tool\mktoolzip.tcl
|
||||
.\testfixture.exe $(TOP)\tool\mktoolzip.tcl
|
||||
|
||||
coretestprogs: testfixture.exe sqlite3.exe
|
||||
coretestprogs: $(TESTPROGS)
|
||||
|
||||
testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe
|
||||
testprogs: coretestprogs srcck1.exe fuzzcheck.exe sessionfuzz.exe
|
||||
|
||||
fulltest: alltest fuzztest
|
||||
|
||||
@ -2559,7 +2547,7 @@ mdevtest:
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: testfixture.exe
|
||||
releasetest: testfixture.exe fuzztest
|
||||
testfixture.exe $(TOP)\test\testrunner.tcl release
|
||||
|
||||
|
||||
|
||||
@ -3,10 +3,7 @@
|
||||
"default_subspecs": "standard",
|
||||
"description": "SQLCipher is an open source extension to SQLite that provides transparent 256-bit AES encryption of database files.",
|
||||
"homepage": "https://www.zetetic.net/sqlcipher/",
|
||||
"license": {
|
||||
"type": "BSD-3-Clause",
|
||||
"file": "LICENSE.md"
|
||||
},
|
||||
"license": "BSD",
|
||||
"name": "SQLCipher",
|
||||
"platforms": {
|
||||
"ios": "12.0",
|
||||
@ -18,10 +15,10 @@
|
||||
"requires_arc": false,
|
||||
"source": {
|
||||
"git": "https://github.com/sqlcipher/sqlcipher.git",
|
||||
"tag": "v4.6.1"
|
||||
"tag": "v4.6.0"
|
||||
},
|
||||
"summary": "Full Database Encryption for SQLite.",
|
||||
"version": "4.6.1",
|
||||
"version": "4.6.0",
|
||||
"subspecs": [
|
||||
{
|
||||
"compiler_flags": [
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
@ -18,13 +18,6 @@
|
||||
TOP = .
|
||||
|
||||
|
||||
# Optionally set EXTRA_SRC to a list of C files to append to
|
||||
# the generated sqlite3.c.
|
||||
#
|
||||
!IFNDEF EXTRA_SRC
|
||||
EXTRA_SRC =
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable full warnings (-W4, etc) when compiling.
|
||||
#
|
||||
!IFNDEF USE_FULLWARN
|
||||
|
||||
@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
|
||||
# so that we create the export library with the dll.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
AC_INIT([sqlite],[3.46.1])
|
||||
AC_INIT([sqlite],[3.45.3])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
|
||||
18
configure
vendored
18
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.71 for sqlcipher 3.46.1.
|
||||
# Generated by GNU Autoconf 2.71 for sqlcipher 3.45.3.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
|
||||
@ -618,8 +618,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlcipher'
|
||||
PACKAGE_TARNAME='sqlcipher'
|
||||
PACKAGE_VERSION='3.46.1'
|
||||
PACKAGE_STRING='sqlcipher 3.46.1'
|
||||
PACKAGE_VERSION='3.45.3'
|
||||
PACKAGE_STRING='sqlcipher 3.45.3'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -1379,7 +1379,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlcipher 3.46.1 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlcipher 3.45.3 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1445,7 +1445,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.46.1:";;
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.45.3:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1588,7 +1588,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlcipher configure 3.46.1
|
||||
sqlcipher configure 3.45.3
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@ -1863,7 +1863,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlcipher $as_me 3.46.1, which was
|
||||
It was created by sqlcipher $as_me 3.45.3, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@ -14684,7 +14684,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlcipher $as_me 3.46.1, which was
|
||||
This file was extended by sqlcipher $as_me 3.45.3, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -14752,7 +14752,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
sqlcipher config.status 3.46.1
|
||||
sqlcipher config.status 3.45.3
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@ -683,7 +683,6 @@ other than that, the order of directives in Lemon is arbitrary.</p>
|
||||
<li><tt><a href='#pifdef'>%endif</a></tt>
|
||||
<li><tt><a href='#extraarg'>%extra_argument</a></tt>
|
||||
<li><tt><a href='#pfallback'>%fallback</a></tt>
|
||||
<li><tt><a href='#reallc'>%free</a></tt>
|
||||
<li><tt><a href='#pifdef'>%if</a></tt>
|
||||
<li><tt><a href='#pifdef'>%ifdef</a></tt>
|
||||
<li><tt><a href='#pifdef'>%ifndef</a></tt>
|
||||
@ -694,7 +693,6 @@ other than that, the order of directives in Lemon is arbitrary.</p>
|
||||
<li><tt><a href='#parse_accept'>%parse_accept</a></tt>
|
||||
<li><tt><a href='#parse_failure'>%parse_failure</a></tt>
|
||||
<li><tt><a href='#pright'>%right</a></tt>
|
||||
<li><tt><a href='#reallc'>%realloc</a></tt>
|
||||
<li><tt><a href='#stack_overflow'>%stack_overflow</a></tt>
|
||||
<li><tt><a href='#stack_size'>%stack_size</a></tt>
|
||||
<li><tt><a href='#start_symbol'>%start_symbol</a></tt>
|
||||
@ -1202,21 +1200,6 @@ match any input token.</p>
|
||||
the wildcard token and some other token, the other token is always used.
|
||||
The wildcard token is only matched if there are no alternatives.</p>
|
||||
|
||||
<a id='reallc'></a>
|
||||
<h4>4.4.26 The <tt>%realloc</tt> and <tt>%free</tt> directives</h4>
|
||||
|
||||
<p>The <tt>%realloc</tt> and <tt>%free</tt> directives defines function
|
||||
that allocate and free heap memory. The signatures of these functions
|
||||
should be the same as the realloc() and free() functions from the standard
|
||||
C library.
|
||||
|
||||
<p>If both of these functions are defined
|
||||
then these functions are used to allocate and free
|
||||
memory for supplemental parser stack space, if the initial
|
||||
parse stack space is exceeded. The initial parser stack size
|
||||
is specified by either <tt>%stack_size</tt> or the
|
||||
-DYYSTACKDEPTH compile-time flag.
|
||||
|
||||
<a id='errors'></a>
|
||||
<h2>5.0 Error Processing</h2>
|
||||
|
||||
@ -1241,7 +1224,6 @@ to begin parsing a new file. This is what will happen at the very
|
||||
first syntax error, of course, if there are no instances of the
|
||||
"error" non-terminal in your grammar.</p>
|
||||
|
||||
|
||||
<a id='history'></a>
|
||||
<h2>6.0 History of Lemon</h2>
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
<li> 3.3. <a href=#source_code_test_failures>Investigating Source Code Test Failures</a>
|
||||
</ul>
|
||||
<li> 4. <a href=#testrunner_options>Extra testrunner.tcl Options</a>
|
||||
# 4. Extra testrunner.tcl Options
|
||||
<li> 5. <a href=#cpu_cores>Controlling CPU Core Utilization</a>
|
||||
</ul>
|
||||
|
||||
@ -28,18 +29,12 @@ multiple jobs. It supports the following types of tests:
|
||||
|
||||
* Tcl test scripts.
|
||||
|
||||
* Tests run with `make` commands. Examples:
|
||||
- `make mdevtest`
|
||||
- `make releasetest`
|
||||
- `make sdevtest`
|
||||
- `make testrunner`
|
||||
* Tests run with [make] commands. Specifically, at time of writing,
|
||||
[make fuzztest], [make mptest], [make sourcetest] and [make threadtest].
|
||||
|
||||
testrunner.tcl pipes the output of all tests and builds run into log file
|
||||
**testrunner.log**, created in the current working directory. Search this
|
||||
file to find details of errors. Suggested search commands:
|
||||
|
||||
* `grep "^!" testrunner.log`
|
||||
* `grep failed testrunner.log`
|
||||
**testrunner.log**, created in the cwd directory. Searching this file for
|
||||
"failed" is a good way to find the output of a failed test.
|
||||
|
||||
testrunner.tcl also populates SQLite database **testrunner.db**. This database
|
||||
contains details of all tests run, running and to be run. A useful query
|
||||
@ -65,7 +60,7 @@ Running:
|
||||
|
||||
in another terminal is a good way to keep an eye on a long running test.
|
||||
|
||||
Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
|
||||
Sometimes testrunner.tcl uses the [testfixture] binary that it is run with
|
||||
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
|
||||
other binaries in specific configurations to test (see "Source Tests").
|
||||
|
||||
@ -73,9 +68,9 @@ other binaries in specific configurations to test (see "Source Tests").
|
||||
# 2. Binary Tests
|
||||
|
||||
The commands described in this section all run various combinations of the Tcl
|
||||
test scripts using the `testfixture` binary used to run the testrunner.tcl
|
||||
test scripts using the [testfixture] binary used to run the testrunner.tcl
|
||||
script (i.e. they do not invoke the compiler to build new binaries, or the
|
||||
`make` command to run tests that are not Tcl scripts). The procedure to run
|
||||
[make] command to run tests that are not Tcl scripts). The procedure to run
|
||||
these tests is therefore:
|
||||
|
||||
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
|
||||
@ -198,7 +193,7 @@ TODO: ./configure + Makefile.msc build systems.
|
||||
## 3.1. Commands to Run SQLite Tests
|
||||
|
||||
The **mdevtest** command is equivalent to running the veryquick tests and
|
||||
the `make fuzztest` target once for each of two --enable-all builds - one
|
||||
the [make fuzztest] target once for each of two --enable-all builds - one
|
||||
with debugging enabled and one without:
|
||||
|
||||
```
|
||||
@ -288,7 +283,7 @@ a dos \*.bat file on windows. For example:
|
||||
```
|
||||
|
||||
The generated bash or \*.bat file script accepts a single argument - a makefile
|
||||
target to build. This may be used either to run a `make` command test directly,
|
||||
target to build. This may be used either to run a [make] command test directly,
|
||||
or else to build a testfixture (or testfixture.exe) binary with which to
|
||||
run a Tcl test script, as <a href=#binary_test_failures>described above</a>.
|
||||
|
||||
@ -315,16 +310,6 @@ would normally execute into the testrunner.log file. Example:
|
||||
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
|
||||
```
|
||||
|
||||
The **--explain** option is similar to --dryrun in that it prevents testrunner.tcl
|
||||
from building any binaries or running any tests. The difference is that --explain
|
||||
prints on standard output a human-readable summary of all the builds and tests that
|
||||
would have been run.
|
||||
|
||||
```
|
||||
# Show what builds and tests would have been run
|
||||
tclsh $TESTDIR/testrunner.tcl --explain mdevtest
|
||||
```
|
||||
|
||||
<a name=cpu_cores></a>
|
||||
# 5. Controlling CPU Core Utilization
|
||||
|
||||
@ -354,3 +339,6 @@ testrunner.log and testrunner.db files:
|
||||
```
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@ -53,6 +53,11 @@
|
||||
# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */
|
||||
#endif
|
||||
|
||||
#if CIO_WIN_WC_XLATE
|
||||
/* Character used to represent a known-incomplete UTF-8 char group (<28>) */
|
||||
static WCHAR cBadGroup = 0xfffd;
|
||||
#endif
|
||||
|
||||
#if CIO_WIN_WC_XLATE
|
||||
static HANDLE handleOfFile(FILE *pf){
|
||||
int fileDesc = _fileno(pf);
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# TESTRUNNER: shell
|
||||
#
|
||||
# The focus of this file is testing the CLI shell tool. Specifically,
|
||||
# the ".recommend" command.
|
||||
@ -465,13 +464,6 @@ do_execsql_test 5.3 {
|
||||
t2 t2_idx_0001295b {100 20 5}
|
||||
}
|
||||
|
||||
do_catchsql_test 5.4 {
|
||||
SELECT sqlite_expert_rem(123, 123);
|
||||
} {1 {no such function: sqlite_expert_rem}}
|
||||
do_catchsql_test 5.5 {
|
||||
SELECT sqlite_expert_sample();
|
||||
} {1 {no such function: sqlite_expert_sample}}
|
||||
|
||||
if 0 {
|
||||
do_test expert1-6.0 {
|
||||
catchcmd :memory: {
|
||||
|
||||
@ -626,7 +626,7 @@ static int expertFilter(
|
||||
pCsr->pData = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
|
||||
"SELECT * FROM main.%Q WHERE sqlite_expert_sample()", pVtab->pTab->zName
|
||||
"SELECT * FROM main.%Q WHERE sample()", pVtab->pTab->zName
|
||||
);
|
||||
}
|
||||
|
||||
@ -1500,7 +1500,7 @@ struct IdxRemCtx {
|
||||
};
|
||||
|
||||
/*
|
||||
** Implementation of scalar function sqlite_expert_rem().
|
||||
** Implementation of scalar function rem().
|
||||
*/
|
||||
static void idxRemFunc(
|
||||
sqlite3_context *pCtx,
|
||||
@ -1513,7 +1513,7 @@ static void idxRemFunc(
|
||||
assert( argc==2 );
|
||||
|
||||
iSlot = sqlite3_value_int(argv[0]);
|
||||
assert( iSlot<p->nSlot );
|
||||
assert( iSlot<=p->nSlot );
|
||||
pSlot = &p->aSlot[iSlot];
|
||||
|
||||
switch( pSlot->eType ){
|
||||
@ -1624,8 +1624,7 @@ static int idxPopulateOneStat1(
|
||||
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
|
||||
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
|
||||
zCols = idxAppendText(&rc, zCols,
|
||||
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
|
||||
zComma, zName, nCol, zName, zColl
|
||||
"%sx.%Q IS rem(%d, x.%Q) COLLATE %s", zComma, zName, nCol, zName, zColl
|
||||
);
|
||||
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
|
||||
}
|
||||
@ -1758,13 +1757,13 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
|
||||
rc = sqlite3_create_function(dbrem, "sqlite_expert_rem",
|
||||
2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
|
||||
rc = sqlite3_create_function(
|
||||
dbrem, "rem", 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(p->db, "sqlite_expert_sample",
|
||||
0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
|
||||
rc = sqlite3_create_function(
|
||||
p->db, "sample", 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
@ -1816,9 +1815,6 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
|
||||
}
|
||||
|
||||
sqlite3_create_function(p->db, "sqlite_expert_rem", 2, SQLITE_UTF8, 0,0,0,0);
|
||||
sqlite3_create_function(p->db, "sqlite_expert_sample", 0,SQLITE_UTF8,0,0,0,0);
|
||||
|
||||
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
|
||||
return rc;
|
||||
}
|
||||
@ -1952,7 +1948,7 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
|
||||
sqlite3_stmt *pSql = 0;
|
||||
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
|
||||
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
|
||||
" AND sql NOT LIKE 'CREATE VIRTUAL %%' ORDER BY rowid"
|
||||
" AND sql NOT LIKE 'CREATE VIRTUAL %%'"
|
||||
);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
||||
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
|
||||
|
||||
@ -4014,24 +4014,22 @@ static int fts3IntegrityMethod(
|
||||
char **pzErr /* Write error message here */
|
||||
){
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
int rc;
|
||||
int bOk = 0;
|
||||
|
||||
UNUSED_PARAMETER(isQuick);
|
||||
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
|
||||
assert( rc!=SQLITE_CORRUPT_VTAB );
|
||||
if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
|
||||
assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
" FTS%d table %s.%s: %s",
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
|
||||
if( *pzErr ) rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_OK && bOk==0 ){
|
||||
}else if( bOk==0 ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname);
|
||||
if( *pzErr==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
return rc;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -446,7 +446,7 @@ static void fts3SnippetDetails(
|
||||
}
|
||||
mCover |= mPhrase;
|
||||
|
||||
for(j=0; j<pPhrase->nToken && j<pIter->nSnippet; j++){
|
||||
for(j=0; j<pPhrase->nToken; j++){
|
||||
mHighlight |= (mPos>>j);
|
||||
}
|
||||
|
||||
|
||||
@ -5372,12 +5372,7 @@ int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_CORRUPT_VTAB ){
|
||||
rc = SQLITE_OK;
|
||||
*pbOk = 0;
|
||||
}else{
|
||||
*pbOk = (rc==SQLITE_OK && cksum1==cksum2);
|
||||
}
|
||||
*pbOk = (rc==SQLITE_OK && cksum1==cksum2);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -55,8 +55,8 @@ struct Fts5PhraseIter {
|
||||
** EXTENSION API FUNCTIONS
|
||||
**
|
||||
** xUserData(pFts):
|
||||
** Return a copy of the pUserData pointer passed to the xCreateFunction()
|
||||
** API when the extension function was registered.
|
||||
** Return a copy of the context pointer the extension function was
|
||||
** registered with.
|
||||
**
|
||||
** xColumnTotalSize(pFts, iCol, pnToken):
|
||||
** If parameter iCol is less than zero, set output variable *pnToken
|
||||
|
||||
@ -324,11 +324,7 @@ int sqlite3Fts5ExprNew(
|
||||
}
|
||||
|
||||
sqlite3_free(sParse.apPhrase);
|
||||
if( 0==*pzErr ){
|
||||
*pzErr = sParse.zErr;
|
||||
}else{
|
||||
sqlite3_free(sParse.zErr);
|
||||
}
|
||||
*pzErr = sParse.zErr;
|
||||
return sParse.rc;
|
||||
}
|
||||
|
||||
@ -2456,7 +2452,6 @@ Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
|
||||
assert( pRight->eType==FTS5_STRING
|
||||
|| pRight->eType==FTS5_TERM
|
||||
|| pRight->eType==FTS5_EOF
|
||||
|| (pRight->eType==FTS5_AND && pParse->bPhraseToAnd)
|
||||
);
|
||||
|
||||
if( pLeft->eType==FTS5_AND ){
|
||||
|
||||
@ -1700,7 +1700,6 @@ static int fts5UpdateMethod(
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = fts5SpecialDelete(pTab, apVal);
|
||||
bUpdateOrDelete = 1;
|
||||
}
|
||||
}else{
|
||||
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
|
||||
@ -2875,16 +2874,14 @@ int sqlite3Fts5GetTokenizer(
|
||||
if( pMod==0 ){
|
||||
assert( nArg>0 );
|
||||
rc = SQLITE_ERROR;
|
||||
if( pzErr ) *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
|
||||
*pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
|
||||
}else{
|
||||
rc = pMod->x.xCreate(
|
||||
pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok
|
||||
);
|
||||
pConfig->pTokApi = &pMod->x;
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( pzErr && rc!=SQLITE_NOMEM ){
|
||||
*pzErr = sqlite3_mprintf("error in tokenizer constructor");
|
||||
}
|
||||
if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
|
||||
}else{
|
||||
pConfig->ePattern = sqlite3Fts5TokenizerPattern(
|
||||
pMod->x.xCreate, pConfig->pTok
|
||||
@ -2978,25 +2975,18 @@ static int fts5IntegrityMethod(
|
||||
|
||||
assert( pzErr!=0 && *pzErr==0 );
|
||||
UNUSED_PARAM(isQuick);
|
||||
assert( pTab->p.pConfig->pzErrmsg==0 );
|
||||
pTab->p.pConfig->pzErrmsg = pzErr;
|
||||
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
|
||||
if( *pzErr==0 && rc!=SQLITE_OK ){
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
|
||||
zSchema, zTabname);
|
||||
rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM;
|
||||
}else{
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
" FTS5 table %s.%s: %s",
|
||||
zSchema, zTabname, sqlite3_errstr(rc));
|
||||
}
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
|
||||
zSchema, zTabname);
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
" FTS5 table %s.%s: %s",
|
||||
zSchema, zTabname, sqlite3_errstr(rc));
|
||||
}
|
||||
|
||||
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
|
||||
pTab->p.pConfig->pzErrmsg = 0;
|
||||
|
||||
return rc;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fts5Init(sqlite3 *db){
|
||||
|
||||
@ -79,7 +79,7 @@ static int fts5AsciiCreate(
|
||||
int i;
|
||||
memset(p, 0, sizeof(AsciiTokenizer));
|
||||
memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar));
|
||||
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){
|
||||
fts5AsciiAddExceptions(p, zArg, 1);
|
||||
@ -90,7 +90,6 @@ static int fts5AsciiCreate(
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK && i<nArg ) rc = SQLITE_ERROR;
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5AsciiDelete((Fts5Tokenizer*)p);
|
||||
p = 0;
|
||||
@ -382,16 +381,17 @@ static int fts5UnicodeCreate(
|
||||
}
|
||||
|
||||
/* Search for a "categories" argument */
|
||||
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
if( 0==sqlite3_stricmp(azArg[i], "categories") ){
|
||||
zCat = azArg[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = unicodeSetCategories(p, zCat);
|
||||
}
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
|
||||
@ -416,7 +416,6 @@ static int fts5UnicodeCreate(
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
if( i<nArg && rc==SQLITE_OK ) rc = SQLITE_ERROR;
|
||||
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
@ -1299,7 +1298,7 @@ static int fts5TriCreate(
|
||||
int i;
|
||||
pNew->bFold = 1;
|
||||
pNew->iFoldParam = 0;
|
||||
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
|
||||
@ -1317,7 +1316,6 @@ static int fts5TriCreate(
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
if( i<nArg && rc==SQLITE_OK ) rc = SQLITE_ERROR;
|
||||
|
||||
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
|
||||
@ -377,28 +377,4 @@ do_catchsql_test 12.3.3 {
|
||||
SELECT fts5_collist(t1, 1) FROM t1('one AND two');
|
||||
} {0 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 13.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=ascii);
|
||||
INSERT INTO t1 VALUES('a b c'), ('d e f');
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
do_catchsql_test 13.2 {
|
||||
SELECT highlight(t1, 0, '[', ']') FROM t1
|
||||
} {0 {{a b c} {d e f}}}
|
||||
|
||||
do_execsql_test 13.3 {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_schema SET sql = 'CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=blah)'
|
||||
WHERE name = 't1';
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_catchsql_test 13.4 {
|
||||
SELECT highlight(t1, 0, '[', ']') FROM t1
|
||||
} {1 {no such tokenizer: blah}}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -57,6 +57,7 @@ foreach_detail_mode $testprefix {
|
||||
|
||||
} ;# foreach_detail_mode...
|
||||
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(a);
|
||||
INSERT INTO x2(x2, rank) VALUES('crisismerge', 2);
|
||||
@ -79,18 +80,5 @@ do_faultsim_test 4 -faults oom-* -prep {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
set TMPDBERROR {1 {unable to open a temporary database file for storing temporary tables}}
|
||||
|
||||
do_faultsim_test 5 -faults oom-t* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { PRAGMA temp_store = memory }
|
||||
} -body {
|
||||
execsql { PRAGMA integrity_check }
|
||||
} -test {
|
||||
if {[string match {*error code=7*} $testresult]==0} {
|
||||
faultsim_test_result {0 ok} {1 SQLITE_NOMEM} $::TMPDBERROR
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@ -380,32 +380,5 @@ do_execsql_test 12.3 {
|
||||
} {ok}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 13.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=ascii);
|
||||
INSERT INTO t1 VALUES('a b c'), ('d e f');
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_catchsql_test 13.2 {
|
||||
PRAGMA integrity_check;
|
||||
} {0 ok}
|
||||
|
||||
do_execsql_test 13.3 {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_schema SET sql = 'CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=blah)'
|
||||
WHERE name = 't1';
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
breakpoint
|
||||
do_catchsql_test 13.4 {
|
||||
PRAGMA integrity_check;
|
||||
} {1 {no such tokenizer: blah}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@ -42,22 +42,6 @@ do_execsql_test 1.2 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE xyz USING fts5 (
|
||||
name,
|
||||
content=''
|
||||
);
|
||||
|
||||
INSERT INTO xyz(xyz, rank) VALUES('secure-delete', 1);
|
||||
INSERT INTO xyz (rowid, name) VALUES(1, 'A');
|
||||
INSERT INTO xyz (rowid, name) VALUES(2, 'A');
|
||||
INSERT INTO xyz(xyz, rowid, name) VALUES('delete', 2, 'A');
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
pragma quick_check;
|
||||
} {ok}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -85,25 +85,5 @@ do_execsql_test 1.7 {
|
||||
SELECT highlight(t1, 0, '>', '<') FROM t1('BB mess');
|
||||
} {AAdont>BBmess<}
|
||||
|
||||
# 2024-08-06 https://sqlite.org/forum/forumpost/171bcc2bcd
|
||||
# Error handling of tokenize= arguments.
|
||||
#
|
||||
foreach {n tkz} {
|
||||
1 {ascii none}
|
||||
2 {unicode61 none}
|
||||
3 {porter none}
|
||||
4 {trigram none}
|
||||
5 {ascii none 0}
|
||||
6 {unicode61 none 0}
|
||||
7 {porter none 0}
|
||||
8 {trigram none 0}
|
||||
} {
|
||||
db eval {DROP TABLE IF EXISTS t2;}
|
||||
do_catchsql_test 2.$n "
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a,b,c,tokenize='$tkz');
|
||||
" {1 {error in tokenizer constructor}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@ -69,9 +69,6 @@ do_execsql_test 2.0 {
|
||||
INSERT INTO t1 VALUES('abcdefghijklm');
|
||||
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
|
||||
}
|
||||
do_catchsql_test 2.0.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(z, tokenize='trigram case_sensitive');
|
||||
} {1 {error in tokenizer constructor}}
|
||||
|
||||
foreach {tn s res} {
|
||||
1 abc "(abc)defghijklm"
|
||||
@ -209,7 +206,7 @@ do_execsql_test 7.0 {
|
||||
(20, "жираф.png"),
|
||||
(30, "cat.png"),
|
||||
(40, "кот.png"),
|
||||
(50, "misic-ðµ-.mp3");
|
||||
(50, "misic-🎵-.mp3");
|
||||
}
|
||||
do_execsql_test 7.1 {
|
||||
SELECT rowid FROM f WHERE +filename GLOB '*ир*';
|
||||
|
||||
@ -21,9 +21,6 @@ do_execsql_test 1.0 "
|
||||
INSERT INTO t1 VALUES('abc\u0303defghijklm');
|
||||
INSERT INTO t1 VALUES('a\u0303b\u0303c\u0303defghijklm');
|
||||
"
|
||||
do_catchsql_test 1.0.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(z, tokenize='trigram remove_diacritics');
|
||||
} {1 {error in tokenizer constructor}}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('abc');
|
||||
|
||||
@ -471,7 +471,7 @@ static void icuLoadCollation(
|
||||
UCollator *pUCollator; /* ICU library collation object */
|
||||
int rc; /* Return code from sqlite3_create_collation_x() */
|
||||
|
||||
assert(nArg==2 || nArg==3);
|
||||
assert(nArg==2);
|
||||
(void)nArg; /* Unused parameter */
|
||||
zLocale = (const char *)sqlite3_value_text(apArg[0]);
|
||||
zName = (const char *)sqlite3_value_text(apArg[1]);
|
||||
@ -486,39 +486,7 @@ static void icuLoadCollation(
|
||||
return;
|
||||
}
|
||||
assert(p);
|
||||
if(nArg==3){
|
||||
const char *zOption = (const char*)sqlite3_value_text(apArg[2]);
|
||||
static const struct {
|
||||
const char *zName;
|
||||
UColAttributeValue val;
|
||||
} aStrength[] = {
|
||||
{ "PRIMARY", UCOL_PRIMARY },
|
||||
{ "SECONDARY", UCOL_SECONDARY },
|
||||
{ "TERTIARY", UCOL_TERTIARY },
|
||||
{ "DEFAULT", UCOL_DEFAULT_STRENGTH },
|
||||
{ "QUARTERNARY", UCOL_QUATERNARY },
|
||||
{ "IDENTICAL", UCOL_IDENTICAL },
|
||||
};
|
||||
unsigned int i;
|
||||
for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
|
||||
if( sqlite3_stricmp(zOption,aStrength[i].zName)==0 ){
|
||||
ucol_setStrength(pUCollator, aStrength[i].val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i>=sizeof(aStrength)/sizeof(aStrength[0]) ){
|
||||
sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p));
|
||||
sqlite3_str_appendf(pStr,
|
||||
"unknown collation strength \"%s\" - should be one of:",
|
||||
zOption);
|
||||
for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
|
||||
sqlite3_str_appendf(pStr, " %s", aStrength[i].zName);
|
||||
}
|
||||
sqlite3_result_error(p, sqlite3_str_value(pStr), -1);
|
||||
sqlite3_free(sqlite3_str_finish(pStr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
|
||||
icuCollationColl, icuCollationDel
|
||||
);
|
||||
@ -541,7 +509,6 @@ int sqlite3IcuInit(sqlite3 *db){
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} scalars[] = {
|
||||
{"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
|
||||
{"icu_load_collation",3,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
||||
{"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
|
||||
{"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
|
||||
@ -1,332 +0,0 @@
|
||||
# 2008 Feb 19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The focus of this file is testing the incremental integrity check
|
||||
# (intck) extension.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] intck_common.tcl]
|
||||
set testprefix intck1
|
||||
return_if_no_intck
|
||||
|
||||
foreach {tn sql} {
|
||||
1 "CREATE TABLE t1(a PRIMARY KEY, b)"
|
||||
2 "CREATE TABLE t2(a PRIMARY KEY, b) WITHOUT ROWID "
|
||||
3 "CREATE TABLE t3(a PRIMARY KEY, b) WITHOUT rowID;"
|
||||
4 "CREATE TABLE t4(a PRIMARY KEY, ROWID)"
|
||||
5 {CREATE TABLE t5(a PRIMARY KEY, ROWID) WITHOUT ROWID
|
||||
}
|
||||
} {
|
||||
do_test 1.1.$tn {
|
||||
db eval $sql
|
||||
set {} {}
|
||||
} {}
|
||||
}
|
||||
|
||||
set space " \n\v\t\r\f"
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT name, (rtrim(sql, $space) LIKE '%rowid')
|
||||
FROM sqlite_schema WHERE type='table'
|
||||
ORDER BY 1
|
||||
} {
|
||||
t1 0
|
||||
t2 1
|
||||
t3 1
|
||||
t4 0
|
||||
t5 1
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
CREATE TABLE x1(a COLLATE nocase, b INTEGER, c BLOB);
|
||||
INSERT INTO x1 VALUES('lEtTeRs', 1234, 1234);
|
||||
}
|
||||
do_execsql_test 1.3.1 {
|
||||
WITH wrapper(c1, c2, c3) AS (
|
||||
SELECT a, b, c FROM x1
|
||||
)
|
||||
SELECT * FROM wrapper WHERE c1='letters';
|
||||
} {lEtTeRs 1234 1234}
|
||||
do_execsql_test 1.3.2 {
|
||||
WITH wrapper(c1, c2, c3) AS (
|
||||
SELECT a, b, c FROM x1
|
||||
)
|
||||
SELECT * FROM wrapper WHERE c2='1234';
|
||||
} {lEtTeRs 1234 1234}
|
||||
do_execsql_test 1.3.2 {
|
||||
WITH wrapper(c1, c2, c3) AS (
|
||||
SELECT a, b, c FROM x1
|
||||
)
|
||||
SELECT * FROM wrapper WHERE c3='1234';
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
CREATE TABLE z1(a, b);
|
||||
CREATE INDEX z1ab ON z1(a+b COLLATE nocase);
|
||||
}
|
||||
do_execsql_test 1.4.1 {
|
||||
SELECT * FROM z1 INDEXED BY z1ab
|
||||
}
|
||||
|
||||
do_catchsql_test 1.5.1 {
|
||||
CREATE INDEX z1b ON z1(b ASC NULLS LAST);
|
||||
} {1 {unsupported use of NULLS LAST}}
|
||||
do_catchsql_test 1.5.2 {
|
||||
CREATE INDEX z1b ON z1(b DESC NULLS LAST);
|
||||
} {1 {unsupported use of NULLS LAST}}
|
||||
do_catchsql_test 1.5.3 {
|
||||
CREATE INDEX z1b ON z1(b ASC NULLS FIRST);
|
||||
} {1 {unsupported use of NULLS FIRST}}
|
||||
do_catchsql_test 1.5.4 {
|
||||
CREATE INDEX z1b ON z1(b DESC NULLS FIRST);
|
||||
} {1 {unsupported use of NULLS FIRST}}
|
||||
|
||||
|
||||
reset_db
|
||||
do_execsql_test 1.6.1 {
|
||||
CREATE TABLE t1(i INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
ANALYZE;
|
||||
INSERT INTO sqlite_stat1 VALUES('t1', 'i1', '10000 10000');
|
||||
ANALYZE sqlite_schema;
|
||||
} {}
|
||||
do_eqp_test 1.6.2 {
|
||||
SELECT 1 FROM t1 INDEXED BY i1 WHERE (b, i) IS (?, ?);
|
||||
} {SEARCH}
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_test 2.0 {
|
||||
set ic [sqlite3_intck db main]
|
||||
$ic close
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
|
||||
CREATE INDEX i1 ON t1(a COLLATE nocase);
|
||||
CREATE INDEX i2 ON t1(b, a);
|
||||
CREATE INDEX i3 ON t1(b + a COLLATE nocase) WHERE a!=1;
|
||||
}
|
||||
|
||||
do_intck_test 2.2 {
|
||||
}
|
||||
|
||||
# Delete a row from each of the i1 and i2 indexes using the imposter
|
||||
# table interface.
|
||||
#
|
||||
do_test 2.3 {
|
||||
db eval {SELECT name, rootpage FROM sqlite_schema} {
|
||||
set R($name) $rootpage
|
||||
}
|
||||
sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(i1)
|
||||
db eval { CREATE TABLE imp1(a PRIMARY KEY, rowid) WITHOUT ROWID; }
|
||||
sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(i2)
|
||||
db eval { CREATE TABLE imp2(b, a, rowid, PRIMARY KEY(b, a)) WITHOUT ROWID; }
|
||||
sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0
|
||||
|
||||
db eval {
|
||||
DELETE FROM imp1 WHERE rowid=1;
|
||||
DELETE FROM imp2 WHERE rowid=2;
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
} {}
|
||||
|
||||
do_intck_test 2.4 {
|
||||
{entry (1,1) missing from index i1}
|
||||
{entry (4,3,2) missing from index i2}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE x1(a, b, c, PRIMARY KEY(c, b)) WITHOUT ROWID;
|
||||
CREATE INDEX x1a ON x1(a COLLATE nocase);
|
||||
|
||||
INSERT INTO x1 VALUES(1, 2, 'three');
|
||||
INSERT INTO x1 VALUES(4, 5, 'six');
|
||||
INSERT INTO x1 VALUES(7, 8, 'nine');
|
||||
}
|
||||
|
||||
do_intck_test 3.1 { }
|
||||
|
||||
do_test 3.2 {
|
||||
db eval {SELECT name, rootpage FROM sqlite_schema} {
|
||||
set R($name) $rootpage
|
||||
}
|
||||
sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $R(x1a)
|
||||
db eval { CREATE TABLE imp1(c, b, a, PRIMARY KEY(c, b)) WITHOUT ROWID }
|
||||
sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0
|
||||
|
||||
db eval {
|
||||
DELETE FROM imp1 WHERE a=5;
|
||||
}
|
||||
execsql_pp {
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
} {}
|
||||
|
||||
do_intck_test 3.3 {
|
||||
{entry (4,'six',5) missing from index x1a}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE www(x, y, z);
|
||||
CREATE INDEX w1 ON www( (x+1), z );
|
||||
INSERT INTO www VALUES(1, 1, 1), (2, 2, 2);
|
||||
}
|
||||
|
||||
do_intck_test 4.1 { }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a COLLATE NOCASE);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
}
|
||||
|
||||
do_test 5.1 {
|
||||
set ic [sqlite3_intck db nosuchdb]
|
||||
$ic step
|
||||
} {SQLITE_ERROR}
|
||||
|
||||
do_test 5.2 {
|
||||
$ic close
|
||||
set ic [sqlite3_intck db {}]
|
||||
while {[$ic step]=="SQLITE_OK"} {}
|
||||
set res [$ic error]
|
||||
$ic close
|
||||
set res
|
||||
} {SQLITE_OK {}}
|
||||
|
||||
do_test 5.3 { test_do_intck db "main" } {}
|
||||
|
||||
do_test 5.4 {
|
||||
set ret {}
|
||||
set ic [sqlite3_intck db main]
|
||||
db eval [$ic test_sql t1] {
|
||||
if {$error_message!=""} { lappend ret $error_message }
|
||||
}
|
||||
$ic close
|
||||
set ret
|
||||
} {}
|
||||
|
||||
do_test 5.5 {
|
||||
set ret {}
|
||||
set ic [sqlite3_intck db main]
|
||||
db eval [$ic test_sql {}] {
|
||||
if {$error_message!=""} { lappend ret $error_message }
|
||||
}
|
||||
$ic close
|
||||
set ret
|
||||
} {}
|
||||
|
||||
db cache flush
|
||||
|
||||
do_test 5.6 {
|
||||
set ret {}
|
||||
set ic [sqlite3_intck db main]
|
||||
$ic step
|
||||
db eval [$ic test_sql {}] {
|
||||
if {$error_message!=""} { lappend ret $error_message }
|
||||
}
|
||||
$ic close
|
||||
set ret
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(x, y, PRIMARY KEY(x)) WITHOUT ROWID;
|
||||
CREATE INDEX i1 ON t1(y, x);
|
||||
INSERT INTO t1 VALUES(X'0000', X'1111');
|
||||
}
|
||||
|
||||
do_intck_test 6.1 {}
|
||||
|
||||
do_execsql_test 6.2.1 {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_schema SET sql = 'CREATE INDEX i1' WHERE name='i1';
|
||||
} {}
|
||||
do_intck_test 6.2.2 {}
|
||||
|
||||
do_execsql_test 6.3.1 {
|
||||
UPDATE sqlite_schema SET sql = 'CREATE INDEX i1(y' WHERE name='i1';
|
||||
} {}
|
||||
do_intck_test 6.3.2 {}
|
||||
|
||||
do_execsql_test 6.4.1 {
|
||||
UPDATE sqlite_schema
|
||||
SET sql = 'CREATE INDEX i1(y) hello world'
|
||||
WHERE name='i1';
|
||||
} {}
|
||||
do_intck_test 6.4.2 {}
|
||||
|
||||
do_execsql_test 6.5.1 {
|
||||
UPDATE sqlite_schema
|
||||
SET sql = 'CREATE INDEX i1(y, x) WHERE 1 '
|
||||
WHERE name='i1';
|
||||
} {}
|
||||
do_intck_test 6.5.2 {}
|
||||
|
||||
do_execsql_test 6.6.1 {
|
||||
UPDATE sqlite_schema
|
||||
SET sql = 'CREATE INDEX i1( , ) WHERE 1 '
|
||||
WHERE name='i1';
|
||||
} {}
|
||||
|
||||
do_test 6.7.2 {
|
||||
set ic [sqlite3_intck db main]
|
||||
$ic step
|
||||
} {SQLITE_ERROR}
|
||||
do_test 6.5.3 {
|
||||
$ic error
|
||||
} {SQLITE_ERROR {near "AS": syntax error}}
|
||||
$ic close
|
||||
|
||||
do_execsql_test 6.6.1 {
|
||||
UPDATE sqlite_schema
|
||||
SET sql = 'CREATE INDEX i1([y'
|
||||
WHERE name='i1';
|
||||
} {}
|
||||
do_intck_test 6.6.2 {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE x1("1", "22", "3333", four);
|
||||
CREATE INDEX i1 ON x1( "1" , "22", NULL);
|
||||
INSERT INTO x1 VALUES(1, 22, 3333, NULL);
|
||||
INSERT INTO x1 VALUES(1, 22, 3333, NULL);
|
||||
}
|
||||
do_execsql_test 7.1 " CREATE INDEX i2 ON x1( \"1\"\r\n\t ) "
|
||||
do_execsql_test 7.2 { CREATE INDEX i3 ON x1( "22" || 'abc''def' || `1` ) }
|
||||
do_execsql_test 7.3 { CREATE INDEX i4 ON x1( [22] + [1] ) }
|
||||
do_execsql_test 7.4 { CREATE INDEX i5 ON x1( four||'hello' ) }
|
||||
|
||||
do_intck_test 7.5 {}
|
||||
|
||||
|
||||
finish_test
|
||||
@ -1,177 +0,0 @@
|
||||
# 2024 Feb 19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The focus of this file is testing the incremental integrity check
|
||||
# (intck) extension.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] intck_common.tcl]
|
||||
set testprefix intck2
|
||||
return_if_no_intck
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
|
||||
proc imposter_edit {obj create sql} {
|
||||
sqlite3 xdb test.db
|
||||
set pgno [xdb one {SELECT rootpage FROM sqlite_schema WHERE name=$obj}]
|
||||
|
||||
sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER xdb main 1 $pgno
|
||||
xdb eval $create
|
||||
sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER xdb main 0 0
|
||||
xdb eval $sql
|
||||
xdb close
|
||||
}
|
||||
|
||||
imposter_edit i1 {
|
||||
CREATE TABLE imp(b, a, PRIMARY KEY(b)) WITHOUT ROWID;
|
||||
} {
|
||||
DELETE FROM imp WHERE b='two';
|
||||
INSERT INTO imp(b, a) VALUES('four', 4);
|
||||
}
|
||||
|
||||
do_intck_test 1.1 {
|
||||
{surplus entry ('four',4) in index i1}
|
||||
{entry ('two',2) missing from index i1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE x1(a, b, "c d");
|
||||
CREATE INDEX x1a ON x1(a COLLATE nocase DESC , b ASC);
|
||||
CREATE INDEX x1b ON x1( a || b || ' "''" ' COLLATE binary ASC );
|
||||
CREATE INDEX x1c ON x1( format('%s', a)ASC, format('%d', "c d" ) );
|
||||
INSERT INTO x1 VALUES('one', 2, 3);
|
||||
INSERT INTO x1 VALUES('One', 4, 5);
|
||||
INSERT INTO x1 VALUES('ONE', 6, 7);
|
||||
INSERT INTO x1 VALUES(NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
do_intck_test 2.1 {}
|
||||
|
||||
imposter_edit x1 {
|
||||
CREATE TABLE imp(a, b, c);
|
||||
} {
|
||||
DELETE FROM imp WHERE c=7;
|
||||
}
|
||||
do_intck_test 2.2 {
|
||||
{surplus entry ('ONE',6,3) in index x1a}
|
||||
{surplus entry ('ONE6 "''" ',3) in index x1b}
|
||||
{surplus entry ('ONE','7',3) in index x1c}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
CREATE INDEX x1all ON x1(a DESC, b ASC, c DESC);
|
||||
INSERT INTO x1 VALUES(2, 1, 2);
|
||||
INSERT INTO x1 VALUES(2, 1, 1);
|
||||
INSERT INTO x1 VALUES(2, 2, 2);
|
||||
INSERT INTO x1 VALUES(2, 2, 1);
|
||||
INSERT INTO x1 VALUES(1, 1, 2);
|
||||
INSERT INTO x1 VALUES(1, 1, 1);
|
||||
INSERT INTO x1 VALUES(1, 2, 2);
|
||||
INSERT INTO x1 VALUES(1, 2, 1);
|
||||
}
|
||||
|
||||
do_intck_test 3.1 {
|
||||
}
|
||||
|
||||
imposter_edit x1 {
|
||||
CREATE TABLE imp(a, b, c);
|
||||
} {
|
||||
DELETE FROM imp WHERE 1;
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_intck_test 3.2 {
|
||||
{surplus entry (2,1,2,1) in index x1all}
|
||||
{surplus entry (2,1,1,2) in index x1all}
|
||||
{surplus entry (2,2,2,3) in index x1all}
|
||||
{surplus entry (2,2,1,4) in index x1all}
|
||||
{surplus entry (1,1,2,5) in index x1all}
|
||||
{surplus entry (1,1,1,6) in index x1all}
|
||||
{surplus entry (1,2,2,7) in index x1all}
|
||||
{surplus entry (1,2,1,8) in index x1all}
|
||||
}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
DELETE FROM x1;
|
||||
INSERT INTO x1 VALUES(NULL, NULL, NULL);
|
||||
INSERT INTO x1 VALUES(NULL, NULL, NULL);
|
||||
INSERT INTO x1 VALUES(NULL, NULL, NULL);
|
||||
INSERT INTO x1 VALUES(NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
do_intck_test 3.4 {
|
||||
}
|
||||
|
||||
imposter_edit x1 {
|
||||
CREATE TABLE imp(a, b, c);
|
||||
} {
|
||||
DELETE FROM imp WHERE 1;
|
||||
INSERT INTO imp(rowid) VALUES(-123);
|
||||
INSERT INTO imp(rowid) VALUES(456);
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_intck_test 3.5 {
|
||||
{entry (NULL,NULL,NULL,-123) missing from index x1all}
|
||||
{entry (NULL,NULL,NULL,456) missing from index x1all}
|
||||
{surplus entry (NULL,NULL,NULL,1) in index x1all}
|
||||
{surplus entry (NULL,NULL,NULL,2) in index x1all}
|
||||
{surplus entry (NULL,NULL,NULL,3) in index x1all}
|
||||
{surplus entry (NULL,NULL,NULL,4) in index x1all}
|
||||
}
|
||||
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.6 {
|
||||
CREATE TABLE w1(a PRIMARY KEY, b, c);
|
||||
INSERT INTO w1 VALUES(1.0, NULL, NULL);
|
||||
INSERT INTO w1 VALUES(33.0, NULL, NULL);
|
||||
INSERT INTO w1 VALUES(100.0, NULL, NULL);
|
||||
CREATE INDEX w1bc ON w1(b, c);
|
||||
}
|
||||
|
||||
do_intck_test 3.7 {
|
||||
}
|
||||
|
||||
imposter_edit w1 {
|
||||
CREATE TABLE imp(a, b, c);
|
||||
} {
|
||||
DELETE FROM imp WHERE a=33;
|
||||
INSERT INTO imp(a) VALUES(1234.5);
|
||||
INSERT INTO imp(a) VALUES(-1234.5);
|
||||
}
|
||||
|
||||
do_intck_test 3.8 {
|
||||
{surplus entry (33.0,2) in index sqlite_autoindex_w1_1}
|
||||
{entry (1234.5,4) missing from index sqlite_autoindex_w1_1}
|
||||
{entry (NULL,NULL,4) missing from index w1bc}
|
||||
{entry (-1234.5,5) missing from index sqlite_autoindex_w1_1}
|
||||
{entry (NULL,NULL,5) missing from index w1bc}
|
||||
{surplus entry (NULL,NULL,2) in index w1bc}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@ -1,66 +0,0 @@
|
||||
# 2024 Feb 18
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !vtab||!pragma {
|
||||
proc return_if_no_intck {} {
|
||||
finish_test
|
||||
return -code return
|
||||
}
|
||||
return
|
||||
} else {
|
||||
proc return_if_no_intck {} {}
|
||||
}
|
||||
|
||||
proc do_intck {db {bSuspend 0}} {
|
||||
set ic [sqlite3_intck $db main]
|
||||
|
||||
set ret [list]
|
||||
while {"SQLITE_OK"==[$ic step]} {
|
||||
set msg [$ic message]
|
||||
if {$msg!=""} {
|
||||
lappend ret $msg
|
||||
}
|
||||
if {$bSuspend} {
|
||||
$ic unlock
|
||||
#puts "SQL: [$ic test_sql {}]"
|
||||
#execsql_pp "EXPLAIN query plan [$ic test_sql {}]"
|
||||
#explain_i [$ic test_sql {}]
|
||||
}
|
||||
}
|
||||
|
||||
set err [$ic error]
|
||||
if {[lindex $err 0]!="SQLITE_OK"} {
|
||||
error $err
|
||||
}
|
||||
$ic close
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
proc intck_sql {db tbl} {
|
||||
set ic [sqlite3_intck $db main]
|
||||
set sql [$ic test_sql $tbl]
|
||||
$ic close
|
||||
return $sql
|
||||
}
|
||||
|
||||
proc do_intck_test {tn expect} {
|
||||
uplevel [list do_test $tn.a [list do_intck db] [list {*}$expect]]
|
||||
uplevel [list do_test $tn.b [list do_intck db 1] [list {*}$expect]]
|
||||
}
|
||||
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
# 2024 February 24
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] intck_common.tcl]
|
||||
set testprefix intckbusy
|
||||
return_if_no_intck
|
||||
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(2, 'two', 'three');
|
||||
INSERT INTO t1 VALUES(3, NULL, NULL);
|
||||
CREATE INDEX i1 ON t1(b, c);
|
||||
}
|
||||
|
||||
sqlite3 db2 test.db
|
||||
|
||||
do_execsql_test -db db2 1.1 {
|
||||
BEGIN EXCLUSIVE;
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
}
|
||||
|
||||
do_test 1.2 {
|
||||
set ic [sqlite3_intck db main]
|
||||
$ic step
|
||||
} {SQLITE_BUSY}
|
||||
do_test 1.3 {
|
||||
$ic unlock
|
||||
} {SQLITE_BUSY}
|
||||
do_test 1.4 {
|
||||
$ic error
|
||||
} {SQLITE_BUSY {database is locked}}
|
||||
do_test 1.4 {
|
||||
$ic close
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -1,236 +0,0 @@
|
||||
# 2024 Feb 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The focus of this file is testing the intck extensions response
|
||||
# to corruption at the b-tree level.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] intck_common.tcl]
|
||||
set testprefix intckcorrupt
|
||||
return_if_no_intck
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 1.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 356352 pagesize 4096 filename crash-acaae0347204ae.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 d0 00 00 00 .....@ ........
|
||||
| 32: 40 00 ea 00 00 00 00 00 00 40 00 00 00 40 00 00 @........@...@..
|
||||
| 96: 00 00 00 00 0d 00 00 00 04 0e 9c 00 0f ad 0f 4f ...............O
|
||||
| 112: 0e fc 0e 9c 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3728: 00 00 00 00 00 00 00 00 00 00 00 00 5e 04 07 17 ............^...
|
||||
| 3744: 1f 1f 01 81 0b 74 61 62 6c 65 74 31 5f 70 61 72 .....tablet1_par
|
||||
| 3760: 65 6e 74 74 31 5f 70 61 72 65 6e 74 04 43 52 45 entt1_parent.CRE
|
||||
| 3776: 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f 70 61 ATE TABLE .t1_pa
|
||||
| 3792: 72 65 6e 74 22 28 6e 6f 64 65 6e 6f 20 49 4e 54 rent.(nodeno INT
|
||||
| 3808: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
|
||||
| 3824: 2c 70 61 72 65 6e 74 6e 6f 64 65 29 51 03 06 17 ,parentnode)Q...
|
||||
| 3840: 1b 1b 01 7b 74 61 62 6c 65 74 31 5f 6e 6f 64 65 ....tablet1_node
|
||||
| 3856: 74 31 5f 6e 6f 64 65 03 43 52 45 41 54 45 20 54 t1_node.CREATE T
|
||||
| 3872: 41 42 4c 45 20 22 74 31 5f 6e 6f 64 65 22 28 6e ABLE .t1_node.(n
|
||||
| 3888: 6f 64 65 6e 6f 20 49 4e 54 45 47 45 52 20 50 52 odeno INTEGER PR
|
||||
| 3904: 49 4d 41 52 59 20 4b 45 59 2c 64 61 74 61 29 5c IMARY KEY,data).
|
||||
| 3920: 02 07 17 1d 1d 01 81 0b 74 61 62 6c 65 74 31 5f ........tablet1_
|
||||
| 3936: 72 6f 77 69 64 74 31 5f 72 6f 77 69 64 02 43 52 rowidt1_rowid.CR
|
||||
| 3952: 45 41 54 45 20 54 41 42 4c 45 20 22 74 31 5f 72 EATE TABLE .t1_r
|
||||
| 3968: 6f 77 69 64 22 28 72 6f 77 69 64 20 49 4e 54 45 owid.(rowid INTE
|
||||
| 3984: 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c GER PRIMARY KEY,
|
||||
| 4000: 6e 6f 64 65 6e 6f 2c 61 30 2c 61 31 29 51 01 07 nodeno,a0,a1)Q..
|
||||
| 4016: 17 11 11 08 81 0f 74 61 62 6c 65 74 31 74 31 43 ......tablet1t1C
|
||||
| 4032: 52 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 REATE VIRTUAL TA
|
||||
| 4048: 42 4c 45 20 74 31 20 55 53 49 4e 47 20 72 74 72 BLE t1 USING rtr
|
||||
| 4064: 65 65 28 69 64 2c 78 30 20 50 52 49 4d 41 52 59 ee(id,x0 PRIMARY
|
||||
| 4080: 20 4b 45 59 2c 70 61 72 65 6e 74 6e 6f 64 65 29 KEY,parentnode)
|
||||
| page 2 offset 4096
|
||||
| 0: 51 03 06 17 1b 1b 01 7b 74 61 62 6c 65 74 31 5f Q.......tablet1_
|
||||
| 16: 6e 6f 64 65 74 31 5f 6e 6f 64 65 03 43 52 45 41 nodet1_node.CREA
|
||||
| 32: 54 45 20 54 41 42 4c 45 20 22 74 31 5f 6e 6f 64 TE TABLE .t1_nod
|
||||
| 48: 65 22 28 6e 6f 64 65 6e 6f 20 49 4e 54 45 47 45 e.(nodeno INTEGE
|
||||
| 64: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 64 61 R PRIMARY KEY,da
|
||||
| 80: 74 61 29 5c 02 07 17 1d 1d 01 81 0b 74 61 62 6c ta).........tabl
|
||||
| 96: 65 74 31 5f 72 6f 77 69 64 74 31 5f 72 6f 77 69 et1_rowidt1_rowi
|
||||
| 112: 64 02 43 52 45 41 54 45 20 54 41 42 4c 45 00 00 d.CREATE TABLE..
|
||||
| 128: 01 0a 02 00 00 00 01 0e 0d 00 00 00 00 24 0e 0d .............$..
|
||||
| 144: 0c 1a 06 85 50 46 60 27 70 08 00 00 00 00 00 00 ....PF`'p.......
|
||||
| 3824: 00 00 00 00 00 00 00 0d 0e 05 00 09 1d 00 74 6f ..............to
|
||||
| 3840: 79 20 68 61 6c 66 10 0d 05 00 09 23 00 62 6f 74 y half.....#.bot
|
||||
| 3856: 74 6f 6d 20 68 61 6c 66 0f 0c 05 00 09 21 00 72 tom half.....!.r
|
||||
| 3872: 69 67 68 74 20 68 61 6c 66 0e 0b 05 00 09 1f 00 ight half.......
|
||||
| 3888: 6c 65 66 74 20 43 15 f6 e6 f6 46 50 34 35 24 54 left C....FP45$T
|
||||
| 3904: 15 44 52 05 44 14 24 c4 52 02 27 43 15 f6 e6 f6 .DR.D.$.R.'C....
|
||||
| 3920: 46 52 22 8e 6f 64 65 6e 6f 20 49 4e 54 45 47 45 FR..odeno INTEGE
|
||||
| 3936: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 64 61 R PRIMARY KEY,da
|
||||
| 3952: 74 61 29 5c 02 07 17 1d 1d 01 81 0b 74 61 62 6c ta).........tabl
|
||||
| 3968: 65 74 31 5f 72 6f 74 74 6f 6d 20 65 64 67 65 0f et1_rottom edge.
|
||||
| 3984: 07 05 00 09 21 00 72 69 67 68 74 20 65 64 67 65 ....!.right edge
|
||||
| 4000: 0e 06 05 00 09 1f 00 6c 65 66 74 20 65 64 67 65 .......left edge
|
||||
| 4016: 0b 05 05 00 09 19 00 63 65 6e 74 65 72 17 04 05 .......center...
|
||||
| 4032: 00 09 31 00 75 70 70 65 72 2d 72 69 67 68 74 20 ..1.upper-right
|
||||
| 4048: 63 6f 72 6e 65 72 17 03 05 00 09 31 00 6c 6f 77 corner.....1.low
|
||||
| 4064: 65 72 2d 72 69 67 68 74 20 63 6f 72 6e 65 72 16 er-right corner.
|
||||
| 4080: 02 05 00 09 2f 00 75 70 70 65 72 2d 6c 65 66 74 ..../.upper-left
|
||||
| page 3 offset 8192
|
||||
| 0: 20 63 6f 72 6e 65 72 16 01 05 00 09 2f 01 8c 6f corner...../..o
|
||||
| 16: 77 65 72 2d 6c 53 51 4c 69 74 65 20 66 6f 72 6d wer-lSQLite form
|
||||
| 32: 61 74 20 33 00 10 00 01 01 00 40 20 20 00 00 00 at 3......@ ...
|
||||
| 48: 00 00 00 00 2f 00 00 0d eb 13 00 00 00 03 00 00 ..../...........
|
||||
| 64: 00 04 00 00 00 00 00 00 00 06 00 00 00 01 00 00 ................
|
||||
| 80: 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| page 6 offset 20480
|
||||
| 128: 00 00 00 00 00 00 00 00 97 3d 04 ae 7c 01 00 00 .........=..|...
|
||||
| 624: 00 00 00 00 00 00 21 97 3d 04 ae 7c 01 00 00 00 ......!.=..|....
|
||||
| 1120: 00 00 00 00 00 20 97 3d 04 ae 7c 01 00 00 00 00 ..... .=..|.....
|
||||
| 1616: 00 00 00 00 1f 97 3d 04 ae 7c 01 00 00 00 00 00 ......=..|......
|
||||
| 2112: 00 00 00 1e 97 3d 04 ae 7c 01 00 00 00 00 00 00 .....=..|.......
|
||||
| 2608: 00 00 1d 97 d3 d0 4a e7 c0 00 00 00 00 00 00 00 ......J.........
|
||||
| 3088: 00 00 00 00 00 00 00 00 00 00 00 00 01 f3 00 00 ................
|
||||
| 3600: 23 97 3d 04 ae 7c 01 00 00 00 00 00 00 00 00 00 #.=..|..........
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 ...............&
|
||||
| page 8 offset 28672
|
||||
| 0: 0d 00 00 00 01 04 30 00 04 30 00 00 00 00 00 00 ......0..0......
|
||||
| 1072: 97 4d 1e 14 00 ae 7c 00 00 00 00 00 00 00 00 00 .M....|.........
|
||||
| 1088: 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................
|
||||
| page 10 offset 36864
|
||||
| 0: 0d 00 00 00 01 04 30 00 04 30 00 00 00 00 00 00 ......0..0......
|
||||
| 1072: 9a ee c1 80 fd 78 1f ce 1b ae eb b4 00 00 00 00 .....x..........
|
||||
| 1088: 13 20 ff 20 00 70 00 00 00 60 50 00 00 00 11 e0 . . .p...`P.....
|
||||
| 1104: 00 00 00 70 00 00 00 60 50 05 35 14 c6 97 46 52 ...p...`P.5...FR
|
||||
| 1120: 06 66 f7 26 d6 17 42 03 30 01 00 00 10 10 04 02 .f.&..B.0.......
|
||||
| 1136: 02 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 .........@......
|
||||
| 1152: 00 00 00 00 00 40 00 00 00 40 00 00 00 00 00 00 .....@...@......
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 ................
|
||||
| page 12 offset 45056
|
||||
| 0: 0d 00 00 00 01 04 30 00 04 30 e1 b4 30 97 4d 46 ......0..0..0.MF
|
||||
| 16: 14 00 ae 7c 00 00 00 00 00 00 00 03 00 00 43 00 ...|..........C.
|
||||
| page 47 offset 188416
|
||||
| 2512: 00 00 00 00 00 00 00 00 be 00 00 00 00 00 00 00 ................
|
||||
| page 87 offset 352256
|
||||
| 2512: 00 00 00 00 00 00 00 00 aa 00 00 00 00 00 00 00 ................
|
||||
| end crash-acaae0347204ae.db
|
||||
}]} {}
|
||||
|
||||
do_intck_test 1.1 {
|
||||
{corruption found while reading database schema}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 2.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 28672 pagesize 4096 filename crash-3afa1ca9e9c1bd.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 07 .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................
|
||||
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
|
||||
| 96: 00 00 00 00 0d 00 00 00 06 0e 88 00 0f b8 0f 6d ...............m
|
||||
| 112: 0f 3a 0f 0b 0e d5 0e 88 01 00 00 00 00 00 00 00 .:..............
|
||||
| 3712: 00 00 00 00 00 00 00 00 4b 06 06 17 25 25 01 5b ........K...%%.[
|
||||
| 3728: 74 61 62 6c 65 73 71 6c 69 74 65 5f 73 74 61 74 tablesqlite_stat
|
||||
| 3744: 31 73 71 6c 69 74 65 5f 73 74 61 74 31 07 43 52 1sqlite_stat1.CR
|
||||
| 3760: 45 41 54 45 20 54 41 42 4c 45 20 73 71 6c 69 74 EATE TABLE sqlit
|
||||
| 3776: 65 5f 73 74 61 74 31 28 74 62 6c 2c 69 64 78 2c e_stat1(tbl,idx,
|
||||
| 3792: 73 74 61 74 29 34 05 06 17 13 11 01 53 69 6e 64 stat)4......Sind
|
||||
| 3808: 65 78 63 31 63 63 31 06 43 52 45 41 54 45 20 55 exc1cc1.CREATE U
|
||||
| 3824: 4e 49 51 55 45 20 49 4e 44 45 58 20 63 31 63 20 NIQUE INDEX c1c
|
||||
| 3840: 4f 4e 20 63 31 28 63 2c 20 62 29 2d 04 06 17 13 ON c1(c, b)-....
|
||||
| 3856: 11 01 45 69 6e 64 65 78 63 31 64 63 31 05 43 52 ..Eindexc1dc1.CR
|
||||
| 3872: 45 41 54 45 20 49 4e 44 45 58 20 63 31 64 20 4f EATE INDEX c1d O
|
||||
| 3888: 4e 20 63 31 28 64 2c 20 62 29 31 03 06 17 13 11 N c1(d, b)1.....
|
||||
| 3904: 01 4d 69 6e 64 65 78 62 31 63 62 31 05 43 52 45 .Mindexb1cb1.CRE
|
||||
| 3920: 41 54 45 20 55 4e 49 51 55 45 20 49 4e 44 45 58 ATE UNIQUE INDEX
|
||||
| 3936: 20 62 31 63 20 4f 4e 20 62 31 28 63 29 49 02 06 b1c ON b1(c)I..
|
||||
| 3952: 17 11 11 0f 7f 74 61 62 6c 65 63 31 63 31 03 43 .....tablec1c1.C
|
||||
| 3968: 52 45 41 54 45 20 54 41 42 4c 45 20 63 31 28 61 REATE TABLE c1(a
|
||||
| 3984: 20 49 4e 54 20 50 52 49 4d 41 52 59 20 4b 45 59 INT PRIMARY KEY
|
||||
| 4000: 2c 20 62 2c 20 63 2c 20 64 29 20 57 49 54 48 4f , b, c, d) WITHO
|
||||
| 4016: 55 54 20 52 4f 57 49 44 46 01 06 17 11 11 01 79 UT ROWIDF......y
|
||||
| 4032: 74 61 62 6c 65 62 31 62 31 02 43 52 45 41 54 45 tableb1b1.CREATE
|
||||
| 4048: 20 54 41 42 4c 45 20 62 31 28 61 20 49 4e 54 20 TABLE b1(a INT
|
||||
| 4064: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 2c 20 PRIMARY KEY, b,
|
||||
| 4080: 63 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 c) WITHOUT ROWID
|
||||
| page 2 offset 4096
|
||||
| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f e2 ................
|
||||
| 16: 0f da 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 06 ................
|
||||
| 4048: 67 07 07 04 01 0f 01 06 66 06 07 04 01 0f 01 05 g.......f.......
|
||||
| 4064: 65 05 07 04 01 0f 01 04 64 04 07 04 01 0f 01 03 e.......d.......
|
||||
| 4080: 63 03 07 04 01 0f 01 02 62 0f 05 04 09 0f 09 61 c.......b......a
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 07 0f bd 00 0f f9 0f ef 0f e5 0f db ................
|
||||
| 16: 0f d1 0f c7 0f bd 00 00 00 00 01 00 00 00 00 00 ................
|
||||
| 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 09 05 01 ................
|
||||
| 4032: 0f 01 01 07 61 07 07 09 05 01 0f 01 01 06 61 06 ....a.........a.
|
||||
| 4048: 06 09 05 01 0f 01 01 05 61 05 05 09 05 01 0f 01 ........a.......
|
||||
| 4064: 01 04 61 04 04 09 05 01 0f 01 01 03 61 03 03 09 ..a.........a...
|
||||
| 4080: 05 01 0f 01 01 02 61 0f 02 06 05 09 0f 09 09 61 ......a........a
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 07 0f d8 00 0f fc 0f f0 0f ea 0f e4 ................
|
||||
| 16: 0f de 0f d8 0f f6 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4048: 00 00 00 00 00 00 00 00 05 03 01 01 07 07 05 03 ................
|
||||
| 4064: 01 01 06 06 05 03 01 01 05 05 05 03 01 01 04 04 ................
|
||||
| 4080: 05 03 01 01 03 03 05 03 01 01 0f 02 03 03 09 09 ................
|
||||
| page 5 offset 16384
|
||||
| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f 00 ................
|
||||
| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................
|
||||
| 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a.......
|
||||
| 4064: 61 05 07 04 01 1f 01 04 61 04 07 04 01 0f 01 03 a.......a.......
|
||||
| 4080: 61 03 07 04 01 0f 01 02 61 02 05 04 09 0f 09 61 a.......a......a
|
||||
| page 6 offset 20480
|
||||
| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f ea 0f e2 00 00 ................
|
||||
| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................
|
||||
| 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a.......
|
||||
| 4064: 61 05 07 04 01 0f 01 04 61 04 07 04 01 0f 01 03 a.......a.......
|
||||
| 4080: 61 03 07 04 01 0f 01 0f 61 02 05 04 09 0f 09 61 a.......a......a
|
||||
| page 7 offset 24576
|
||||
| 0: 0d 00 00 00 05 0f 1c 00 0f f0 0f e0 0f d3 0f c5 ................
|
||||
| 16: 0f b8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4016: 00 00 00 00 00 00 00 00 0b 05 04 11 11 13 62 31 ..............b1
|
||||
| 4032: 62 31 37 20 31 0c 04 04 11 13 13 62 31 62 31 63 b17 1......b1b1c
|
||||
| 4048: 37 20 31 0b 03 04 11 11 13 63 31 63 31 37 20 31 7 1......c1c17 1
|
||||
| 4064: 0e 02 04 11 13 07 63 31 63 31 64 37 20 31 20 31 ......c1c1d7 1 1
|
||||
| 4080: 0e 01 04 11 13 17 63 31 63 31 63 37 20 31 00 00 ......c1c1c7 1..
|
||||
| end crash-3afa1ca9e9c1bd.db
|
||||
}]} {}
|
||||
|
||||
do_intck_test 2.1 {
|
||||
{corruption found while reading database schema}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 3);
|
||||
}
|
||||
|
||||
do_test 3.1 {
|
||||
set pgno [db one {SELECT rootpage FROM sqlite_schema WHERE name='t1'}]
|
||||
db close
|
||||
hexio_write test.db [expr ($pgno-1)*1024] 0000
|
||||
} {2}
|
||||
|
||||
sqlite3 db test.db
|
||||
do_intck_test 3.2 {
|
||||
{corruption found while scanning database object i1}
|
||||
{corruption found while scanning database object t1}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
# 2024 February 24
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] intck_common.tcl]
|
||||
set testprefix intckfault
|
||||
return_if_no_intck
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(2, 'two', 'three');
|
||||
INSERT INTO t1 VALUES(3, NULL, NULL);
|
||||
CREATE INDEX i1 ON t1(b, c);
|
||||
}
|
||||
|
||||
do_faultsim_test 1 -faults oom-t* -prep {
|
||||
} -body {
|
||||
set ::ic [sqlite3_intck db main]
|
||||
set nStep 0
|
||||
while {"SQLITE_OK"==[$::ic step]} {
|
||||
incr nStep
|
||||
if {$nStep==3} { $::ic unlock }
|
||||
}
|
||||
set res [$::ic error]
|
||||
$::ic close
|
||||
set res
|
||||
} -test {
|
||||
catch { $::ic close }
|
||||
faultsim_test_result {0 {SQLITE_OK {}}} {0 {SQLITE_NOMEM {}}} {0 {SQLITE_NOMEM {out of memory}}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -1,940 +0,0 @@
|
||||
/*
|
||||
** 2024-02-08
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
#include "sqlite3intck.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
** nKeyVal:
|
||||
** The number of values that make up the 'key' for the current pCheck
|
||||
** statement.
|
||||
**
|
||||
** rc:
|
||||
** Error code returned by most recent sqlite3_intck_step() or
|
||||
** sqlite3_intck_unlock() call. This is set to SQLITE_DONE when
|
||||
** the integrity-check operation is finished.
|
||||
**
|
||||
** zErr:
|
||||
** If the object has entered the error state, this is the error message.
|
||||
** Is freed using sqlite3_free() when the object is deleted.
|
||||
**
|
||||
** zTestSql:
|
||||
** The value returned by the most recent call to sqlite3_intck_testsql().
|
||||
** Each call to testsql() frees the previous zTestSql value (using
|
||||
** sqlite3_free()) and replaces it with the new value it will return.
|
||||
*/
|
||||
struct sqlite3_intck {
|
||||
sqlite3 *db;
|
||||
const char *zDb; /* Copy of zDb parameter to _open() */
|
||||
char *zObj; /* Current object. Or NULL. */
|
||||
|
||||
sqlite3_stmt *pCheck; /* Current check statement */
|
||||
char *zKey;
|
||||
int nKeyVal;
|
||||
|
||||
char *zMessage;
|
||||
int bCorruptSchema;
|
||||
|
||||
int rc; /* Error code */
|
||||
char *zErr; /* Error message */
|
||||
char *zTestSql; /* Returned by sqlite3_intck_test_sql() */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Some error has occurred while using database p->db. Save the error message
|
||||
** and error code currently held by the database handle in p->rc and p->zErr.
|
||||
*/
|
||||
static void intckSaveErrmsg(sqlite3_intck *p){
|
||||
p->rc = sqlite3_errcode(p->db);
|
||||
sqlite3_free(p->zErr);
|
||||
p->zErr = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
|
||||
}
|
||||
|
||||
/*
|
||||
** If the handle passed as the first argument is already in the error state,
|
||||
** then this function is a no-op (returns NULL immediately). Otherwise, if an
|
||||
** error occurs within this function, it leaves an error in said handle.
|
||||
**
|
||||
** Otherwise, this function attempts to prepare SQL statement zSql and
|
||||
** return the resulting statement handle to the user.
|
||||
*/
|
||||
static sqlite3_stmt *intckPrepare(sqlite3_intck *p, const char *zSql){
|
||||
sqlite3_stmt *pRet = 0;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_prepare_v2(p->db, zSql, -1, &pRet, 0);
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
intckSaveErrmsg(p);
|
||||
assert( pRet==0 );
|
||||
}
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the handle passed as the first argument is already in the error state,
|
||||
** then this function is a no-op (returns NULL immediately). Otherwise, if an
|
||||
** error occurs within this function, it leaves an error in said handle.
|
||||
**
|
||||
** Otherwise, this function treats argument zFmt as a printf() style format
|
||||
** string. It formats it according to the trailing arguments and then
|
||||
** attempts to prepare the results and return the resulting prepared
|
||||
** statement.
|
||||
*/
|
||||
static sqlite3_stmt *intckPrepareFmt(sqlite3_intck *p, const char *zFmt, ...){
|
||||
sqlite3_stmt *pRet = 0;
|
||||
va_list ap;
|
||||
char *zSql = 0;
|
||||
va_start(ap, zFmt);
|
||||
zSql = sqlite3_vmprintf(zFmt, ap);
|
||||
if( p->rc==SQLITE_OK && zSql==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
pRet = intckPrepare(p, zSql);
|
||||
sqlite3_free(zSql);
|
||||
va_end(ap);
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Finalize SQL statement pStmt. If an error occurs and the handle passed
|
||||
** as the first argument does not already contain an error, store the
|
||||
** error in the handle.
|
||||
*/
|
||||
static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){
|
||||
int rc = sqlite3_finalize(pStmt);
|
||||
if( p->rc==SQLITE_OK && rc!=SQLITE_OK ){
|
||||
intckSaveErrmsg(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If there is already an error in handle p, return it. Otherwise, call
|
||||
** sqlite3_step() on the statement handle and return that value.
|
||||
*/
|
||||
static int intckStep(sqlite3_intck *p, sqlite3_stmt *pStmt){
|
||||
if( p->rc ) return p->rc;
|
||||
return sqlite3_step(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Execute SQL statement zSql. There is no way to obtain any results
|
||||
** returned by the statement. This function uses the sqlite3_intck error
|
||||
** code convention.
|
||||
*/
|
||||
static void intckExec(sqlite3_intck *p, const char *zSql){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
pStmt = intckPrepare(p, zSql);
|
||||
intckStep(p, pStmt);
|
||||
intckFinalize(p, pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** A wrapper around sqlite3_mprintf() that uses the sqlite3_intck error
|
||||
** code convention.
|
||||
*/
|
||||
static char *intckMprintf(sqlite3_intck *p, const char *zFmt, ...){
|
||||
va_list ap;
|
||||
char *zRet = 0;
|
||||
va_start(ap, zFmt);
|
||||
zRet = sqlite3_vmprintf(zFmt, ap);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( zRet==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
}else{
|
||||
sqlite3_free(zRet);
|
||||
zRet = 0;
|
||||
}
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is used by sqlite3_intck_unlock() to save the vector key value
|
||||
** required to restart the current pCheck query as a nul-terminated string
|
||||
** in p->zKey.
|
||||
*/
|
||||
static void intckSaveKey(sqlite3_intck *p){
|
||||
int ii;
|
||||
char *zSql = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
sqlite3_stmt *pXinfo = 0;
|
||||
const char *zDir = 0;
|
||||
|
||||
assert( p->pCheck );
|
||||
assert( p->zKey==0 );
|
||||
|
||||
pXinfo = intckPrepareFmt(p,
|
||||
"SELECT group_concat(desc, '') FROM %Q.sqlite_schema s, "
|
||||
"pragma_index_xinfo(%Q, %Q) "
|
||||
"WHERE s.type='index' AND s.name=%Q",
|
||||
p->zDb, p->zObj, p->zDb, p->zObj
|
||||
);
|
||||
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXinfo) ){
|
||||
zDir = (const char*)sqlite3_column_text(pXinfo, 0);
|
||||
}
|
||||
|
||||
if( zDir==0 ){
|
||||
/* Object is a table, not an index. This is the easy case,as there are
|
||||
** no DESC columns or NULL values in a primary key. */
|
||||
const char *zSep = "SELECT '(' || ";
|
||||
for(ii=0; ii<p->nKeyVal; ii++){
|
||||
zSql = intckMprintf(p, "%z%squote(?)", zSql, zSep);
|
||||
zSep = " || ', ' || ";
|
||||
}
|
||||
zSql = intckMprintf(p, "%z || ')'", zSql);
|
||||
}else{
|
||||
|
||||
/* Object is an index. */
|
||||
assert( p->nKeyVal>1 );
|
||||
for(ii=p->nKeyVal; ii>0; ii--){
|
||||
int bLastIsDesc = zDir[ii-1]=='1';
|
||||
int bLastIsNull = sqlite3_column_type(p->pCheck, ii)==SQLITE_NULL;
|
||||
const char *zLast = sqlite3_column_name(p->pCheck, ii);
|
||||
char *zLhs = 0;
|
||||
char *zRhs = 0;
|
||||
char *zWhere = 0;
|
||||
|
||||
if( bLastIsNull ){
|
||||
if( bLastIsDesc ) continue;
|
||||
zWhere = intckMprintf(p, "'%s IS NOT NULL'", zLast);
|
||||
}else{
|
||||
const char *zOp = bLastIsDesc ? "<" : ">";
|
||||
zWhere = intckMprintf(p, "'%s %s ' || quote(?%d)", zLast, zOp, ii);
|
||||
}
|
||||
|
||||
if( ii>1 ){
|
||||
const char *zLhsSep = "";
|
||||
const char *zRhsSep = "";
|
||||
int jj;
|
||||
for(jj=0; jj<ii-1; jj++){
|
||||
const char *zAlias = (const char*)sqlite3_column_name(p->pCheck,jj+1);
|
||||
zLhs = intckMprintf(p, "%z%s%s", zLhs, zLhsSep, zAlias);
|
||||
zRhs = intckMprintf(p, "%z%squote(?%d)", zRhs, zRhsSep, jj+1);
|
||||
zLhsSep = ",";
|
||||
zRhsSep = " || ',' || ";
|
||||
}
|
||||
|
||||
zWhere = intckMprintf(p,
|
||||
"'(%z) IS (' || %z || ') AND ' || %z",
|
||||
zLhs, zRhs, zWhere);
|
||||
}
|
||||
zWhere = intckMprintf(p, "'WHERE ' || %z", zWhere);
|
||||
|
||||
zSql = intckMprintf(p, "%z%s(quote( %z ) )",
|
||||
zSql,
|
||||
(zSql==0 ? "VALUES" : ",\n "),
|
||||
zWhere
|
||||
);
|
||||
}
|
||||
zSql = intckMprintf(p,
|
||||
"WITH wc(q) AS (\n%z\n)"
|
||||
"SELECT 'VALUES' || group_concat('(' || q || ')', ',\n ') FROM wc"
|
||||
, zSql
|
||||
);
|
||||
}
|
||||
|
||||
pStmt = intckPrepare(p, zSql);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
for(ii=0; ii<p->nKeyVal; ii++){
|
||||
sqlite3_bind_value(pStmt, ii+1, sqlite3_column_value(p->pCheck, ii+1));
|
||||
}
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
p->zKey = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
intckFinalize(p, pStmt);
|
||||
}
|
||||
|
||||
sqlite3_free(zSql);
|
||||
intckFinalize(p, pXinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the next database object (table or index) to check. If successful,
|
||||
** set sqlite3_intck.zObj to point to a nul-terminated buffer containing
|
||||
** the object's name before returning.
|
||||
*/
|
||||
static void intckFindObject(sqlite3_intck *p){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
char *zPrev = p->zObj;
|
||||
p->zObj = 0;
|
||||
|
||||
assert( p->rc==SQLITE_OK );
|
||||
assert( p->pCheck==0 );
|
||||
|
||||
pStmt = intckPrepareFmt(p,
|
||||
"WITH tables(table_name) AS ("
|
||||
" SELECT name"
|
||||
" FROM %Q.sqlite_schema WHERE (type='table' OR type='index') AND rootpage"
|
||||
" UNION ALL "
|
||||
" SELECT 'sqlite_schema'"
|
||||
")"
|
||||
"SELECT table_name FROM tables "
|
||||
"WHERE ?1 IS NULL OR table_name%s?1 "
|
||||
"ORDER BY 1"
|
||||
, p->zDb, (p->zKey ? ">=" : ">")
|
||||
);
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pStmt, 1, zPrev, -1, SQLITE_TRANSIENT);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
p->zObj = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
}
|
||||
intckFinalize(p, pStmt);
|
||||
|
||||
/* If this is a new object, ensure the previous key value is cleared. */
|
||||
if( sqlite3_stricmp(p->zObj, zPrev) ){
|
||||
sqlite3_free(p->zKey);
|
||||
p->zKey = 0;
|
||||
}
|
||||
|
||||
sqlite3_free(zPrev);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size in bytes of the first token in nul-terminated buffer z.
|
||||
** For the purposes of this call, a token is either:
|
||||
**
|
||||
** * a quoted SQL string,
|
||||
* * a contiguous series of ascii alphabet characters, or
|
||||
* * any other single byte.
|
||||
*/
|
||||
static int intckGetToken(const char *z){
|
||||
char c = z[0];
|
||||
int iRet = 1;
|
||||
if( c=='\'' || c=='"' || c=='`' ){
|
||||
while( 1 ){
|
||||
if( z[iRet]==c ){
|
||||
iRet++;
|
||||
if( z[iRet]!=c ) break;
|
||||
}
|
||||
iRet++;
|
||||
}
|
||||
}
|
||||
else if( c=='[' ){
|
||||
while( z[iRet++]!=']' && z[iRet] );
|
||||
}
|
||||
else if( (c>='A' && c<='Z') || (c>='a' && c<='z') ){
|
||||
while( (z[iRet]>='A' && z[iRet]<='Z') || (z[iRet]>='a' && z[iRet]<='z') ){
|
||||
iRet++;
|
||||
}
|
||||
}
|
||||
|
||||
return iRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if argument c is an ascii whitespace character.
|
||||
*/
|
||||
static int intckIsSpace(char c){
|
||||
return (c==' ' || c=='\t' || c=='\n' || c=='\r');
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument z points to the text of a CREATE INDEX statement. This function
|
||||
** identifies the part of the text that contains either the index WHERE
|
||||
** clause (if iCol<0) or the iCol'th column of the index.
|
||||
**
|
||||
** If (iCol<0), the identified fragment does not include the "WHERE" keyword,
|
||||
** only the expression that follows it. If (iCol>=0) then the identified
|
||||
** fragment does not include any trailing sort-order keywords - "ASC" or
|
||||
** "DESC".
|
||||
**
|
||||
** If the CREATE INDEX statement does not contain the requested field or
|
||||
** clause, NULL is returned and (*pnByte) is set to 0. Otherwise, a pointer to
|
||||
** the identified fragment is returned and output parameter (*pnByte) set
|
||||
** to its size in bytes.
|
||||
*/
|
||||
static const char *intckParseCreateIndex(const char *z, int iCol, int *pnByte){
|
||||
int iOff = 0;
|
||||
int iThisCol = 0;
|
||||
int iStart = 0;
|
||||
int nOpen = 0;
|
||||
|
||||
const char *zRet = 0;
|
||||
int nRet = 0;
|
||||
|
||||
int iEndOfCol = 0;
|
||||
|
||||
/* Skip forward until the first "(" token */
|
||||
while( z[iOff]!='(' ){
|
||||
iOff += intckGetToken(&z[iOff]);
|
||||
if( z[iOff]=='\0' ) return 0;
|
||||
}
|
||||
assert( z[iOff]=='(' );
|
||||
|
||||
nOpen = 1;
|
||||
iOff++;
|
||||
iStart = iOff;
|
||||
while( z[iOff] ){
|
||||
const char *zToken = &z[iOff];
|
||||
int nToken = 0;
|
||||
|
||||
/* Check if this is the end of the current column - either a "," or ")"
|
||||
** when nOpen==1. */
|
||||
if( nOpen==1 ){
|
||||
if( z[iOff]==',' || z[iOff]==')' ){
|
||||
if( iCol==iThisCol ){
|
||||
int iEnd = iEndOfCol ? iEndOfCol : iOff;
|
||||
nRet = (iEnd - iStart);
|
||||
zRet = &z[iStart];
|
||||
break;
|
||||
}
|
||||
iStart = iOff+1;
|
||||
while( intckIsSpace(z[iStart]) ) iStart++;
|
||||
iThisCol++;
|
||||
}
|
||||
if( z[iOff]==')' ) break;
|
||||
}
|
||||
if( z[iOff]=='(' ) nOpen++;
|
||||
if( z[iOff]==')' ) nOpen--;
|
||||
nToken = intckGetToken(zToken);
|
||||
|
||||
if( (nToken==3 && 0==sqlite3_strnicmp(zToken, "ASC", nToken))
|
||||
|| (nToken==4 && 0==sqlite3_strnicmp(zToken, "DESC", nToken))
|
||||
){
|
||||
iEndOfCol = iOff;
|
||||
}else if( 0==intckIsSpace(zToken[0]) ){
|
||||
iEndOfCol = 0;
|
||||
}
|
||||
|
||||
iOff += nToken;
|
||||
}
|
||||
|
||||
/* iStart is now the byte offset of 1 byte passed the final ')' in the
|
||||
** CREATE INDEX statement. Try to find a WHERE clause to return. */
|
||||
while( zRet==0 && z[iOff] ){
|
||||
int n = intckGetToken(&z[iOff]);
|
||||
if( n==5 && 0==sqlite3_strnicmp(&z[iOff], "where", 5) ){
|
||||
zRet = &z[iOff+5];
|
||||
nRet = (int)strlen(zRet);
|
||||
}
|
||||
iOff += n;
|
||||
}
|
||||
|
||||
/* Trim any whitespace from the start and end of the returned string. */
|
||||
if( zRet ){
|
||||
while( intckIsSpace(zRet[0]) ){
|
||||
nRet--;
|
||||
zRet++;
|
||||
}
|
||||
while( nRet>0 && intckIsSpace(zRet[nRet-1]) ) nRet--;
|
||||
}
|
||||
|
||||
*pnByte = nRet;
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** User-defined SQL function wrapper for intckParseCreateIndex():
|
||||
**
|
||||
** SELECT parse_create_index(<sql>, <icol>);
|
||||
*/
|
||||
static void intckParseCreateIndexFunc(
|
||||
sqlite3_context *pCtx,
|
||||
int nVal,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
const char *zSql = (const char*)sqlite3_value_text(apVal[0]);
|
||||
int idx = sqlite3_value_int(apVal[1]);
|
||||
const char *zRes = 0;
|
||||
int nRes = 0;
|
||||
|
||||
assert( nVal==2 );
|
||||
if( zSql ){
|
||||
zRes = intckParseCreateIndex(zSql, idx, &nRes);
|
||||
}
|
||||
sqlite3_result_text(pCtx, zRes, nRes, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if sqlite3_intck.db has automatic indexes enabled, false
|
||||
** otherwise.
|
||||
*/
|
||||
static int intckGetAutoIndex(sqlite3_intck *p){
|
||||
int bRet = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
pStmt = intckPrepare(p, "PRAGMA automatic_index");
|
||||
if( SQLITE_ROW==intckStep(p, pStmt) ){
|
||||
bRet = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
intckFinalize(p, pStmt);
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if zObj is an index, or false otherwise.
|
||||
*/
|
||||
static int intckIsIndex(sqlite3_intck *p, const char *zObj){
|
||||
int bRet = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
pStmt = intckPrepareFmt(p,
|
||||
"SELECT 1 FROM %Q.sqlite_schema WHERE name=%Q AND type='index'",
|
||||
p->zDb, zObj
|
||||
);
|
||||
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
bRet = 1;
|
||||
}
|
||||
intckFinalize(p, pStmt);
|
||||
return bRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to a nul-terminated buffer containing the SQL statement
|
||||
** used to check database object zObj (a table or index) for corruption.
|
||||
** If parameter zPrev is not NULL, then it must be a string containing the
|
||||
** vector key required to restart the check where it left off last time.
|
||||
** If pnKeyVal is not NULL, then (*pnKeyVal) is set to the number of
|
||||
** columns in the vector key value for the specified object.
|
||||
**
|
||||
** This function uses the sqlite3_intck error code convention.
|
||||
*/
|
||||
static char *intckCheckObjectSql(
|
||||
sqlite3_intck *p, /* Integrity check object */
|
||||
const char *zObj, /* Object (table or index) to scan */
|
||||
const char *zPrev, /* Restart key vector, if any */
|
||||
int *pnKeyVal /* OUT: Number of key-values for this scan */
|
||||
){
|
||||
char *zRet = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int bAutoIndex = 0;
|
||||
int bIsIndex = 0;
|
||||
|
||||
const char *zCommon =
|
||||
/* Relation without_rowid also contains just one row. Column "b" is
|
||||
** set to true if the table being examined is a WITHOUT ROWID table,
|
||||
** or false otherwise. */
|
||||
", without_rowid(b) AS ("
|
||||
" SELECT EXISTS ("
|
||||
" SELECT 1 FROM tabname, pragma_index_list(tab, db) AS l"
|
||||
" WHERE origin='pk' "
|
||||
" AND NOT EXISTS (SELECT 1 FROM sqlite_schema WHERE name=l.name)"
|
||||
" )"
|
||||
")"
|
||||
""
|
||||
/* Table idx_cols contains 1 row for each column in each index on the
|
||||
** table being checked. Columns are:
|
||||
**
|
||||
** idx_name: Name of the index.
|
||||
** idx_ispk: True if this index is the PK of a WITHOUT ROWID table.
|
||||
** col_name: Name of indexed column, or NULL for index on expression.
|
||||
** col_expr: Indexed expression, including COLLATE clause.
|
||||
** col_alias: Alias used for column in 'intck_wrapper' table.
|
||||
*/
|
||||
", idx_cols(idx_name, idx_ispk, col_name, col_expr, col_alias) AS ("
|
||||
" SELECT l.name, (l.origin=='pk' AND w.b), i.name, COALESCE(("
|
||||
" SELECT parse_create_index(sql, i.seqno) FROM "
|
||||
" sqlite_schema WHERE name = l.name"
|
||||
" ), format('\"%w\"', i.name) || ' COLLATE ' || quote(i.coll)),"
|
||||
" 'c' || row_number() OVER ()"
|
||||
" FROM "
|
||||
" tabname t,"
|
||||
" without_rowid w,"
|
||||
" pragma_index_list(t.tab, t.db) l,"
|
||||
" pragma_index_xinfo(l.name) i"
|
||||
" WHERE i.key"
|
||||
" UNION ALL"
|
||||
" SELECT '', 1, '_rowid_', '_rowid_', 'r1' FROM without_rowid WHERE b=0"
|
||||
")"
|
||||
""
|
||||
""
|
||||
/*
|
||||
** For a PK declared as "PRIMARY KEY(a, b) ... WITHOUT ROWID", where
|
||||
** the intck_wrapper aliases of "a" and "b" are "c1" and "c2":
|
||||
**
|
||||
** o_pk: "o.c1, o.c2"
|
||||
** i_pk: "i.'a', i.'b'"
|
||||
** ...
|
||||
** n_pk: 2
|
||||
*/
|
||||
", tabpk(db, tab, idx, o_pk, i_pk, q_pk, eq_pk, ps_pk, pk_pk, n_pk) AS ("
|
||||
" WITH pkfields(f, a) AS ("
|
||||
" SELECT i.col_name, i.col_alias FROM idx_cols i WHERE i.idx_ispk"
|
||||
" )"
|
||||
" SELECT t.db, t.tab, t.idx, "
|
||||
" group_concat(a, ', '), "
|
||||
" group_concat('i.'||quote(f), ', '), "
|
||||
" group_concat('quote(o.'||a||')', ' || '','' || '), "
|
||||
" format('(%s)==(%s)',"
|
||||
" group_concat('o.'||a, ', '), "
|
||||
" group_concat(format('\"%w\"', f), ', ')"
|
||||
" ),"
|
||||
" group_concat('%s', ','),"
|
||||
" group_concat('quote('||a||')', ', '), "
|
||||
" count(*)"
|
||||
" FROM tabname t, pkfields"
|
||||
")"
|
||||
""
|
||||
", idx(name, match_expr, partial, partial_alias, idx_ps, idx_idx) AS ("
|
||||
" SELECT idx_name,"
|
||||
" format('(%s,%s) IS (%s,%s)', "
|
||||
" group_concat(i.col_expr, ', '), i_pk,"
|
||||
" group_concat('o.'||i.col_alias, ', '), o_pk"
|
||||
" ), "
|
||||
" parse_create_index("
|
||||
" (SELECT sql FROM sqlite_schema WHERE name=idx_name), -1"
|
||||
" ),"
|
||||
" 'cond' || row_number() OVER ()"
|
||||
" , group_concat('%s', ',')"
|
||||
" , group_concat('quote('||i.col_alias||')', ', ')"
|
||||
" FROM tabpk t, "
|
||||
" without_rowid w,"
|
||||
" idx_cols i"
|
||||
" WHERE i.idx_ispk==0 "
|
||||
" GROUP BY idx_name"
|
||||
")"
|
||||
""
|
||||
", wrapper_with(s) AS ("
|
||||
" SELECT 'intck_wrapper AS (\n SELECT\n ' || ("
|
||||
" WITH f(a, b) AS ("
|
||||
" SELECT col_expr, col_alias FROM idx_cols"
|
||||
" UNION ALL "
|
||||
" SELECT partial, partial_alias FROM idx WHERE partial IS NOT NULL"
|
||||
" )"
|
||||
" SELECT group_concat(format('%s AS %s', a, b), ',\n ') FROM f"
|
||||
" )"
|
||||
" || format('\n FROM %Q.%Q ', t.db, t.tab)"
|
||||
/* If the object being checked is a table, append "NOT INDEXED".
|
||||
** Otherwise, append "INDEXED BY <index>", and then, if the index
|
||||
** is a partial index " WHERE <condition>". */
|
||||
" || CASE WHEN t.idx IS NULL THEN "
|
||||
" 'NOT INDEXED'"
|
||||
" ELSE"
|
||||
" format('INDEXED BY %Q%s', t.idx, ' WHERE '||i.partial)"
|
||||
" END"
|
||||
" || '\n)'"
|
||||
" FROM tabname t LEFT JOIN idx i ON (i.name=t.idx)"
|
||||
")"
|
||||
""
|
||||
;
|
||||
|
||||
bAutoIndex = intckGetAutoIndex(p);
|
||||
if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 0");
|
||||
|
||||
bIsIndex = intckIsIndex(p, zObj);
|
||||
if( bIsIndex ){
|
||||
pStmt = intckPrepareFmt(p,
|
||||
/* Table idxname contains a single row. The first column, "db", contains
|
||||
** the name of the db containing the table (e.g. "main") and the second,
|
||||
** "tab", the name of the table itself. */
|
||||
"WITH tabname(db, tab, idx) AS ("
|
||||
" SELECT %Q, (SELECT tbl_name FROM %Q.sqlite_schema WHERE name=%Q), %Q "
|
||||
")"
|
||||
""
|
||||
", whereclause(w_c) AS (%s)"
|
||||
""
|
||||
"%s" /* zCommon */
|
||||
""
|
||||
", case_statement(c) AS ("
|
||||
" SELECT "
|
||||
" 'CASE WHEN (' || group_concat(col_alias, ', ') || ', 1) IS (\n' "
|
||||
" || ' SELECT ' || group_concat(col_expr, ', ') || ', 1 FROM '"
|
||||
" || format('%%Q.%%Q NOT INDEXED WHERE %%s\n', t.db, t.tab, p.eq_pk)"
|
||||
" || ' )\n THEN NULL\n '"
|
||||
" || 'ELSE format(''surplus entry ('"
|
||||
" || group_concat('%%s', ',') || ',' || p.ps_pk"
|
||||
" || ') in index ' || t.idx || ''', ' "
|
||||
" || group_concat('quote('||i.col_alias||')', ', ') || ', ' || p.pk_pk"
|
||||
" || ')'"
|
||||
" || '\n END AS error_message'"
|
||||
" FROM tabname t, tabpk p, idx_cols i WHERE i.idx_name=t.idx"
|
||||
")"
|
||||
""
|
||||
", thiskey(k, n) AS ("
|
||||
" SELECT group_concat(i.col_alias, ', ') || ', ' || p.o_pk, "
|
||||
" count(*) + p.n_pk "
|
||||
" FROM tabpk p, idx_cols i WHERE i.idx_name=p.idx"
|
||||
")"
|
||||
""
|
||||
", main_select(m, n) AS ("
|
||||
" SELECT format("
|
||||
" 'WITH %%s\n' ||"
|
||||
" ', idx_checker AS (\n' ||"
|
||||
" ' SELECT %%s,\n' ||"
|
||||
" ' %%s\n' || "
|
||||
" ' FROM intck_wrapper AS o\n' ||"
|
||||
" ')\n',"
|
||||
" ww.s, c, t.k"
|
||||
" ), t.n"
|
||||
" FROM case_statement, wrapper_with ww, thiskey t"
|
||||
")"
|
||||
|
||||
"SELECT m || "
|
||||
" group_concat('SELECT * FROM idx_checker ' || w_c, ' UNION ALL '), n"
|
||||
" FROM "
|
||||
"main_select, whereclause "
|
||||
, p->zDb, p->zDb, zObj, zObj
|
||||
, zPrev ? zPrev : "VALUES('')", zCommon
|
||||
);
|
||||
}else{
|
||||
pStmt = intckPrepareFmt(p,
|
||||
/* Table tabname contains a single row. The first column, "db", contains
|
||||
** the name of the db containing the table (e.g. "main") and the second,
|
||||
** "tab", the name of the table itself. */
|
||||
"WITH tabname(db, tab, idx, prev) AS (SELECT %Q, %Q, NULL, %Q)"
|
||||
""
|
||||
"%s" /* zCommon */
|
||||
|
||||
/* expr(e) contains one row for each index on table zObj. Value e
|
||||
** is set to an expression that evaluates to NULL if the required
|
||||
** entry is present in the index, or an error message otherwise. */
|
||||
", expr(e, p) AS ("
|
||||
" SELECT format('CASE WHEN EXISTS \n"
|
||||
" (SELECT 1 FROM %%Q.%%Q AS i INDEXED BY %%Q WHERE %%s%%s)\n"
|
||||
" THEN NULL\n"
|
||||
" ELSE format(''entry (%%s,%%s) missing from index %%s'', %%s, %%s)\n"
|
||||
" END\n'"
|
||||
" , t.db, t.tab, i.name, i.match_expr, ' AND (' || partial || ')',"
|
||||
" i.idx_ps, t.ps_pk, i.name, i.idx_idx, t.pk_pk),"
|
||||
" CASE WHEN partial IS NULL THEN NULL ELSE i.partial_alias END"
|
||||
" FROM tabpk t, idx i"
|
||||
")"
|
||||
|
||||
", numbered(ii, cond, e) AS ("
|
||||
" SELECT 0, 'n.ii=0', 'NULL'"
|
||||
" UNION ALL "
|
||||
" SELECT row_number() OVER (),"
|
||||
" '(n.ii='||row_number() OVER ()||COALESCE(' AND '||p||')', ')'), e"
|
||||
" FROM expr"
|
||||
")"
|
||||
|
||||
", counter_with(w) AS ("
|
||||
" SELECT 'WITH intck_counter(ii) AS (\n ' || "
|
||||
" group_concat('SELECT '||ii, ' UNION ALL\n ') "
|
||||
" || '\n)' FROM numbered"
|
||||
")"
|
||||
""
|
||||
", case_statement(c) AS ("
|
||||
" SELECT 'CASE ' || "
|
||||
" group_concat(format('\n WHEN %%s THEN (%%s)', cond, e), '') ||"
|
||||
" '\nEND AS error_message'"
|
||||
" FROM numbered"
|
||||
")"
|
||||
""
|
||||
|
||||
/* This table contains a single row consisting of a single value -
|
||||
** the text of an SQL expression that may be used by the main SQL
|
||||
** statement to output an SQL literal that can be used to resume
|
||||
** the scan if it is suspended. e.g. for a rowid table, an expression
|
||||
** like:
|
||||
**
|
||||
** format('(%d,%d)', _rowid_, n.ii)
|
||||
*/
|
||||
", thiskey(k, n) AS ("
|
||||
" SELECT o_pk || ', ii', n_pk+1 FROM tabpk"
|
||||
")"
|
||||
""
|
||||
", whereclause(w_c) AS ("
|
||||
" SELECT CASE WHEN prev!='' THEN "
|
||||
" '\nWHERE (' || o_pk ||', n.ii) > ' || prev"
|
||||
" ELSE ''"
|
||||
" END"
|
||||
" FROM tabpk, tabname"
|
||||
")"
|
||||
""
|
||||
", main_select(m, n) AS ("
|
||||
" SELECT format("
|
||||
" '%%s, %%s\nSELECT %%s,\n%%s\nFROM intck_wrapper AS o"
|
||||
", intck_counter AS n%%s\nORDER BY %%s', "
|
||||
" w, ww.s, c, thiskey.k, whereclause.w_c, t.o_pk"
|
||||
" ), thiskey.n"
|
||||
" FROM case_statement, tabpk t, counter_with, "
|
||||
" wrapper_with ww, thiskey, whereclause"
|
||||
")"
|
||||
|
||||
"SELECT m, n FROM main_select",
|
||||
p->zDb, zObj, zPrev, zCommon
|
||||
);
|
||||
}
|
||||
|
||||
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
zRet = intckMprintf(p, "%s", (const char*)sqlite3_column_text(pStmt, 0));
|
||||
if( pnKeyVal ){
|
||||
*pnKeyVal = sqlite3_column_int(pStmt, 1);
|
||||
}
|
||||
}
|
||||
intckFinalize(p, pStmt);
|
||||
|
||||
if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 1");
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new integrity-check object.
|
||||
*/
|
||||
int sqlite3_intck_open(
|
||||
sqlite3 *db, /* Database handle to operate on */
|
||||
const char *zDbArg, /* "main", "temp" etc. */
|
||||
sqlite3_intck **ppOut /* OUT: New integrity-check handle */
|
||||
){
|
||||
sqlite3_intck *pNew = 0;
|
||||
int rc = SQLITE_OK;
|
||||
const char *zDb = zDbArg ? zDbArg : "main";
|
||||
int nDb = (int)strlen(zDb);
|
||||
|
||||
pNew = (sqlite3_intck*)sqlite3_malloc(sizeof(*pNew) + nDb + 1);
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->db = db;
|
||||
pNew->zDb = (const char*)&pNew[1];
|
||||
memcpy(&pNew[1], zDb, nDb+1);
|
||||
rc = sqlite3_create_function(db, "parse_create_index",
|
||||
2, SQLITE_UTF8, 0, intckParseCreateIndexFunc, 0, 0
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_intck_close(pNew);
|
||||
pNew = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*ppOut = pNew;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the integrity-check object.
|
||||
*/
|
||||
void sqlite3_intck_close(sqlite3_intck *p){
|
||||
if( p ){
|
||||
sqlite3_finalize(p->pCheck);
|
||||
sqlite3_create_function(
|
||||
p->db, "parse_create_index", 1, SQLITE_UTF8, 0, 0, 0, 0
|
||||
);
|
||||
sqlite3_free(p->zObj);
|
||||
sqlite3_free(p->zKey);
|
||||
sqlite3_free(p->zTestSql);
|
||||
sqlite3_free(p->zErr);
|
||||
sqlite3_free(p->zMessage);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Step the integrity-check object.
|
||||
*/
|
||||
int sqlite3_intck_step(sqlite3_intck *p){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
|
||||
if( p->zMessage ){
|
||||
sqlite3_free(p->zMessage);
|
||||
p->zMessage = 0;
|
||||
}
|
||||
|
||||
if( p->bCorruptSchema ){
|
||||
p->rc = SQLITE_DONE;
|
||||
}else
|
||||
if( p->pCheck==0 ){
|
||||
intckFindObject(p);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( p->zObj ){
|
||||
char *zSql = 0;
|
||||
zSql = intckCheckObjectSql(p, p->zObj, p->zKey, &p->nKeyVal);
|
||||
p->pCheck = intckPrepare(p, zSql);
|
||||
sqlite3_free(zSql);
|
||||
sqlite3_free(p->zKey);
|
||||
p->zKey = 0;
|
||||
}else{
|
||||
p->rc = SQLITE_DONE;
|
||||
}
|
||||
}else if( p->rc==SQLITE_CORRUPT ){
|
||||
p->rc = SQLITE_OK;
|
||||
p->zMessage = intckMprintf(p, "%s",
|
||||
"corruption found while reading database schema"
|
||||
);
|
||||
p->bCorruptSchema = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( p->pCheck ){
|
||||
assert( p->rc==SQLITE_OK );
|
||||
if( sqlite3_step(p->pCheck)==SQLITE_ROW ){
|
||||
/* Normal case, do nothing. */
|
||||
}else{
|
||||
intckFinalize(p, p->pCheck);
|
||||
p->pCheck = 0;
|
||||
p->nKeyVal = 0;
|
||||
if( p->rc==SQLITE_CORRUPT ){
|
||||
p->rc = SQLITE_OK;
|
||||
p->zMessage = intckMprintf(p,
|
||||
"corruption found while scanning database object %s", p->zObj
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p->rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a message describing the corruption encountered by the most recent
|
||||
** call to sqlite3_intck_step(), or NULL if no corruption was encountered.
|
||||
*/
|
||||
const char *sqlite3_intck_message(sqlite3_intck *p){
|
||||
assert( p->pCheck==0 || p->zMessage==0 );
|
||||
if( p->zMessage ){
|
||||
return p->zMessage;
|
||||
}
|
||||
if( p->pCheck ){
|
||||
return (const char*)sqlite3_column_text(p->pCheck, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the error code and message.
|
||||
*/
|
||||
int sqlite3_intck_error(sqlite3_intck *p, const char **pzErr){
|
||||
if( pzErr ) *pzErr = p->zErr;
|
||||
return (p->rc==SQLITE_DONE ? SQLITE_OK : p->rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close any read transaction the integrity-check object is holding open
|
||||
** on the database.
|
||||
*/
|
||||
int sqlite3_intck_unlock(sqlite3_intck *p){
|
||||
if( p->rc==SQLITE_OK && p->pCheck ){
|
||||
assert( p->zKey==0 && p->nKeyVal>0 );
|
||||
intckSaveKey(p);
|
||||
intckFinalize(p, p->pCheck);
|
||||
p->pCheck = 0;
|
||||
}
|
||||
return p->rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the SQL statement used to check object zObj. Or, if zObj is
|
||||
** NULL, the current SQL statement.
|
||||
*/
|
||||
const char *sqlite3_intck_test_sql(sqlite3_intck *p, const char *zObj){
|
||||
sqlite3_free(p->zTestSql);
|
||||
if( zObj ){
|
||||
p->zTestSql = intckCheckObjectSql(p, zObj, 0, 0);
|
||||
}else{
|
||||
if( p->zObj ){
|
||||
p->zTestSql = intckCheckObjectSql(p, p->zObj, p->zKey, 0);
|
||||
}else{
|
||||
sqlite3_free(p->zTestSql);
|
||||
p->zTestSql = 0;
|
||||
}
|
||||
}
|
||||
return p->zTestSql;
|
||||
}
|
||||
@ -1,171 +0,0 @@
|
||||
/*
|
||||
** 2024-02-08
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
** Incremental Integrity-Check Extension
|
||||
** -------------------------------------
|
||||
**
|
||||
** This module contains code to check whether or not an SQLite database
|
||||
** is well-formed or corrupt. This is the same task as performed by SQLite's
|
||||
** built-in "PRAGMA integrity_check" command. This module differs from
|
||||
** "PRAGMA integrity_check" in that:
|
||||
**
|
||||
** + It is less thorough - this module does not detect certain types
|
||||
** of corruption that are detected by the PRAGMA command. However,
|
||||
** it does detect all kinds of corruption that are likely to cause
|
||||
** errors in SQLite applications.
|
||||
**
|
||||
** + It is slower. Sometimes up to three times slower.
|
||||
**
|
||||
** + It allows integrity-check operations to be split into multiple
|
||||
** transactions, so that the database does not need to be read-locked
|
||||
** for the duration of the integrity-check.
|
||||
**
|
||||
** One way to use the API to run integrity-check on the "main" database
|
||||
** of handle db is:
|
||||
**
|
||||
** int rc = SQLITE_OK;
|
||||
** sqlite3_intck *p = 0;
|
||||
**
|
||||
** sqlite3_intck_open(db, "main", &p);
|
||||
** while( SQLITE_OK==sqlite3_intck_step(p) ){
|
||||
** const char *zMsg = sqlite3_intck_message(p);
|
||||
** if( zMsg ) printf("corruption: %s\n", zMsg);
|
||||
** }
|
||||
** rc = sqlite3_intck_error(p, &zErr);
|
||||
** if( rc!=SQLITE_OK ){
|
||||
** printf("error occured (rc=%d), (errmsg=%s)\n", rc, zErr);
|
||||
** }
|
||||
** sqlite3_intck_close(p);
|
||||
**
|
||||
** Usually, the sqlite3_intck object opens a read transaction within the
|
||||
** first call to sqlite3_intck_step() and holds it open until the
|
||||
** integrity-check is complete. However, if sqlite3_intck_unlock() is
|
||||
** called, the read transaction is ended and a new read transaction opened
|
||||
** by the subsequent call to sqlite3_intck_step().
|
||||
*/
|
||||
|
||||
#ifndef _SQLITE_INTCK_H
|
||||
#define _SQLITE_INTCK_H
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** An ongoing incremental integrity-check operation is represented by an
|
||||
** opaque pointer of the following type.
|
||||
*/
|
||||
typedef struct sqlite3_intck sqlite3_intck;
|
||||
|
||||
/*
|
||||
** Open a new incremental integrity-check object. If successful, populate
|
||||
** output variable (*ppOut) with the new object handle and return SQLITE_OK.
|
||||
** Or, if an error occurs, set (*ppOut) to NULL and return an SQLite error
|
||||
** code (e.g. SQLITE_NOMEM).
|
||||
**
|
||||
** The integrity-check will be conducted on database zDb (which must be "main",
|
||||
** "temp", or the name of an attached database) of database handle db. Once
|
||||
** this function has been called successfully, the caller should not use
|
||||
** database handle db until the integrity-check object has been destroyed
|
||||
** using sqlite3_intck_close().
|
||||
*/
|
||||
int sqlite3_intck_open(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zDb, /* Database name ("main", "temp" etc.) */
|
||||
sqlite3_intck **ppOut /* OUT: New sqlite3_intck handle */
|
||||
);
|
||||
|
||||
/*
|
||||
** Close and release all resources associated with a handle opened by an
|
||||
** earlier call to sqlite3_intck_open(). The results of using an
|
||||
** integrity-check handle after it has been passed to this function are
|
||||
** undefined.
|
||||
*/
|
||||
void sqlite3_intck_close(sqlite3_intck *pCk);
|
||||
|
||||
/*
|
||||
** Do the next step of the integrity-check operation specified by the handle
|
||||
** passed as the only argument. This function returns SQLITE_DONE if the
|
||||
** integrity-check operation is finished, or an SQLite error code if
|
||||
** an error occurs, or SQLITE_OK if no error occurs but the integrity-check
|
||||
** is not finished. It is not considered an error if database corruption
|
||||
** is encountered.
|
||||
**
|
||||
** Following a successful call to sqlite3_intck_step() (one that returns
|
||||
** SQLITE_OK), sqlite3_intck_message() returns a non-NULL value if
|
||||
** corruption was detected in the db.
|
||||
**
|
||||
** If an error occurs and a value other than SQLITE_OK or SQLITE_DONE is
|
||||
** returned, then the integrity-check handle is placed in an error state.
|
||||
** In this state all subsequent calls to sqlite3_intck_step() or
|
||||
** sqlite3_intck_unlock() will immediately return the same error. The
|
||||
** sqlite3_intck_error() method may be used to obtain an English language
|
||||
** error message in this case.
|
||||
*/
|
||||
int sqlite3_intck_step(sqlite3_intck *pCk);
|
||||
|
||||
/*
|
||||
** If the previous call to sqlite3_intck_step() encountered corruption
|
||||
** within the database, then this function returns a pointer to a buffer
|
||||
** containing a nul-terminated string describing the corruption in
|
||||
** English. If the previous call to sqlite3_intck_step() did not encounter
|
||||
** corruption, or if there was no previous call, this function returns
|
||||
** NULL.
|
||||
*/
|
||||
const char *sqlite3_intck_message(sqlite3_intck *pCk);
|
||||
|
||||
/*
|
||||
** Close any read-transaction opened by an earlier call to
|
||||
** sqlite3_intck_step(). Any subsequent call to sqlite3_intck_step() will
|
||||
** open a new transaction. Return SQLITE_OK if successful, or an SQLite error
|
||||
** code otherwise.
|
||||
**
|
||||
** If an error occurs, then the integrity-check handle is placed in an error
|
||||
** state. In this state all subsequent calls to sqlite3_intck_step() or
|
||||
** sqlite3_intck_unlock() will immediately return the same error. The
|
||||
** sqlite3_intck_error() method may be used to obtain an English language
|
||||
** error message in this case.
|
||||
*/
|
||||
int sqlite3_intck_unlock(sqlite3_intck *pCk);
|
||||
|
||||
/*
|
||||
** If an error has occurred in an earlier call to sqlite3_intck_step()
|
||||
** or sqlite3_intck_unlock(), then this method returns the associated
|
||||
** SQLite error code. Additionally, if pzErr is not NULL, then (*pzErr)
|
||||
** may be set to point to a nul-terminated string containing an English
|
||||
** language error message. Or, if no error message is available, to
|
||||
** NULL.
|
||||
**
|
||||
** If no error has occurred within sqlite3_intck_step() or
|
||||
** sqlite_intck_unlock() calls on the handle passed as the first argument,
|
||||
** then SQLITE_OK is returned and (*pzErr) set to NULL.
|
||||
*/
|
||||
int sqlite3_intck_error(sqlite3_intck *pCk, const char **pzErr);
|
||||
|
||||
/*
|
||||
** This API is used for testing only. It returns the full-text of an SQL
|
||||
** statement used to test object zObj, which may be a table or index.
|
||||
** The returned buffer is valid until the next call to either this function
|
||||
** or sqlite3_intck_close() on the same sqlite3_intck handle.
|
||||
*/
|
||||
const char *sqlite3_intck_test_sql(sqlite3_intck *pCk, const char *zObj);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* ifndef _SQLITE_INTCK_H */
|
||||
@ -1,238 +0,0 @@
|
||||
/*
|
||||
** 2010 August 28
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing all sorts of SQLite interfaces. This code
|
||||
** is not included in the SQLite library.
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqlite3intck.h"
|
||||
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* In test1.c */
|
||||
int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
const char *sqlite3ErrName(int);
|
||||
|
||||
typedef struct TestIntck TestIntck;
|
||||
struct TestIntck {
|
||||
sqlite3_intck *intck;
|
||||
};
|
||||
|
||||
static int testIntckCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
struct Subcmd {
|
||||
const char *zName;
|
||||
int nArg;
|
||||
const char *zExpect;
|
||||
} aCmd[] = {
|
||||
{"close", 0, ""}, /* 0 */
|
||||
{"step", 0, ""}, /* 1 */
|
||||
{"message", 0, ""}, /* 2 */
|
||||
{"error", 0, ""}, /* 3 */
|
||||
{"unlock", 0, ""}, /* 4 */
|
||||
{"test_sql", 1, ""}, /* 5 */
|
||||
{0 , 0}
|
||||
};
|
||||
int rc = TCL_OK;
|
||||
int iIdx = -1;
|
||||
TestIntck *p = (TestIntck*)clientData;
|
||||
|
||||
if( objc<2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = Tcl_GetIndexFromObjStruct(
|
||||
interp, objv[1], aCmd, sizeof(aCmd[0]), "SUB-COMMAND", 0, &iIdx
|
||||
);
|
||||
if( rc ) return rc;
|
||||
|
||||
if( objc!=2+aCmd[iIdx].nArg ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, aCmd[iIdx].zExpect);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( iIdx ){
|
||||
case 0: assert( 0==strcmp("close", aCmd[iIdx].zName) ); {
|
||||
Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0));
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: assert( 0==strcmp("step", aCmd[iIdx].zName) ); {
|
||||
rc = sqlite3_intck_step(p->intck);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: assert( 0==strcmp("message", aCmd[iIdx].zName) ); {
|
||||
const char *z = sqlite3_intck_message(p->intck);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(z ? z : "", -1));
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: assert( 0==strcmp("error", aCmd[iIdx].zName) ); {
|
||||
const char *zErr = 0;
|
||||
rc = sqlite3_intck_error(p->intck, 0);
|
||||
Tcl_Obj *pRes = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(
|
||||
interp, pRes, Tcl_NewStringObj(sqlite3ErrName(rc), -1)
|
||||
);
|
||||
sqlite3_intck_error(p->intck, &zErr);
|
||||
Tcl_ListObjAppendElement(
|
||||
interp, pRes, Tcl_NewStringObj(zErr ? zErr : 0, -1)
|
||||
);
|
||||
Tcl_SetObjResult(interp, pRes);
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: assert( 0==strcmp("unlock", aCmd[iIdx].zName) ); {
|
||||
rc = sqlite3_intck_unlock(p->intck);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
break;
|
||||
}
|
||||
|
||||
case 5: assert( 0==strcmp("test_sql", aCmd[iIdx].zName) ); {
|
||||
const char *zObj = Tcl_GetString(objv[2]);
|
||||
const char *zSql = sqlite3_intck_test_sql(p->intck, zObj[0] ? zObj : 0);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zSql, -1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for commands created by test_sqlite3_intck().
|
||||
*/
|
||||
static void testIntckFree(void *clientData){
|
||||
TestIntck *p = (TestIntck*)clientData;
|
||||
sqlite3_intck_close(p->intck);
|
||||
ckfree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_intck DB DBNAME
|
||||
*/
|
||||
static int test_sqlite3_intck(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
char zName[64];
|
||||
int iName = 0;
|
||||
Tcl_CmdInfo info;
|
||||
TestIntck *p = 0;
|
||||
sqlite3 *db = 0;
|
||||
const char *zDb = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
p = (TestIntck*)ckalloc(sizeof(TestIntck));
|
||||
memset(p, 0, sizeof(TestIntck));
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
if( zDb[0]=='\0' ) zDb = 0;
|
||||
|
||||
rc = sqlite3_intck_open(db, zDb, &p->intck);
|
||||
if( rc!=SQLITE_OK ){
|
||||
ckfree(p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errstr(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
do {
|
||||
sprintf(zName, "intck%d", iName++);
|
||||
}while( Tcl_GetCommandInfo(interp, zName, &info)!=0 );
|
||||
Tcl_CreateObjCommand(interp, zName, testIntckCmd, (void*)p, testIntckFree);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zName, -1));
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: test_do_intck DB DBNAME
|
||||
*/
|
||||
static int test_do_intck(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db = 0;
|
||||
const char *zDb = 0;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_intck *pCk = 0;
|
||||
Tcl_Obj *pRet = 0;
|
||||
const char *zErr = 0;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRet);
|
||||
|
||||
rc = sqlite3_intck_open(db, zDb, &pCk);
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_intck_step(pCk)==SQLITE_OK ){
|
||||
const char *zMsg = sqlite3_intck_message(pCk);
|
||||
if( zMsg ){
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zMsg, -1));
|
||||
}
|
||||
}
|
||||
rc = sqlite3_intck_error(pCk, &zErr);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( zErr ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
}
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
}
|
||||
Tcl_DecrRefCount(pRet);
|
||||
sqlite3_intck_close(pCk);
|
||||
sqlite3_intck_close(0);
|
||||
return rc ? TCL_ERROR : TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetestintck_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_intck", test_sqlite3_intck, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "test_do_intck", test_do_intck, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
@ -446,9 +446,9 @@ static int cksmRead(
|
||||
** (2) checksum verification is enabled
|
||||
** (3) we are not in the middle of checkpoint
|
||||
*/
|
||||
if( iAmt>=512 && (iAmt & (iAmt-1))==0 /* (1) */
|
||||
&& p->verifyCksm /* (2) */
|
||||
&& !p->inCkpt /* (3) */
|
||||
if( iAmt>=512 /* (1) */
|
||||
&& p->verifyCksm /* (2) */
|
||||
&& !p->inCkpt /* (3) */
|
||||
){
|
||||
u8 cksum[8];
|
||||
cksmCompute((u8*)zBuf, iAmt-8, cksum);
|
||||
|
||||
@ -372,9 +372,7 @@ static int writeFile(
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
if( S_ISLNK(mode) ){
|
||||
const char *zTo = (const char*)sqlite3_value_text(pData);
|
||||
if( zTo==0 ) return 1;
|
||||
unlink(zFile);
|
||||
if( symlink(zTo, zFile)<0 ) return 1;
|
||||
if( zTo==0 || symlink(zTo, zFile)<0 ) return 1;
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
@ -460,19 +458,13 @@ static int writeFile(
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
/* Legacy unix.
|
||||
**
|
||||
** Do not use utimes() on a symbolic link - it sees through the link and
|
||||
** modifies the timestamps on the target. Or fails if the target does
|
||||
** not exist. */
|
||||
if( 0==S_ISLNK(mode) ){
|
||||
struct timeval times[2];
|
||||
times[0].tv_usec = times[1].tv_usec = 0;
|
||||
times[0].tv_sec = time(0);
|
||||
times[1].tv_sec = mtime;
|
||||
if( utimes(zFile, times) ){
|
||||
return 1;
|
||||
}
|
||||
/* Legacy unix */
|
||||
struct timeval times[2];
|
||||
times[0].tv_usec = times[1].tv_usec = 0;
|
||||
times[0].tv_sec = time(0);
|
||||
times[1].tv_sec = mtime;
|
||||
if( utimes(zFile, times) ){
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -103,20 +103,16 @@ SQLITE_EXTENSION_INIT1
|
||||
** index is ix. The 0th member is given by smBase. The sequence members
|
||||
** progress per ix increment by smStep.
|
||||
*/
|
||||
static sqlite3_int64 genSeqMember(
|
||||
sqlite3_int64 smBase,
|
||||
sqlite3_int64 smStep,
|
||||
sqlite3_uint64 ix
|
||||
){
|
||||
static const sqlite3_uint64 mxI64 =
|
||||
((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff;
|
||||
if( ix>=mxI64 ){
|
||||
static sqlite3_int64 genSeqMember(sqlite3_int64 smBase,
|
||||
sqlite3_int64 smStep,
|
||||
sqlite3_uint64 ix){
|
||||
if( ix>=(sqlite3_uint64)LLONG_MAX ){
|
||||
/* Get ix into signed i64 range. */
|
||||
ix -= mxI64;
|
||||
ix -= (sqlite3_uint64)LLONG_MAX;
|
||||
/* With 2's complement ALU, this next can be 1 step, but is split into
|
||||
* 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
|
||||
smBase += (mxI64/2) * smStep;
|
||||
smBase += (mxI64 - mxI64/2) * smStep;
|
||||
smBase += (LLONG_MAX/2) * smStep;
|
||||
smBase += (LLONG_MAX - LLONG_MAX/2) * smStep;
|
||||
}
|
||||
/* Under UBSAN (or on 1's complement machines), must do this last term
|
||||
* in steps to avoid the dreaded (and harmless) signed multiply overlow. */
|
||||
@ -376,13 +372,13 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
|
||||
** parameter. (idxStr is not used in this implementation.) idxNum
|
||||
** is a bitmask showing which constraints are available:
|
||||
**
|
||||
** 0x01: start=VALUE
|
||||
** 0x02: stop=VALUE
|
||||
** 0x04: step=VALUE
|
||||
** 0x08: descending order
|
||||
** 0x10: ascending order
|
||||
** 0x20: LIMIT VALUE
|
||||
** 0x40: OFFSET VALUE
|
||||
** 1: start=VALUE
|
||||
** 2: stop=VALUE
|
||||
** 4: step=VALUE
|
||||
**
|
||||
** Also, if bit 8 is set, that means that the series should be output
|
||||
** in descending order rather than in ascending order. If bit 16 is
|
||||
** set, then output must appear in ascending order.
|
||||
**
|
||||
** This routine should initialize the cursor and position it so that it
|
||||
** is pointing at the first row, or pointing off the end of the table
|
||||
@ -396,44 +392,26 @@ static int seriesFilter(
|
||||
series_cursor *pCur = (series_cursor *)pVtabCursor;
|
||||
int i = 0;
|
||||
(void)idxStrUnused;
|
||||
if( idxNum & 0x01 ){
|
||||
if( idxNum & 1 ){
|
||||
pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->ss.iBase = 0;
|
||||
}
|
||||
if( idxNum & 0x02 ){
|
||||
if( idxNum & 2 ){
|
||||
pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->ss.iTerm = 0xffffffff;
|
||||
}
|
||||
if( idxNum & 0x04 ){
|
||||
if( idxNum & 4 ){
|
||||
pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
|
||||
if( pCur->ss.iStep==0 ){
|
||||
pCur->ss.iStep = 1;
|
||||
}else if( pCur->ss.iStep<0 ){
|
||||
if( (idxNum & 0x10)==0 ) idxNum |= 0x08;
|
||||
if( (idxNum & 16)==0 ) idxNum |= 8;
|
||||
}
|
||||
}else{
|
||||
pCur->ss.iStep = 1;
|
||||
}
|
||||
if( idxNum & 0x20 ){
|
||||
sqlite3_int64 iLimit = sqlite3_value_int64(argv[i++]);
|
||||
sqlite3_int64 iTerm;
|
||||
if( idxNum & 0x40 ){
|
||||
sqlite3_int64 iOffset = sqlite3_value_int64(argv[i++]);
|
||||
if( iOffset>0 ){
|
||||
pCur->ss.iBase += pCur->ss.iStep*iOffset;
|
||||
}
|
||||
}
|
||||
if( iLimit>=0 ){
|
||||
iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
|
||||
if( pCur->ss.iStep<0 ){
|
||||
if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
|
||||
}else{
|
||||
if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<argc; i++){
|
||||
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
|
||||
/* If any of the constraints have a NULL value, then return no rows.
|
||||
@ -444,7 +422,7 @@ static int seriesFilter(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( idxNum & 0x08 ){
|
||||
if( idxNum & 8 ){
|
||||
pCur->ss.isReversing = pCur->ss.iStep > 0;
|
||||
}else{
|
||||
pCur->ss.isReversing = pCur->ss.iStep < 0;
|
||||
@ -464,13 +442,10 @@ static int seriesFilter(
|
||||
**
|
||||
** The query plan is represented by bits in idxNum:
|
||||
**
|
||||
** 0x01 start = $value -- constraint exists
|
||||
** 0x02 stop = $value -- constraint exists
|
||||
** 0x04 step = $value -- constraint exists
|
||||
** 0x08 output is in descending order
|
||||
** 0x10 output is in ascending order
|
||||
** 0x20 LIMIT $value -- constraint exists
|
||||
** 0x40 OFFSET $value -- constraint exists
|
||||
** (1) start = $value -- constraint exists
|
||||
** (2) stop = $value -- constraint exists
|
||||
** (4) step = $value -- constraint exists
|
||||
** (8) output in descending order
|
||||
*/
|
||||
static int seriesBestIndex(
|
||||
sqlite3_vtab *pVTab,
|
||||
@ -478,12 +453,10 @@ static int seriesBestIndex(
|
||||
){
|
||||
int i, j; /* Loop over constraints */
|
||||
int idxNum = 0; /* The query plan bitmask */
|
||||
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
|
||||
int bStartSeen = 0; /* EQ constraint seen on the START column */
|
||||
#endif
|
||||
int unusableMask = 0; /* Mask of unusable constraints */
|
||||
int nArg = 0; /* Number of arguments that seriesFilter() expects */
|
||||
int aIdx[5]; /* Constraints on start, stop, step, LIMIT, OFFSET */
|
||||
int aIdx[3]; /* Constraints on start, stop, and step */
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
|
||||
/* This implementation assumes that the start, stop, and step columns
|
||||
@ -491,54 +464,28 @@ static int seriesBestIndex(
|
||||
assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
|
||||
assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
|
||||
|
||||
aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = -1;
|
||||
aIdx[0] = aIdx[1] = aIdx[2] = -1;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
int iCol; /* 0 for start, 1 for stop, 2 for step */
|
||||
int iMask; /* bitmask for those column */
|
||||
int op = pConstraint->op;
|
||||
if( op>=SQLITE_INDEX_CONSTRAINT_LIMIT
|
||||
&& op<=SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
){
|
||||
if( pConstraint->usable==0 ){
|
||||
/* do nothing */
|
||||
}else if( op==SQLITE_INDEX_CONSTRAINT_LIMIT ){
|
||||
aIdx[3] = i;
|
||||
idxNum |= 0x20;
|
||||
}else{
|
||||
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
|
||||
aIdx[4] = i;
|
||||
idxNum |= 0x40;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( pConstraint->iColumn<SERIES_COLUMN_START ) continue;
|
||||
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
|
||||
assert( iCol>=0 && iCol<=2 );
|
||||
iMask = 1 << iCol;
|
||||
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
|
||||
if( iCol==0 && op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
bStartSeen = 1;
|
||||
}
|
||||
#endif
|
||||
if( iCol==0 ) bStartSeen = 1;
|
||||
if( pConstraint->usable==0 ){
|
||||
unusableMask |= iMask;
|
||||
continue;
|
||||
}else if( op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
idxNum |= iMask;
|
||||
aIdx[iCol] = i;
|
||||
}
|
||||
}
|
||||
if( aIdx[3]==0 ){
|
||||
/* Ignore OFFSET if LIMIT is omitted */
|
||||
idxNum &= ~0x60;
|
||||
aIdx[4] = 0;
|
||||
}
|
||||
for(i=0; i<5; i++){
|
||||
for(i=0; i<3; i++){
|
||||
if( (j = aIdx[i])>=0 ){
|
||||
pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[j].omit =
|
||||
!SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3;
|
||||
pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
|
||||
}
|
||||
}
|
||||
/* The current generate_column() implementation requires at least one
|
||||
@ -559,22 +506,19 @@ static int seriesBestIndex(
|
||||
** this plan is unusable */
|
||||
return SQLITE_CONSTRAINT;
|
||||
}
|
||||
if( (idxNum & 0x03)==0x03 ){
|
||||
if( (idxNum & 3)==3 ){
|
||||
/* Both start= and stop= boundaries are available. This is the
|
||||
** the preferred case */
|
||||
pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
|
||||
pIdxInfo->estimatedRows = 1000;
|
||||
if( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0].iColumn==0 ){
|
||||
if( pIdxInfo->aOrderBy[0].desc ){
|
||||
idxNum |= 0x08;
|
||||
idxNum |= 8;
|
||||
}else{
|
||||
idxNum |= 0x10;
|
||||
idxNum |= 16;
|
||||
}
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
}else if( (idxNum & 0x21)==0x21 ){
|
||||
/* We have start= and LIMIT */
|
||||
pIdxInfo->estimatedRows = 2500;
|
||||
}else{
|
||||
/* If either boundary is missing, we have to generate a huge span
|
||||
** of numbers. Make this case very expensive so that the query
|
||||
|
||||
@ -81,7 +81,7 @@ static void sqlarUncompressFunc(
|
||||
sqlite3_value **argv
|
||||
){
|
||||
uLong nData;
|
||||
sqlite3_int64 sz;
|
||||
uLongf sz;
|
||||
|
||||
assert( argc==2 );
|
||||
sz = sqlite3_value_int(argv[1]);
|
||||
@ -89,15 +89,14 @@ static void sqlarUncompressFunc(
|
||||
if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}else{
|
||||
uLongf szf = sz;
|
||||
const Bytef *pData= sqlite3_value_blob(argv[0]);
|
||||
Bytef *pOut = sqlite3_malloc(sz);
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){
|
||||
}else if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){
|
||||
sqlite3_result_error(context, "error in uncompress()", -1);
|
||||
}else{
|
||||
sqlite3_result_blob(context, pOut, szf, SQLITE_TRANSIENT);
|
||||
sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT);
|
||||
}
|
||||
sqlite3_free(pOut);
|
||||
}
|
||||
|
||||
@ -38,9 +38,8 @@ SQLITE_EXTENSION_INIT1
|
||||
typedef struct vtablog_vtab vtablog_vtab;
|
||||
struct vtablog_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
char *zDb; /* Schema name. argv[1] of xConnect/xCreate */
|
||||
char *zName; /* Table name. argv[2] of xConnect/xCreate */
|
||||
int nRow; /* Number of rows in the table */
|
||||
int iInst; /* Instance number for this vtablog table */
|
||||
int nCursor; /* Number of cursors created */
|
||||
};
|
||||
|
||||
@ -168,14 +167,15 @@ static int vtablogConnectCreate(
|
||||
char **pzErr,
|
||||
int isCreate
|
||||
){
|
||||
static int nInst = 0;
|
||||
vtablog_vtab *pNew;
|
||||
int i;
|
||||
int rc;
|
||||
int iInst = ++nInst;
|
||||
char *zSchema = 0;
|
||||
char *zNRow = 0;
|
||||
|
||||
printf("%s.%s.%s():\n", argv[1], argv[2],
|
||||
isCreate ? "xCreate" : "xConnect");
|
||||
printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst);
|
||||
printf(" argc=%d\n", argc);
|
||||
for(i=0; i<argc; i++){
|
||||
printf(" argv[%d] = ", i);
|
||||
@ -189,18 +189,17 @@ static int vtablogConnectCreate(
|
||||
for(i=3; i<argc; i++){
|
||||
const char *z = argv[i];
|
||||
if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){
|
||||
rc = SQLITE_ERROR;
|
||||
goto vtablog_end_connect;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
|
||||
rc = SQLITE_ERROR;
|
||||
goto vtablog_end_connect;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if( zSchema==0 ){
|
||||
zSchema = sqlite3_mprintf("%s","CREATE TABLE x(a,b);");
|
||||
*pzErr = sqlite3_mprintf("no schema defined");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
printf(" schema = '%s'\n", zSchema);
|
||||
rc = sqlite3_declare_vtab(db, zSchema);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
@ -209,14 +208,8 @@ static int vtablogConnectCreate(
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->nRow = 10;
|
||||
if( zNRow ) pNew->nRow = atoi(zNRow);
|
||||
printf(" nrow = %d\n", pNew->nRow);
|
||||
pNew->zDb = sqlite3_mprintf("%s", argv[1]);
|
||||
pNew->zName = sqlite3_mprintf("%s", argv[2]);
|
||||
pNew->iInst = iInst;
|
||||
}
|
||||
|
||||
vtablog_end_connect:
|
||||
sqlite3_free(zSchema);
|
||||
sqlite3_free(zNRow);
|
||||
return rc;
|
||||
}
|
||||
static int vtablogCreate(
|
||||
@ -244,9 +237,7 @@ static int vtablogConnect(
|
||||
*/
|
||||
static int vtablogDisconnect(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
printf("%s.%s.xDisconnect()\n", pTab->zDb, pTab->zName);
|
||||
sqlite3_free(pTab->zDb);
|
||||
sqlite3_free(pTab->zName);
|
||||
printf("vtablogDisconnect(%d)\n", pTab->iInst);
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -256,9 +247,7 @@ static int vtablogDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int vtablogDestroy(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
printf("%s.%s.xDestroy()\n", pTab->zDb, pTab->zName);
|
||||
sqlite3_free(pTab->zDb);
|
||||
sqlite3_free(pTab->zName);
|
||||
printf("vtablogDestroy(%d)\n", pTab->iInst);
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -269,8 +258,7 @@ static int vtablogDestroy(sqlite3_vtab *pVtab){
|
||||
static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)p;
|
||||
vtablog_cursor *pCur;
|
||||
printf("%s.%s.xOpen(cursor=%d)\n", pTab->zDb, pTab->zName,
|
||||
++pTab->nCursor);
|
||||
printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor);
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
@ -285,7 +273,7 @@ static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
static int vtablogClose(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xClose(cursor=%d)\n", pTab->zDb, pTab->zName, pCur->iCursor);
|
||||
printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor);
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -297,9 +285,8 @@ static int vtablogClose(sqlite3_vtab_cursor *cur){
|
||||
static int vtablogNext(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xNext(cursor=%d) rowid %d -> %d\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor,
|
||||
(int)pCur->iRowid, (int)pCur->iRowid+1);
|
||||
printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n",
|
||||
pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1);
|
||||
pCur->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -323,8 +310,8 @@ static int vtablogColumn(
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
|
||||
}
|
||||
printf("%s.%s.xColumn(cursor=%d, i=%d): [%s]\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor, i, zVal);
|
||||
printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n",
|
||||
pTab->iInst, pCur->iCursor, i, zVal);
|
||||
sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -336,8 +323,8 @@ static int vtablogColumn(
|
||||
static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xRowid(cursor=%d): %d\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor, (int)pCur->iRowid);
|
||||
printf("vtablogRowid(tab=%d, cursor=%d): %d\n",
|
||||
pTab->iInst, pCur->iCursor, (int)pCur->iRowid);
|
||||
*pRowid = pCur->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -350,8 +337,8 @@ static int vtablogEof(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
int rc = pCur->iRowid >= pTab->nRow;
|
||||
printf("%s.%s.xEof(cursor=%d): %d\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor, rc);
|
||||
printf("vtablogEof(tab=%d, cursor=%d): %d\n",
|
||||
pTab->iInst, pCur->iCursor, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -430,7 +417,7 @@ static int vtablogFilter(
|
||||
){
|
||||
vtablog_cursor *pCur = (vtablog_cursor *)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xFilter(cursor=%d):\n", pTab->zDb, pTab->zName, pCur->iCursor);
|
||||
printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor);
|
||||
pCur->iRowid = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -443,37 +430,12 @@ static int vtablogFilter(
|
||||
*/
|
||||
static int vtablogBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *p
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
int i;
|
||||
printf("%s.%s.xBestIndex():\n", pTab->zDb, pTab->zName);
|
||||
printf(" colUsed: 0x%016llx\n", p->colUsed);
|
||||
printf(" nConstraint: %d\n", p->nConstraint);
|
||||
for(i=0; i<p->nConstraint; i++){
|
||||
printf(
|
||||
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
|
||||
i,
|
||||
p->aConstraint[i].iColumn,
|
||||
p->aConstraint[i].iTermOffset,
|
||||
p->aConstraint[i].op,
|
||||
p->aConstraint[i].usable,
|
||||
sqlite3_vtab_collation(p,i));
|
||||
}
|
||||
printf(" nOrderBy: %d\n", p->nOrderBy);
|
||||
for(i=0; i<p->nOrderBy; i++){
|
||||
printf(" orderby[%d]: col=%d desc=%d\n",
|
||||
i,
|
||||
p->aOrderBy[i].iColumn,
|
||||
p->aOrderBy[i].desc);
|
||||
}
|
||||
p->estimatedCost = (double)500;
|
||||
p->estimatedRows = 500;
|
||||
printf(" idxNum=%d\n", p->idxNum);
|
||||
printf(" idxStr=NULL\n");
|
||||
printf(" orderByConsumed=%d\n", p->orderByConsumed);
|
||||
printf(" estimatedCost=%g\n", p->estimatedCost);
|
||||
printf(" estimatedRows=%lld\n", p->estimatedRows);
|
||||
printf("vtablogBestIndex(tab=%d):\n", pTab->iInst);
|
||||
pIdxInfo->estimatedCost = (double)500;
|
||||
pIdxInfo->estimatedRows = 500;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -492,7 +454,7 @@ static int vtablogUpdate(
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
int i;
|
||||
printf("%s.%s.xUpdate():\n", pTab->zDb, pTab->zName);
|
||||
printf("vtablogUpdate(tab=%d):\n", pTab->iInst);
|
||||
printf(" argc=%d\n", argc);
|
||||
for(i=0; i<argc; i++){
|
||||
printf(" argv[%d]=", i);
|
||||
@ -502,88 +464,12 @@ static int vtablogUpdate(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int vtablogBegin(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xBegin()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogSync(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xSync()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogCommit(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xCommit()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRollback(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRollback()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogSavepoint(sqlite3_vtab *tab, int N){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xSavepoint(%d)\n", pTab->zDb, pTab->zName, N);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRelease(sqlite3_vtab *tab, int N){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRelease(%d)\n", pTab->zDb, pTab->zName, N);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRollbackTo(sqlite3_vtab *tab, int N){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRollbackTo(%d)\n", pTab->zDb, pTab->zName, N);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int vtablogFindMethod(
|
||||
sqlite3_vtab *tab,
|
||||
int nArg,
|
||||
const char *zName,
|
||||
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void **ppArg
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xFindMethod(nArg=%d, zName=%s)\n",
|
||||
pTab->zDb, pTab->zName, nArg, zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRename(sqlite3_vtab *tab, const char *zNew){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRename('%s')\n", pTab->zDb, pTab->zName, zNew);
|
||||
sqlite3_free(pTab->zName);
|
||||
pTab->zName = sqlite3_mprintf("%s", zNew);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Any table name that contains the text "shadow" is seen as a
|
||||
** shadow table. Nothing else is.
|
||||
*/
|
||||
static int vtablogShadowName(const char *zName){
|
||||
printf("vtablog.xShadowName('%s')\n", zName);
|
||||
return sqlite3_strglob("*shadow*", zName)==0;
|
||||
}
|
||||
|
||||
static int vtablogIntegrity(
|
||||
sqlite3_vtab *tab,
|
||||
const char *zSchema,
|
||||
const char *zTabName,
|
||||
int mFlags,
|
||||
char **pzErr
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xIntegrity(mFlags=0x%x)\n", pTab->zDb, pTab->zName, mFlags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** vtablog virtual table.
|
||||
*/
|
||||
static sqlite3_module vtablogModule = {
|
||||
4, /* iVersion */
|
||||
0, /* iVersion */
|
||||
vtablogCreate, /* xCreate */
|
||||
vtablogConnect, /* xConnect */
|
||||
vtablogBestIndex, /* xBestIndex */
|
||||
@ -597,17 +483,17 @@ static sqlite3_module vtablogModule = {
|
||||
vtablogColumn, /* xColumn - read data */
|
||||
vtablogRowid, /* xRowid - read data */
|
||||
vtablogUpdate, /* xUpdate */
|
||||
vtablogBegin, /* xBegin */
|
||||
vtablogSync, /* xSync */
|
||||
vtablogCommit, /* xCommit */
|
||||
vtablogRollback, /* xRollback */
|
||||
vtablogFindMethod, /* xFindMethod */
|
||||
vtablogRename, /* xRename */
|
||||
vtablogSavepoint, /* xSavepoint */
|
||||
vtablogRelease, /* xRelease */
|
||||
vtablogRollbackTo, /* xRollbackTo */
|
||||
vtablogShadowName, /* xShadowName */
|
||||
vtablogIntegrity /* xIntegrity */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@ -199,7 +199,6 @@ typedef unsigned int u32;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned char u8;
|
||||
typedef sqlite3_int64 i64;
|
||||
typedef sqlite3_uint64 u64;
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -886,7 +885,6 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
if( rc!=SQLITE_ROW ){
|
||||
rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg);
|
||||
pIter->zTbl = 0;
|
||||
pIter->zDataTbl = 0;
|
||||
}else{
|
||||
pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
|
||||
pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
|
||||
@ -2981,7 +2979,7 @@ static i64 rbuShmChecksum(sqlite3rbu *p){
|
||||
u32 volatile *ptr;
|
||||
p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]);
|
||||
iRet = ((i64)ptr[10] << 32) + ptr[11];
|
||||
}
|
||||
}
|
||||
return iRet;
|
||||
|
||||
@ -88,15 +88,6 @@ typedef unsigned int u32;
|
||||
|
||||
typedef struct DbdataTable DbdataTable;
|
||||
typedef struct DbdataCursor DbdataCursor;
|
||||
typedef struct DbdataBuffer DbdataBuffer;
|
||||
|
||||
/*
|
||||
** Buffer type.
|
||||
*/
|
||||
struct DbdataBuffer {
|
||||
u8 *aBuf;
|
||||
sqlite3_int64 nBuf;
|
||||
};
|
||||
|
||||
/* Cursor object */
|
||||
struct DbdataCursor {
|
||||
@ -113,7 +104,7 @@ struct DbdataCursor {
|
||||
sqlite3_int64 iRowid;
|
||||
|
||||
/* Only for the sqlite_dbdata table */
|
||||
DbdataBuffer rec;
|
||||
u8 *pRec; /* Buffer containing current record */
|
||||
sqlite3_int64 nRec; /* Size of pRec[] in bytes */
|
||||
sqlite3_int64 nHdr; /* Size of header in bytes */
|
||||
int iField; /* Current field number */
|
||||
@ -158,31 +149,6 @@ struct DbdataTable {
|
||||
" schema TEXT HIDDEN" \
|
||||
")"
|
||||
|
||||
/*
|
||||
** Ensure the buffer passed as the first argument is at least nMin bytes
|
||||
** in size. If an error occurs while attempting to resize the buffer,
|
||||
** SQLITE_NOMEM is returned. Otherwise, SQLITE_OK.
|
||||
*/
|
||||
static int dbdataBufferSize(DbdataBuffer *pBuf, sqlite3_int64 nMin){
|
||||
if( nMin>pBuf->nBuf ){
|
||||
sqlite3_int64 nNew = nMin+16384;
|
||||
u8 *aNew = (u8*)sqlite3_realloc64(pBuf->aBuf, nNew);
|
||||
|
||||
if( aNew==0 ) return SQLITE_NOMEM;
|
||||
pBuf->aBuf = aNew;
|
||||
pBuf->nBuf = nNew;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release the allocation managed by buffer pBuf.
|
||||
*/
|
||||
static void dbdataBufferFree(DbdataBuffer *pBuf){
|
||||
sqlite3_free(pBuf->aBuf);
|
||||
memset(pBuf, 0, sizeof(*pBuf));
|
||||
}
|
||||
|
||||
/*
|
||||
** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual
|
||||
** table.
|
||||
@ -323,9 +289,9 @@ static void dbdataResetCursor(DbdataCursor *pCsr){
|
||||
pCsr->iField = 0;
|
||||
pCsr->bOnePage = 0;
|
||||
sqlite3_free(pCsr->aPage);
|
||||
dbdataBufferFree(&pCsr->rec);
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->pRec = 0;
|
||||
pCsr->aPage = 0;
|
||||
pCsr->nRec = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -467,74 +433,62 @@ static void dbdataValue(
|
||||
u8 *pData,
|
||||
sqlite3_int64 nData
|
||||
){
|
||||
if( eType>=0 ){
|
||||
if( dbdataValueBytes(eType)<=nData ){
|
||||
switch( eType ){
|
||||
case 0:
|
||||
case 10:
|
||||
case 11:
|
||||
sqlite3_result_null(pCtx);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
sqlite3_result_int(pCtx, 0);
|
||||
break;
|
||||
case 9:
|
||||
sqlite3_result_int(pCtx, 1);
|
||||
break;
|
||||
|
||||
case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
|
||||
sqlite3_uint64 v = (signed char)pData[0];
|
||||
pData++;
|
||||
switch( eType ){
|
||||
case 7:
|
||||
case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 4: v = (v<<8) + pData[0]; pData++;
|
||||
case 3: v = (v<<8) + pData[0]; pData++;
|
||||
case 2: v = (v<<8) + pData[0]; pData++;
|
||||
}
|
||||
|
||||
if( eType==7 ){
|
||||
double r;
|
||||
memcpy(&r, &v, sizeof(r));
|
||||
sqlite3_result_double(pCtx, r);
|
||||
}else{
|
||||
sqlite3_result_int64(pCtx, (sqlite3_int64)v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int n = ((eType-12) / 2);
|
||||
if( eType % 2 ){
|
||||
switch( enc ){
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
case SQLITE_UTF16BE:
|
||||
sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
|
||||
break;
|
||||
case SQLITE_UTF16LE:
|
||||
sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if( eType==7 ){
|
||||
sqlite3_result_double(pCtx, 0.0);
|
||||
}else if( eType<7 ){
|
||||
if( eType>=0 && dbdataValueBytes(eType)<=nData ){
|
||||
switch( eType ){
|
||||
case 0:
|
||||
case 10:
|
||||
case 11:
|
||||
sqlite3_result_null(pCtx);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
sqlite3_result_int(pCtx, 0);
|
||||
}else if( eType%2 ){
|
||||
sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC);
|
||||
}else{
|
||||
sqlite3_result_blob(pCtx, "", 0, SQLITE_STATIC);
|
||||
break;
|
||||
case 9:
|
||||
sqlite3_result_int(pCtx, 1);
|
||||
break;
|
||||
|
||||
case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
|
||||
sqlite3_uint64 v = (signed char)pData[0];
|
||||
pData++;
|
||||
switch( eType ){
|
||||
case 7:
|
||||
case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 4: v = (v<<8) + pData[0]; pData++;
|
||||
case 3: v = (v<<8) + pData[0]; pData++;
|
||||
case 2: v = (v<<8) + pData[0]; pData++;
|
||||
}
|
||||
|
||||
if( eType==7 ){
|
||||
double r;
|
||||
memcpy(&r, &v, sizeof(r));
|
||||
sqlite3_result_double(pCtx, r);
|
||||
}else{
|
||||
sqlite3_result_int64(pCtx, (sqlite3_int64)v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int n = ((eType-12) / 2);
|
||||
if( eType % 2 ){
|
||||
switch( enc ){
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
case SQLITE_UTF16BE:
|
||||
sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
|
||||
break;
|
||||
case SQLITE_UTF16LE:
|
||||
sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -597,8 +551,7 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
}
|
||||
}else{
|
||||
/* If there is no record loaded, load it now. */
|
||||
assert( pCsr->rec.aBuf!=0 || pCsr->nRec==0 );
|
||||
if( pCsr->nRec==0 ){
|
||||
if( pCsr->pRec==0 ){
|
||||
int bHasRowid = 0;
|
||||
int nPointer = 0;
|
||||
sqlite3_int64 nPayload = 0;
|
||||
@ -642,7 +595,6 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
}else{
|
||||
iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload);
|
||||
if( nPayload>0x7fffff00 ) nPayload &= 0x3fff;
|
||||
if( nPayload==0 ) nPayload = 1;
|
||||
}
|
||||
|
||||
/* If this is a leaf intkey cell, load the rowid */
|
||||
@ -677,12 +629,13 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
/* Allocate space for payload. And a bit more to catch small buffer
|
||||
** overruns caused by attempting to read a varint or similar from
|
||||
** near the end of a corrupt record. */
|
||||
rc = dbdataBufferSize(&pCsr->rec, nPayload+DBDATA_PADDING_BYTES);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
assert( nPayload!=0 );
|
||||
pCsr->pRec = (u8*)sqlite3_malloc64(nPayload+DBDATA_PADDING_BYTES);
|
||||
if( pCsr->pRec==0 ) return SQLITE_NOMEM;
|
||||
memset(pCsr->pRec, 0, nPayload+DBDATA_PADDING_BYTES);
|
||||
pCsr->nRec = nPayload;
|
||||
|
||||
/* Load the nLocal bytes of payload */
|
||||
memcpy(pCsr->rec.aBuf, &pCsr->aPage[iOff], nLocal);
|
||||
memcpy(pCsr->pRec, &pCsr->aPage[iOff], nLocal);
|
||||
iOff += nLocal;
|
||||
|
||||
/* Load content from overflow pages */
|
||||
@ -700,22 +653,19 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
|
||||
nCopy = U-4;
|
||||
if( nCopy>nRem ) nCopy = nRem;
|
||||
memcpy(&pCsr->rec.aBuf[nPayload-nRem], &aOvfl[4], nCopy);
|
||||
memcpy(&pCsr->pRec[nPayload-nRem], &aOvfl[4], nCopy);
|
||||
nRem -= nCopy;
|
||||
|
||||
pgnoOvfl = get_uint32(aOvfl);
|
||||
sqlite3_free(aOvfl);
|
||||
}
|
||||
nPayload -= nRem;
|
||||
}
|
||||
memset(&pCsr->rec.aBuf[nPayload], 0, DBDATA_PADDING_BYTES);
|
||||
pCsr->nRec = nPayload;
|
||||
|
||||
iHdr = dbdataGetVarintU32(pCsr->rec.aBuf, &nHdr);
|
||||
iHdr = dbdataGetVarintU32(pCsr->pRec, &nHdr);
|
||||
if( nHdr>nPayload ) nHdr = 0;
|
||||
pCsr->nHdr = nHdr;
|
||||
pCsr->pHdrPtr = &pCsr->rec.aBuf[iHdr];
|
||||
pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nHdr];
|
||||
pCsr->pHdrPtr = &pCsr->pRec[iHdr];
|
||||
pCsr->pPtr = &pCsr->pRec[pCsr->nHdr];
|
||||
pCsr->iField = (bHasRowid ? -1 : 0);
|
||||
}
|
||||
}
|
||||
@ -723,7 +673,7 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
pCsr->iField++;
|
||||
if( pCsr->iField>0 ){
|
||||
sqlite3_int64 iType;
|
||||
if( pCsr->pHdrPtr>=&pCsr->rec.aBuf[pCsr->nRec]
|
||||
if( pCsr->pHdrPtr>=&pCsr->pRec[pCsr->nRec]
|
||||
|| pCsr->iField>=DBDATA_MX_FIELD
|
||||
){
|
||||
bNextPage = 1;
|
||||
@ -731,8 +681,8 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
int szField = 0;
|
||||
pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType);
|
||||
szField = dbdataValueBytes(iType);
|
||||
if( (pCsr->nRec - (pCsr->pPtr - pCsr->rec.aBuf))<szField ){
|
||||
pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nRec];
|
||||
if( (pCsr->nRec - (pCsr->pPtr - pCsr->pRec))<szField ){
|
||||
pCsr->pPtr = &pCsr->pRec[pCsr->nRec];
|
||||
}else{
|
||||
pCsr->pPtr += szField;
|
||||
}
|
||||
@ -742,18 +692,20 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
|
||||
if( bNextPage ){
|
||||
sqlite3_free(pCsr->aPage);
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->aPage = 0;
|
||||
pCsr->nRec = 0;
|
||||
pCsr->pRec = 0;
|
||||
if( pCsr->bOnePage ) return SQLITE_OK;
|
||||
pCsr->iPgno++;
|
||||
}else{
|
||||
if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->rec.aBuf[pCsr->nHdr] ){
|
||||
if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Advance to the next cell. The next iteration of the loop will load
|
||||
** the record and so on. */
|
||||
pCsr->nRec = 0;
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->pRec = 0;
|
||||
pCsr->iCell++;
|
||||
}
|
||||
}
|
||||
@ -943,12 +895,12 @@ static int dbdataColumn(
|
||||
case DBDATA_COLUMN_VALUE: {
|
||||
if( pCsr->iField<0 ){
|
||||
sqlite3_result_int64(ctx, pCsr->iIntkey);
|
||||
}else if( &pCsr->rec.aBuf[pCsr->nRec] >= pCsr->pPtr ){
|
||||
}else if( &pCsr->pRec[pCsr->nRec] >= pCsr->pPtr ){
|
||||
sqlite3_int64 iType;
|
||||
dbdataGetVarintU32(pCsr->pHdrPtr, &iType);
|
||||
dbdataValue(
|
||||
ctx, pCsr->enc, iType, pCsr->pPtr,
|
||||
&pCsr->rec.aBuf[pCsr->nRec] - pCsr->pPtr
|
||||
&pCsr->pRec[pCsr->nRec] - pCsr->pPtr
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -342,7 +342,7 @@ foreach enc {utf8 utf16 utf16le utf16be} {
|
||||
DELETE FROM sqlite_schema WHERE name='t1';
|
||||
}
|
||||
|
||||
proc my_sql_hook2 {sql} {
|
||||
proc my_sql_hook {sql} {
|
||||
if {[string match "INSERT INTO lostandfound*" $sql]} {
|
||||
lappend ::script $sql
|
||||
}
|
||||
@ -350,7 +350,7 @@ foreach enc {utf8 utf16 utf16le utf16be} {
|
||||
}
|
||||
do_test 18.$enc.2 {
|
||||
set ::script [list]
|
||||
set R [sqlite3_recover_init_sql db main my_sql_hook2]
|
||||
set R [sqlite3_recover_init_sql db main my_sql_hook]
|
||||
$R config lostandfound lostandfound
|
||||
$R run
|
||||
$R finish
|
||||
@ -358,18 +358,7 @@ foreach enc {utf8 utf16 utf16le utf16be} {
|
||||
} {{INSERT INTO lostandfound VALUES(2, 2, 2, 1, 'abc', 'def')}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 19.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
|
||||
ALTER TABLE t1 ADD COLUMN c NOT NULL DEFAULT 13;
|
||||
INSERT INTO t1 VALUES(3, 'three', 'hello world');
|
||||
}
|
||||
|
||||
do_recover_test 19.1
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,549 +0,0 @@
|
||||
# 2024 May 1
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
set testprefix recovercorrupt3
|
||||
|
||||
#| 0: d5 d5 9b d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 d5 ................
|
||||
#| 16: 04 00 00 00 1d 00 00 00 00 00 00 00 5f 5f 5f 5f ............____
|
||||
#| 32: 5f 5f 5f 5f 5f 5f 5f 5f 71 5f 5f 5f 02 02 02 02 ________q___....
|
||||
#| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
#| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
#| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
#| 96: 02 02 02 02
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 1.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 3821 pagesize 1024 filename clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................
|
||||
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ................
|
||||
| 96: 00 2e 7a 70 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 a0 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 29 29 ..............))
|
||||
| 464: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 480: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 496: 29 29 29 dd dd dd dd dd dd dd dd dd dd dd dd dd ))).............
|
||||
| 512: dd dd dd dd dd dd dd dd dd 6e 69 d2 e9 e9 e9 d2 .........ni.....
|
||||
| 528: d2 d2 d2 d2 dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 544: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 560: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 576: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 592: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 608: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 624: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 640: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 656: dd dd dd dd dd dd dd da dd dd dd dd dd dd dd dd ................
|
||||
| 672: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 688: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 704: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 720: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 736: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 752: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 768: dd dd dd dd dd dd dd dd dd dd dd dd dd 29 29 29 .............)))
|
||||
| 784: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 800: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 816: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 832: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 848: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 864: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 880: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 896: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 912: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 928: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 944: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 960: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 976: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 992: 29 29 29 29 29 29 29 29 29 29 dd dd dd dd dd dd ))))))))))......
|
||||
| 1008: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| page 2 offset 1024
|
||||
| 0: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 16: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 32: dd dd 6e 69 d2 e9 e9 e9 d2 d2 d2 d2 d2 dd dd dd ..ni............
|
||||
| 48: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 64: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 80: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 96: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 112: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 128: dd dd dd dd dd dd 29 29 29 29 29 29 29 29 29 29 ......))))))))))
|
||||
| 144: 29 29 29 29 29 29 29 29 29 ad a5 29 29 29 29 00 )))))))))..)))).
|
||||
| 160: 75 9c 11 00 5b e5 64 28 7c ca 09 69 28 2d 69 00 u...[.d(|..i(-i.
|
||||
| 176: 85 88 6c 81 48 83 a0 93 c0 c0 82 8b 81 84 85 f9 ..l.H...........
|
||||
| 192: 88 7a 00 7f 00 96 40 7b 12 4b 84 75 a0 00 99 a0 .z....@..K.u....
|
||||
| 208: df a0 7e 81 c6 90 8f 7f 84 85 cc 84 82 90 88 60 ..~............`
|
||||
| 224: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 240: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 256: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 272: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 288: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 832: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 848: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 864: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 880: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 896: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 912: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 928: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 944: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 960: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| page 3 offset 2048
|
||||
| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 832: 02 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 848: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 864: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 880: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 896: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 912: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 928: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 944: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 960: 80 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| page 4 offset 3072
|
||||
| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 736: 5f 5f 5f 5f 5f 5f 5f 00 d5 fe fe fe 08 00 00 00 _______.........
|
||||
| end clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056
|
||||
}]} {}
|
||||
|
||||
sqlite3_dbdata_init db
|
||||
do_execsql_test 1.1 {
|
||||
PRAGMA writable_schema = 1;
|
||||
}
|
||||
|
||||
do_test 1.2 {
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
$R run
|
||||
$R finish
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 2.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
| size 3821 pagesize 1024 filename clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 04 00 01 01 00 40 20 20 00 00 00 01 00 00 00 02 .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................
|
||||
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ................
|
||||
| 96: 00 2e 7a 70 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 a0 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 29 29 ..............))
|
||||
| 464: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 480: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 496: 29 29 29 dd dd dd dd dd dd dd dd dd dd dd dd dd ))).............
|
||||
| 512: dd dd dd dd dd dd dd dd dd 6e 69 d2 e9 e9 e9 d2 .........ni.....
|
||||
| 528: d2 d2 d2 d2 dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 544: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 560: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 576: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 592: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 608: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 624: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 640: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 656: dd dd dd dd dd dd dd da dd dd dd dd dd dd dd dd ................
|
||||
| 672: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 688: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 704: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 720: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 736: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 752: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 768: dd dd dd dd dd dd dd dd dd dd dd dd dd 29 29 29 .............)))
|
||||
| 784: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 800: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 816: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 832: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 848: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 864: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 880: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 896: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 912: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 928: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 944: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 960: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 976: 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 ))))))))))))))))
|
||||
| 992: 29 29 29 29 29 29 29 29 29 29 dd dd dd dd dd dd ))))))))))......
|
||||
| 1008: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| page 2 offset 1024
|
||||
| 0: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 16: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 32: dd dd 6e 69 d2 e9 e9 e9 d2 d2 d2 d2 d2 dd dd dd ..ni............
|
||||
| 48: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 64: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 80: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 96: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 112: dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ................
|
||||
| 128: dd dd dd dd dd dd 29 29 29 29 29 29 29 29 29 29 ......))))))))))
|
||||
| 144: 29 29 29 29 29 29 29 29 29 ad a5 29 29 29 29 00 )))))))))..)))).
|
||||
| 160: 75 9c 11 00 5b e5 64 28 7c ca 09 69 28 2d 69 00 u...[.d(|..i(-i.
|
||||
| 176: 85 88 6c 81 48 83 a0 93 c0 c0 82 8b 81 84 85 f9 ..l.H...........
|
||||
| 192: 88 7a 00 7f 00 96 40 7b 12 4b 84 75 a0 00 99 a0 .z....@..K.u....
|
||||
| 208: df a0 7e 81 c6 90 8f 7f 84 85 cc 84 82 90 88 60 ..~............`
|
||||
| 224: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 240: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 256: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 272: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 288: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 832: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 848: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 864: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 880: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 896: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 912: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 928: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 944: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 960: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| page 3 offset 2048
|
||||
| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 736: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 752: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 768: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 784: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 800: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 816: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 832: 02 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 848: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 864: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 880: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 896: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 912: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 928: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 944: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 960: 80 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 976: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 992: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 1008: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| page 4 offset 3072
|
||||
| 0: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 16: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 32: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 48: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 64: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 80: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 96: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 112: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 128: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 144: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 160: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 176: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 192: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 208: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 224: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 240: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 256: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 272: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 288: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 304: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 320: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 336: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 352: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 368: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 384: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 400: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 416: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 432: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 448: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 464: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 480: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 496: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 512: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 528: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 544: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 560: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 576: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 592: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 608: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 624: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 640: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 656: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 672: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 688: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 704: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 720: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 736: 5f 5f 5f 5f 5f 5f 5f 00 d5 fe fe fe 08 00 00 00 _______.........
|
||||
| end clusterfuzz-testcase-sql_recovery_fuzzer-5803962339885056
|
||||
}]} {}
|
||||
|
||||
sqlite3_dbdata_init db
|
||||
do_execsql_test 2.1 {
|
||||
PRAGMA writable_schema = 1;
|
||||
}
|
||||
|
||||
do_test 2.2 {
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
$R run
|
||||
$R finish
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
# 2024 May 15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
set testprefix recovercorrupt4
|
||||
|
||||
database_may_be_corrupt
|
||||
|
||||
if {[permutation]!="inmemory_journal"} {
|
||||
# This test cannot be run with the inmemory_journal permutation, as it
|
||||
# must open a truncated, corrupt, database file. With the inmemory_journal
|
||||
# permutation, this fails (SQLITE_CORRUPT error) when the [sqlite3] wrapper
|
||||
# executes "PRAGMA journal_mode = memory".
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL, filler BLOB NOT NULL DEFAULT 13);
|
||||
-- CREATE UNIQUE INDEX rows_index ON rows(indexed);
|
||||
INSERT INTO rows(indexed, unindexed, filler) VALUES(1, 1, x'31');
|
||||
INSERT INTO rows(indexed, unindexed, filler) VALUES(2, 2, x'32');
|
||||
INSERT INTO rows(indexed, unindexed, filler) VALUES(4, 4, x'34');
|
||||
INSERT INTO rows(indexed, unindexed, filler) VALUES(8, 8, randomblob(2048));
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
do_test 1.1 {
|
||||
set sz [expr [file size test.db] - 1024]
|
||||
set fd [open test.db]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
|
||||
set data [read $fd $sz]
|
||||
set fd2 [open test.db2 w]
|
||||
fconfigure $fd2 -encoding binary -translation binary
|
||||
puts -nonewline $fd2 $data
|
||||
close $fd2
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
do_test 1.2 {
|
||||
forcedelete test.db3
|
||||
sqlite3 db test.db2
|
||||
set R [sqlite3_recover_init db main test.db3]
|
||||
$R run
|
||||
$R finish
|
||||
} {}
|
||||
|
||||
do_test 1.3 {
|
||||
sqlite3 db test.db3
|
||||
execsql {
|
||||
SELECT indexed, unindexed FROM rows
|
||||
}
|
||||
} {1 1 2 2 4 4 8 8}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -363,8 +363,8 @@ static int recoverError(
|
||||
va_start(ap, zFmt);
|
||||
if( zFmt ){
|
||||
z = sqlite3_vmprintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
va_end(ap);
|
||||
sqlite3_free(p->zErrMsg);
|
||||
p->zErrMsg = z;
|
||||
p->errCode = errCode;
|
||||
|
||||
@ -1841,8 +1841,6 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite3IntFloatCompare(i64,double);
|
||||
|
||||
/*
|
||||
** Rtree virtual table module xFilter method.
|
||||
*/
|
||||
@ -1872,8 +1870,7 @@ static int rtreeFilter(
|
||||
i64 iNode = 0;
|
||||
int eType = sqlite3_value_numeric_type(argv[0]);
|
||||
if( eType==SQLITE_INTEGER
|
||||
|| (eType==SQLITE_FLOAT
|
||||
&& 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0])))
|
||||
|| (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid)
|
||||
){
|
||||
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
|
||||
}else{
|
||||
@ -3228,7 +3225,6 @@ constraint:
|
||||
*/
|
||||
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
|
||||
Rtree *pRtree = (Rtree *)pVtab;
|
||||
assert( pRtree->inWrTrans==0 );
|
||||
pRtree->inWrTrans = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -797,22 +797,4 @@ do_test 23.0 {
|
||||
db eval {PRAGMA integrity_check;}
|
||||
} {ok}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 24.0 {
|
||||
CREATE VIRTUAL TABLE rt1 USING rtree_i32(rid, c1, c2);
|
||||
INSERT INTO rt1(rid, c1, c2) VALUES (9223372036854775807, 10, 18);
|
||||
}
|
||||
|
||||
do_execsql_test 24.1 {
|
||||
SELECT (rid = (CAST (9223372036854775807 AS REAL)))
|
||||
FROM rt1 WHERE
|
||||
(rid = (CAST (9223372036854775807 AS REAL)));
|
||||
}
|
||||
|
||||
do_execsql_test 24.2 {
|
||||
DELETE FROM rt1;
|
||||
INSERT INTO rt1(rid, c1, c2) VALUES(1,2,3);
|
||||
SELECT * FROM rt1 WHERE rid=1.005;
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@ -1,101 +0,0 @@
|
||||
# 2011 March 07
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] session_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix sessionchange
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
set C [changeset_from_sql {
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(10, 20, 30);
|
||||
}]
|
||||
|
||||
set res [list]
|
||||
set iter [sqlite3changeset_start $C]
|
||||
while {[$iter next]=="SQLITE_ROW"} {
|
||||
lappend res [$iter data]
|
||||
}
|
||||
$iter finalize
|
||||
|
||||
set res
|
||||
} [list \
|
||||
{INSERT t1 0 X.. {} {i 10 i 20 i 30}} \
|
||||
{INSERT t1 0 X.. {} {i 1 i 2 i 3}} \
|
||||
]
|
||||
|
||||
|
||||
do_test 1.2 {
|
||||
sqlite3changegroup grp
|
||||
set iter [sqlite3changeset_start $C]
|
||||
while {[$iter next]=="SQLITE_ROW"} {
|
||||
grp add_change $iter
|
||||
}
|
||||
$iter finalize
|
||||
|
||||
set res [list]
|
||||
grp output
|
||||
sqlite3session_foreach c [grp output] { lappend res $c }
|
||||
grp delete
|
||||
set res
|
||||
} [list \
|
||||
{INSERT t1 0 X.. {} {i 10 i 20 i 30}} \
|
||||
{INSERT t1 0 X.. {} {i 1 i 2 i 3}} \
|
||||
]
|
||||
|
||||
do_test 1.3.1 {
|
||||
set iter [sqlite3changeset_start $C]
|
||||
sqlite3changegroup grp
|
||||
list [catch { grp add_change $iter } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test 1.3.2 {
|
||||
while {[$iter next]=="SQLITE_ROW"} { }
|
||||
list [catch { grp add_change $iter } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
grp delete
|
||||
$iter finalize
|
||||
|
||||
do_test 1.4 {
|
||||
set res [list]
|
||||
set iter [sqlite3changeset_start -invert $C]
|
||||
while {[$iter next]=="SQLITE_ROW"} {
|
||||
lappend res [$iter data]
|
||||
}
|
||||
$iter finalize
|
||||
set res
|
||||
} [list \
|
||||
{DELETE t1 0 X.. {i 10 i 20 i 30} {}} \
|
||||
{DELETE t1 0 X.. {i 1 i 2 i 3} {}} \
|
||||
]
|
||||
|
||||
do_test 1.5 {
|
||||
sqlite3changegroup grp
|
||||
set iter [sqlite3changeset_start -invert $C]
|
||||
$iter next
|
||||
list [catch { grp add_change $iter } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
$iter finalize
|
||||
grp delete
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@ -1,76 +0,0 @@
|
||||
# 2011 March 07
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] session_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix sessionconflict
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
test_sqlite3_log log
|
||||
proc log {code msg} { puts "LOG $code $msg" }
|
||||
sqlite3 db test.db
|
||||
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_test 1.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, 1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db2 1.1 {
|
||||
INSERT INTO t1 VALUES(6, 6, 6);
|
||||
}
|
||||
|
||||
proc xConflict {args} {
|
||||
return "ABORT"
|
||||
}
|
||||
|
||||
|
||||
do_test 1.2 {
|
||||
set chng [changeset_from_sql {
|
||||
UPDATE t1 SET b=10, c=10 WHERE a=1;
|
||||
UPDATE t1 SET b=444 WHERE a=2;
|
||||
INSERT INTO t1 VALUES(4, 4, 4);
|
||||
INSERT INTO t1 VALUES(5, 5, 5);
|
||||
INSERT INTO t1 VALUES(6, 6, 6);
|
||||
}]
|
||||
|
||||
execsql BEGIN db2
|
||||
set res [list [catch { sqlite3changeset_apply db2 $chng xConflict } msg] $msg]
|
||||
execsql ROLLBACK db2
|
||||
set res
|
||||
} {1 SQLITE_ABORT}
|
||||
|
||||
do_execsql_test -db db2 1.3 {
|
||||
SELECT * FROM t1;
|
||||
} {
|
||||
1 1 1
|
||||
2 2 2
|
||||
3 3 3
|
||||
6 6 6
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@ -92,7 +92,7 @@ do_test 2.1 {
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db2 2.2 {
|
||||
SELECT * FROM sqlite_stat1 ORDER BY tbl, idx
|
||||
SELECT * FROM sqlite_stat1
|
||||
} {
|
||||
t1 sqlite_autoindex_t1_1 {32 1}
|
||||
t1 t1b {32 4}
|
||||
@ -104,7 +104,7 @@ do_test 2.3 {
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db2 2.4 {
|
||||
SELECT * FROM sqlite_stat1 ORDER BY tbl, idx;
|
||||
SELECT * FROM sqlite_stat1
|
||||
} {
|
||||
t1 sqlite_autoindex_t1_1 {32 1}
|
||||
t1 t1b {32 4}
|
||||
|
||||
@ -3685,14 +3685,14 @@ static int sessionChangesetNextOne(
|
||||
p->rc = sessionInputBuffer(&p->in, 2);
|
||||
if( p->rc!=SQLITE_OK ) return p->rc;
|
||||
|
||||
sessionDiscardData(&p->in);
|
||||
p->in.iCurrent = p->in.iNext;
|
||||
|
||||
/* If the iterator is already at the end of the changeset, return DONE. */
|
||||
if( p->in.iNext>=p->in.nData ){
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
sessionDiscardData(&p->in);
|
||||
p->in.iCurrent = p->in.iNext;
|
||||
|
||||
op = p->in.aData[p->in.iNext++];
|
||||
while( op=='T' || op=='P' ){
|
||||
if( pbNew ) *pbNew = 1;
|
||||
@ -5427,7 +5427,6 @@ struct sqlite3_changegroup {
|
||||
int rc; /* Error code */
|
||||
int bPatch; /* True to accumulate patchsets */
|
||||
SessionTable *pList; /* List of tables in current patch */
|
||||
SessionBuffer rec;
|
||||
|
||||
sqlite3 *db; /* Configured by changegroup_schema() */
|
||||
char *zDb; /* Configured by changegroup_schema() */
|
||||
@ -5726,128 +5725,108 @@ static int sessionChangesetExtendRecord(
|
||||
}
|
||||
|
||||
/*
|
||||
** Locate or create a SessionTable object that may be used to add the
|
||||
** change currently pointed to by iterator pIter to changegroup pGrp.
|
||||
** If successful, set output variable (*ppTab) to point to the table
|
||||
** object and return SQLITE_OK. Otherwise, if some error occurs, return
|
||||
** an SQLite error code and leave (*ppTab) set to NULL.
|
||||
** Add all changes in the changeset traversed by the iterator passed as
|
||||
** the first argument to the changegroup hash tables.
|
||||
*/
|
||||
static int sessionChangesetFindTable(
|
||||
sqlite3_changegroup *pGrp,
|
||||
const char *zTab,
|
||||
sqlite3_changeset_iter *pIter,
|
||||
SessionTable **ppTab
|
||||
static int sessionChangesetToHash(
|
||||
sqlite3_changeset_iter *pIter, /* Iterator to read from */
|
||||
sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
|
||||
int bRebase /* True if hash table is for rebasing */
|
||||
){
|
||||
u8 *aRec;
|
||||
int nRec;
|
||||
int rc = SQLITE_OK;
|
||||
SessionTable *pTab = 0;
|
||||
int nTab = (int)strlen(zTab);
|
||||
u8 *abPK = 0;
|
||||
int nCol = 0;
|
||||
SessionBuffer rec = {0, 0, 0};
|
||||
|
||||
*ppTab = 0;
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol);
|
||||
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
|
||||
const char *zNew;
|
||||
int nCol;
|
||||
int op;
|
||||
int iHash;
|
||||
int bIndirect;
|
||||
SessionChange *pChange;
|
||||
SessionChange *pExist = 0;
|
||||
SessionChange **pp;
|
||||
|
||||
/* Search the list for an existing table */
|
||||
for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
|
||||
if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
|
||||
}
|
||||
|
||||
/* If one was not found above, create a new table now */
|
||||
if( !pTab ){
|
||||
SessionTable **ppNew;
|
||||
|
||||
pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1);
|
||||
if( !pTab ){
|
||||
return SQLITE_NOMEM;
|
||||
/* Ensure that only changesets, or only patchsets, but not a mixture
|
||||
** of both, are being combined. It is an error to try to combine a
|
||||
** changeset and a patchset. */
|
||||
if( pGrp->pList==0 ){
|
||||
pGrp->bPatch = pIter->bPatchset;
|
||||
}else if( pIter->bPatchset!=pGrp->bPatch ){
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
memset(pTab, 0, sizeof(SessionTable));
|
||||
pTab->nCol = nCol;
|
||||
pTab->abPK = (u8*)&pTab[1];
|
||||
memcpy(pTab->abPK, abPK, nCol);
|
||||
pTab->zName = (char*)&pTab->abPK[nCol];
|
||||
memcpy(pTab->zName, zTab, nTab+1);
|
||||
|
||||
if( pGrp->db ){
|
||||
pTab->nCol = 0;
|
||||
rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
|
||||
if( rc ){
|
||||
assert( pTab->azCol==0 );
|
||||
sqlite3_free(pTab);
|
||||
return rc;
|
||||
sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
|
||||
if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){
|
||||
/* Search the list for a matching table */
|
||||
int nNew = (int)strlen(zNew);
|
||||
u8 *abPK;
|
||||
|
||||
sqlite3changeset_pk(pIter, &abPK, 0);
|
||||
for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
|
||||
if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
|
||||
}
|
||||
if( !pTab ){
|
||||
SessionTable **ppTab;
|
||||
|
||||
pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1);
|
||||
if( !pTab ){
|
||||
rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
memset(pTab, 0, sizeof(SessionTable));
|
||||
pTab->nCol = nCol;
|
||||
pTab->abPK = (u8*)&pTab[1];
|
||||
memcpy(pTab->abPK, abPK, nCol);
|
||||
pTab->zName = (char*)&pTab->abPK[nCol];
|
||||
memcpy(pTab->zName, zNew, nNew+1);
|
||||
|
||||
if( pGrp->db ){
|
||||
pTab->nCol = 0;
|
||||
rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
|
||||
if( rc ){
|
||||
assert( pTab->azCol==0 );
|
||||
sqlite3_free(pTab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* The new object must be linked on to the end of the list, not
|
||||
** simply added to the start of it. This is to ensure that the
|
||||
** tables within the output of sqlite3changegroup_output() are in
|
||||
** the right order. */
|
||||
for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
|
||||
*ppTab = pTab;
|
||||
}
|
||||
|
||||
if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
|
||||
rc = SQLITE_SCHEMA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* The new object must be linked on to the end of the list, not
|
||||
** simply added to the start of it. This is to ensure that the
|
||||
** tables within the output of sqlite3changegroup_output() are in
|
||||
** the right order. */
|
||||
for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext);
|
||||
*ppNew = pTab;
|
||||
}
|
||||
if( nCol<pTab->nCol ){
|
||||
assert( pGrp->db );
|
||||
rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec);
|
||||
if( rc ) break;
|
||||
aRec = rec.aBuf;
|
||||
nRec = rec.nBuf;
|
||||
}
|
||||
|
||||
/* Check that the table is compatible. */
|
||||
if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
|
||||
rc = SQLITE_SCHEMA;
|
||||
}
|
||||
|
||||
*ppTab = pTab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the change currently indicated by iterator pIter to the hash table
|
||||
** belonging to changegroup pGrp.
|
||||
*/
|
||||
static int sessionOneChangeToHash(
|
||||
sqlite3_changegroup *pGrp,
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int bRebase
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int nCol = 0;
|
||||
int op = 0;
|
||||
int iHash = 0;
|
||||
int bIndirect = 0;
|
||||
SessionChange *pChange = 0;
|
||||
SessionChange *pExist = 0;
|
||||
SessionChange **pp = 0;
|
||||
SessionTable *pTab = 0;
|
||||
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
|
||||
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
|
||||
|
||||
/* Ensure that only changesets, or only patchsets, but not a mixture
|
||||
** of both, are being combined. It is an error to try to combine a
|
||||
** changeset and a patchset. */
|
||||
if( pGrp->pList==0 ){
|
||||
pGrp->bPatch = pIter->bPatchset;
|
||||
}else if( pIter->bPatchset!=pGrp->bPatch ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zTab = 0;
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && nCol<pTab->nCol ){
|
||||
SessionBuffer *pBuf = &pGrp->rec;
|
||||
rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
|
||||
aRec = pBuf->aBuf;
|
||||
nRec = pBuf->nBuf;
|
||||
assert( pGrp->db );
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
/* Search for existing entry. If found, remove it from the hash table.
|
||||
** Code below may link it back in. */
|
||||
if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
|
||||
rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
iHash = sessionChangeHash(
|
||||
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
|
||||
);
|
||||
|
||||
/* Search for existing entry. If found, remove it from the hash table.
|
||||
** Code below may link it back in.
|
||||
*/
|
||||
for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
|
||||
int bPkOnly1 = 0;
|
||||
int bPkOnly2 = 0;
|
||||
@ -5862,41 +5841,19 @@ static int sessionOneChangeToHash(
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangeMerge(pTab, bRebase,
|
||||
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && pChange ){
|
||||
pChange->pNext = pTab->apChange[iHash];
|
||||
pTab->apChange[iHash] = pChange;
|
||||
pTab->nEntry++;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ) rc = pIter->rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add all changes in the changeset traversed by the iterator passed as
|
||||
** the first argument to the changegroup hash tables.
|
||||
*/
|
||||
static int sessionChangesetToHash(
|
||||
sqlite3_changeset_iter *pIter, /* Iterator to read from */
|
||||
sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
|
||||
int bRebase /* True if hash table is for rebasing */
|
||||
){
|
||||
u8 *aRec;
|
||||
int nRec;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
|
||||
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
if( rc ) break;
|
||||
if( pChange ){
|
||||
pChange->pNext = pTab->apChange[iHash];
|
||||
pTab->apChange[iHash] = pChange;
|
||||
pTab->nEntry++;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(rec.aBuf);
|
||||
if( rc==SQLITE_OK ) rc = pIter->rc;
|
||||
return rc;
|
||||
}
|
||||
@ -6024,23 +5981,6 @@ int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a single change to a changeset-group.
|
||||
*/
|
||||
int sqlite3changegroup_add_change(
|
||||
sqlite3_changegroup *pGrp,
|
||||
sqlite3_changeset_iter *pIter
|
||||
){
|
||||
if( pIter->in.iCurrent==pIter->in.iNext
|
||||
|| pIter->rc!=SQLITE_OK
|
||||
|| pIter->bInvert
|
||||
){
|
||||
/* Iterator does not point to any valid entry or is an INVERT iterator. */
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return sessionOneChangeToHash(pGrp, pIter, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain a buffer containing a changeset representing the concatenation
|
||||
** of all changesets added to the group so far.
|
||||
@ -6090,7 +6030,6 @@ void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
|
||||
if( pGrp ){
|
||||
sqlite3_free(pGrp->zDb);
|
||||
sessionDeleteTable(0, pGrp->pList);
|
||||
sqlite3_free(pGrp->rec.aBuf);
|
||||
sqlite3_free(pGrp);
|
||||
}
|
||||
}
|
||||
@ -6492,7 +6431,6 @@ int sqlite3rebaser_rebase_strm(
|
||||
void sqlite3rebaser_delete(sqlite3_rebaser *p){
|
||||
if( p ){
|
||||
sessionDeleteTable(0, p->grp.pList);
|
||||
sqlite3_free(p->grp.rec.aBuf);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1057,30 +1057,6 @@ int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
|
||||
*/
|
||||
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Add A Single Change To A Changegroup
|
||||
** METHOD: sqlite3_changegroup
|
||||
**
|
||||
** This function adds the single change currently indicated by the iterator
|
||||
** passed as the second argument to the changegroup object. The rules for
|
||||
** adding the change are just as described for [sqlite3changegroup_add()].
|
||||
**
|
||||
** If the change is successfully added to the changegroup, SQLITE_OK is
|
||||
** returned. Otherwise, an SQLite error code is returned.
|
||||
**
|
||||
** The iterator must point to a valid entry when this function is called.
|
||||
** If it does not, SQLITE_ERROR is returned and no change is added to the
|
||||
** changegroup. Additionally, the iterator must not have been opened with
|
||||
** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also
|
||||
** returned.
|
||||
*/
|
||||
int sqlite3changegroup_add_change(
|
||||
sqlite3_changegroup*,
|
||||
sqlite3_changeset_iter*
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
|
||||
** METHOD: sqlite3_changegroup
|
||||
|
||||
@ -1038,64 +1038,6 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
|
||||
Tcl_Obj *pVar = 0;
|
||||
int nCol; /* Number of columns in table */
|
||||
int nCol2; /* Number of columns in table */
|
||||
int op; /* SQLITE_INSERT, UPDATE or DELETE */
|
||||
const char *zTab; /* Name of table change applies to */
|
||||
Tcl_Obj *pOld; /* Vector of old.* values */
|
||||
Tcl_Obj *pNew; /* Vector of new.* values */
|
||||
int bIndirect;
|
||||
|
||||
char *zPK;
|
||||
unsigned char *abPK;
|
||||
int i;
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
pVar = Tcl_NewObj();
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
|
||||
op==SQLITE_INSERT ? "INSERT" :
|
||||
op==SQLITE_UPDATE ? "UPDATE" :
|
||||
"DELETE", -1
|
||||
));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
|
||||
|
||||
zPK = ckalloc(nCol+1);
|
||||
memset(zPK, 0, nCol+1);
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol2);
|
||||
assert( nCol==nCol2 );
|
||||
for(i=0; i<nCol; i++){
|
||||
zPK[i] = (abPK[i] ? 'X' : '.');
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
|
||||
ckfree(zPK);
|
||||
|
||||
pOld = Tcl_NewObj();
|
||||
if( op!=SQLITE_INSERT ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
test_append_value(pOld, pVal);
|
||||
}
|
||||
}
|
||||
pNew = Tcl_NewObj();
|
||||
if( op!=SQLITE_DELETE ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
test_append_value(pNew, pVal);
|
||||
}
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, pOld);
|
||||
Tcl_ListObjAppendElement(0, pVar, pNew);
|
||||
|
||||
return pVar;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
|
||||
*/
|
||||
@ -1169,8 +1111,67 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
}
|
||||
|
||||
while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
|
||||
Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */
|
||||
pVar = testIterData(pIter);
|
||||
int nCol; /* Number of columns in table */
|
||||
int nCol2; /* Number of columns in table */
|
||||
int op; /* SQLITE_INSERT, UPDATE or DELETE */
|
||||
const char *zTab; /* Name of table change applies to */
|
||||
Tcl_Obj *pVar; /* Tcl value to set $VARNAME to */
|
||||
Tcl_Obj *pOld; /* Vector of old.* values */
|
||||
Tcl_Obj *pNew; /* Vector of new.* values */
|
||||
int bIndirect;
|
||||
|
||||
char *zPK;
|
||||
unsigned char *abPK;
|
||||
int i;
|
||||
|
||||
/* Test that _fk_conflicts() returns SQLITE_MISUSE if called on this
|
||||
** iterator. */
|
||||
int nDummy;
|
||||
if( SQLITE_MISUSE!=sqlite3changeset_fk_conflicts(pIter, &nDummy) ){
|
||||
sqlite3changeset_finalize(pIter);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
pVar = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
|
||||
op==SQLITE_INSERT ? "INSERT" :
|
||||
op==SQLITE_UPDATE ? "UPDATE" :
|
||||
"DELETE", -1
|
||||
));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
|
||||
|
||||
zPK = ckalloc(nCol+1);
|
||||
memset(zPK, 0, nCol+1);
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol2);
|
||||
assert( nCol==nCol2 );
|
||||
for(i=0; i<nCol; i++){
|
||||
zPK[i] = (abPK[i] ? 'X' : '.');
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
|
||||
ckfree(zPK);
|
||||
|
||||
pOld = Tcl_NewObj();
|
||||
if( op!=SQLITE_INSERT ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
test_append_value(pOld, pVal);
|
||||
}
|
||||
}
|
||||
pNew = Tcl_NewObj();
|
||||
if( op!=SQLITE_DELETE ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
test_append_value(pNew, pVal);
|
||||
}
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, pOld);
|
||||
Tcl_ListObjAppendElement(0, pVar, pNew);
|
||||
|
||||
Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
|
||||
@ -1458,12 +1459,6 @@ struct TestChangegroup {
|
||||
sqlite3_changegroup *pGrp;
|
||||
};
|
||||
|
||||
typedef struct TestChangeIter TestChangeIter;
|
||||
struct TestChangeIter {
|
||||
sqlite3_changeset_iter *pIter;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Destructor for Tcl changegroup command object.
|
||||
*/
|
||||
@ -1496,7 +1491,6 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
|
||||
{ "add", 1, "CHANGESET", }, /* 1 */
|
||||
{ "output", 0, "", }, /* 2 */
|
||||
{ "delete", 0, "", }, /* 3 */
|
||||
{ "add_change", 1, "ITERATOR", }, /* 4 */
|
||||
{ 0 }
|
||||
};
|
||||
int rc = TCL_OK;
|
||||
@ -1548,24 +1542,6 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
|
||||
break;
|
||||
};
|
||||
|
||||
case 4: { /* add_change */
|
||||
Tcl_CmdInfo cmdInfo; /* Database Tcl command (objv[2]) info */
|
||||
TestChangeIter *pIter = 0;
|
||||
const char *zIter = Tcl_GetString(objv[2]);
|
||||
if( 0==Tcl_GetCommandInfo(interp, zIter, &cmdInfo) ){
|
||||
Tcl_AppendResult(interp, "no such iter: ", Tcl_GetString(objv[2]), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pIter = (struct TestChangeIter*)cmdInfo.objClientData;
|
||||
|
||||
rc = sqlite3changegroup_add_change(p->pGrp, pIter->pIter);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
default: { /* delete */
|
||||
assert( iSub==3 );
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
@ -1609,115 +1585,6 @@ static int SQLITE_TCLAPI test_sqlite3changegroup(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
/*
|
||||
** Destructor for Tcl iterator command object.
|
||||
*/
|
||||
static void test_iter_del(void *clientData){
|
||||
TestChangeIter *p = (TestChangeIter*)clientData;
|
||||
sqlite3changeset_finalize(p->pIter);
|
||||
ckfree(p);
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI test_iter_cmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
static const char *aSub[] = {
|
||||
"next", /* 0 */
|
||||
"data", /* 1 */
|
||||
"finalize", /* 2 */
|
||||
0
|
||||
};
|
||||
int iSub = 0;
|
||||
|
||||
TestChangeIter *p = (TestChangeIter*)clientData;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( objc<2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "CMD");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( Tcl_GetIndexFromObj(interp, objv[1], aSub, "sub-command", 0, &iSub) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
switch( iSub ){
|
||||
case 0:
|
||||
rc = sqlite3changeset_next(p->pIter);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
break;
|
||||
case 1:
|
||||
Tcl_SetObjResult(interp, testIterData(p->pIter));
|
||||
break;
|
||||
case 2:
|
||||
rc = sqlite3changeset_finalize(p->pIter);
|
||||
p->pIter = 0;
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: sqlite3changeset_start ?-invert? CHANGESET
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_start(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int isInvert = 0;
|
||||
void *pChangeset = 0; /* Buffer containing changeset */
|
||||
int nChangeset = 0; /* Size of buffer aChangeset in bytes */
|
||||
TestChangeIter *pNew = 0;
|
||||
sqlite3_changeset_iter *pIter = 0;
|
||||
int flags = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
static int iCmd = 1;
|
||||
char zCmd[64];
|
||||
|
||||
if( objc==3 ){
|
||||
int n = 0;
|
||||
const char *z = Tcl_GetStringFromObj(objv[1], &n);
|
||||
isInvert = (n>=2 && sqlite3_strnicmp(z, "-invert", n)==0);
|
||||
}
|
||||
|
||||
if( objc!=2 && (objc!=3 || !isInvert) ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "?-invert? CHANGESET");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0;
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[objc-1], &nChangeset);
|
||||
rc = sqlite3changeset_start_v2(&pIter, nChangeset, pChangeset, flags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
char *zErr = sqlite3_mprintf(
|
||||
"error in sqlite3changeset_start_v2() - %d", rc
|
||||
);
|
||||
Tcl_AppendResult(interp, zErr, (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pNew = (TestChangeIter*)ckalloc(sizeof(TestChangeIter));
|
||||
pNew->pIter = pIter;
|
||||
|
||||
sprintf(zCmd, "csiter%d", iCmd++);
|
||||
Tcl_CreateObjCommand(interp, zCmd, test_iter_cmd, (void*)pNew, test_iter_del);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int TestSession_Init(Tcl_Interp *interp){
|
||||
struct Cmd {
|
||||
const char *zCmd;
|
||||
@ -1725,7 +1592,6 @@ int TestSession_Init(Tcl_Interp *interp){
|
||||
} aCmd[] = {
|
||||
{ "sqlite3session", test_sqlite3session },
|
||||
{ "sqlite3changegroup", test_sqlite3changegroup },
|
||||
{ "sqlite3changeset_start", test_sqlite3changeset_start },
|
||||
{ "sqlite3session_foreach", test_sqlite3session_foreach },
|
||||
{ "sqlite3changeset_invert", test_sqlite3changeset_invert },
|
||||
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
|
||||
|
||||
@ -1,15 +1,3 @@
|
||||
*********************************** NOTICE ************************************
|
||||
* This extension is deprecated. The SQLite developers do not maintain this *
|
||||
* extension. At some point in the future, it might disappear from the source *
|
||||
* tree. *
|
||||
* *
|
||||
* If you are using this extension and think it should be supported moving *
|
||||
* forward, visit the SQLite Forum (https://sqlite.org/forum) and argue your *
|
||||
* case there. *
|
||||
* *
|
||||
* This deprecation notice was added on 2024-01-22. *
|
||||
*******************************************************************************
|
||||
|
||||
Activate the user authentication logic by including the
|
||||
ext/userauth/userauth.c source code file in the build and
|
||||
adding the -DSQLITE_USER_AUTHENTICATION compile-time option.
|
||||
|
||||
@ -43,9 +43,8 @@
|
||||
# which generates the makefile code, rather than using $(call) and
|
||||
# $(eval), or at least centralize the setup of the numerous vars
|
||||
# related to each build variant $(JS_BUILD_MODES). (Update: an
|
||||
# external script was attempted but generating properly-escaped
|
||||
# makefile code from within a shell script is even less legible
|
||||
# than the $(eval) indirection going on in this file.)
|
||||
# external script was attempted but it's even less legible than the
|
||||
# $(eval) indirection going on in this file.
|
||||
#
|
||||
default: all
|
||||
#default: quick
|
||||
@ -289,12 +288,6 @@ endif
|
||||
# embedding in the JS files and in building the distribution zip file.
|
||||
# It must NOT be in $(dir.tmp) because we need it to survive the
|
||||
# cleanup process for the dist build to work properly.
|
||||
#
|
||||
# Slight caveat: this uses the version info from the in-tree
|
||||
# sqlite3.c/h, which may diff from a user-provided $(sqlite3.c). The
|
||||
# end result is that the generated JS files may have static version
|
||||
# info from $(bin.version-info) which differ from their runtime-emited
|
||||
# version info (e.g. from sqlite3_libversion()).
|
||||
bin.version-info := $(dir.top)/version-info
|
||||
.NOTPARALLEL: $(bin.version-info)
|
||||
$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
|
||||
@ -313,9 +306,8 @@ DISTCLEAN_FILES += $(bin.stripccomments)
|
||||
|
||||
|
||||
########################################################################
|
||||
# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via:
|
||||
#
|
||||
# ./c-pp -f $(1) -o $(2) $(3)
|
||||
# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via ./c-pp -f
|
||||
# $(1) ...
|
||||
#
|
||||
# Historical notes:
|
||||
#
|
||||
@ -341,26 +333,19 @@ DISTCLEAN_FILES += $(bin.stripccomments)
|
||||
#
|
||||
# Note that the SQLITE_... build flags used here have NO EFFECT on the
|
||||
# JS/WASM build. They are solely for use with $(bin.c-pp) itself.
|
||||
#
|
||||
# -D... flags which should be included in all invocations should be
|
||||
# appended to $(C-PP.FILTER.global).
|
||||
bin.c-pp := ./c-pp
|
||||
$(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE)
|
||||
$(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_UTF16 \
|
||||
-DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_TEMP_STORE=3
|
||||
C-PP.FILTER.global ?=
|
||||
ifeq (1,$(SQLITE_C_IS_SEE))
|
||||
C-PP.FILTER.global += -Denable-see
|
||||
endif
|
||||
define C-PP.FILTER
|
||||
# Create $2 from $1 using $(bin.c-pp)
|
||||
# $1 = Input file: c-pp -f $(1).js
|
||||
# $2 = Output file: c-pp -o $(2).js
|
||||
# $3 = optional c-pp -D... flags
|
||||
$(2): $(1) $$(MAKEFILE) $$(bin.c-pp)
|
||||
$$(bin.c-pp) -f $(1) -o $$@ $(3) $(C-PP.FILTER.global)
|
||||
$$(bin.c-pp) -f $(1) -o $$@ $(3)
|
||||
CLEAN_FILES += $(2)
|
||||
endef
|
||||
# /end C-PP.FILTER
|
||||
@ -447,10 +432,8 @@ sqlite3-api.jses += $(sqlite3-api-build-version.js)
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
|
||||
# sqlite3-api-worker.js = the Worker1 API:
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
|
||||
# sqlite3-vfs-helper = helper APIs for VFSes:
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js
|
||||
# sqlite3-vtab-helper = helper APIs for VTABLEs:
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js
|
||||
# sqlite3-v-helper = helper APIs for VFSes and VTABLEs:
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
|
||||
# sqlite3-vfs-opfs.c-pp.js = the first OPFS VFS:
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
|
||||
# sqlite3-vfs-opfs-sahpool.c-pp.js = the second OPFS VFS:
|
||||
@ -466,14 +449,13 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||
# the first OPFS VFS and necessarily an external file.
|
||||
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
|
||||
SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
|
||||
#
|
||||
# $(sqlite3-api.ext.jses) = API-related files which are standalone files,
|
||||
# not part of the amalgamation.
|
||||
#
|
||||
sqlite3-api.ext.jses := $(SOAP.js.bld)
|
||||
sqlite3-api.ext.jses += $(SOAP.js.bld)
|
||||
$(SOAP.js.bld): $(SOAP.js)
|
||||
cp $< $@
|
||||
|
||||
all quick: $(sqlite3-api.ext.jses)
|
||||
q: quick
|
||||
|
||||
########################################################################
|
||||
# $(sqlite3-api*.*js) contain the core library code but not the
|
||||
# Emscripten-related glue which deals with loading sqlite3.wasm. In
|
||||
@ -546,10 +528,6 @@ emcc.jsflags += -sSTRICT_JS=0
|
||||
# 3.1.31. The fix for that in newer emcc's is to throw a built-time
|
||||
# error if STRICT_JS is used together with those options.
|
||||
|
||||
# emcc.jsflags += -sSTRICT=1
|
||||
# -sSTRICT=1 Causes failures about unknown symbols which the build
|
||||
# tools should be installing, e.g. __syscall_geteuid32
|
||||
|
||||
# -sENVIRONMENT values for the various build modes:
|
||||
emcc.environment.vanilla := web,worker
|
||||
emcc.environment.bundler-friendly := $(emcc.environment.vanilla)
|
||||
@ -570,11 +548,6 @@ emcc.environment.node := node
|
||||
# time with 16mb+ memory and 3X time when starting with 8MB. However,
|
||||
# such test results are inconsistent due to browser internals which
|
||||
# are opaque to us.
|
||||
#
|
||||
# 2024-03-04: emsdk 3.1.55 replaces INITIAL_MEMORY with INITIAL_HEAP,
|
||||
# but also says (in its changelog): "Note that it is currently not
|
||||
# supported in all configurations (#21071)."
|
||||
# https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md
|
||||
emcc.jsflags += -sALLOW_MEMORY_GROWTH
|
||||
emcc.INITIAL_MEMORY.128 := 134217728
|
||||
emcc.INITIAL_MEMORY.96 := 100663296
|
||||
@ -840,13 +813,13 @@ pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)
|
||||
# $4 = resulting sqlite-api JS/MJS file
|
||||
# $5 = resulting JS/MJS file
|
||||
# $6 = -D... flags for $(bin.c-pp)
|
||||
# $7 = optional extra flags for emcc
|
||||
# $7 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
|
||||
#
|
||||
# Maintenance reminder: be careful not to introduce spaces around args
|
||||
# ($1, $2), otherwise string concatenation will malfunction.
|
||||
#
|
||||
# Before calling this, emcc.environment.$(2) must be set to a value
|
||||
# for emcc's -sENVIRONMENT flag.
|
||||
# emcc.environment.$(2) must be set to a value for emcc's
|
||||
# -sENVIRONMENT flag.
|
||||
#
|
||||
# $(cflags.$(1)) and $(cflags.$(1).$(2)) may be defined to append
|
||||
# CFLAGS to a given build mode.
|
||||
@ -953,39 +926,18 @@ sqlite3-worker1.js.in := $(dir.api)/sqlite3-worker1.c-pp.js
|
||||
sqlite3-worker1-promiser.js.in := $(dir.api)/sqlite3-worker1-promiser.c-pp.js
|
||||
sqlite3-worker1.js := $(dir.dout)/sqlite3-worker1.js
|
||||
sqlite3-worker1-promiser.js := $(dir.dout)/sqlite3-worker1-promiser.js
|
||||
sqlite3-worker1-promiser.mjs := $(dir.dout)/sqlite3-worker1-promiser.mjs
|
||||
sqlite3-worker1-bundler-friendly.mjs := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs
|
||||
sqlite3-worker1-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs
|
||||
sqlite3-worker1-promiser-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js
|
||||
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js)))
|
||||
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.mjs),\
|
||||
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.js),\
|
||||
$(c-pp.D.sqlite3-bundler-friendly)))
|
||||
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js)))
|
||||
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\
|
||||
$(sqlite3-worker1-promiser-bundler-friendly.js),\
|
||||
$(c-pp.D.sqlite3-bundler-friendly)))
|
||||
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\
|
||||
-Dtarget=es6-module -Dtarget=es6-bundler-friendly))
|
||||
$(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.mjs) \
|
||||
$(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.js) \
|
||||
$(sqlite3-worker1-promiser-bundler-friendly.js)
|
||||
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js))
|
||||
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\
|
||||
-Dtarget=es6-module))
|
||||
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html))
|
||||
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\
|
||||
-Dtarget=es6-module))
|
||||
all: $(sqlite3-worker1.js) \
|
||||
$(sqlite3-worker1-promiser.js) $(sqlite3-worker1-promiser.mjs)
|
||||
|
||||
demo-worker1-promiser.html: $(sqlite3-worker1-promiser.js) demo-worker1-promiser.js
|
||||
demo-worker1-promiser-esm.html: $(sqlite3-worker1-promiser.mjs) demo-worker1-promiser.mjs
|
||||
all: demo-worker1-promiser.html demo-worker1-promiser-esm.html
|
||||
|
||||
sqlite3-api.ext.jses += \
|
||||
$(sqlite3-worker1-promiser.mjs) \
|
||||
$(sqlite3-worker1-bundler-friendly.mjs) \
|
||||
$(sqlite3-worker1.js)
|
||||
all quick: $(sqlite3-api.ext.jses)
|
||||
q: quick
|
||||
$(sqlite3.js) $(sqlite3.mjs): $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js)
|
||||
|
||||
########################################################################
|
||||
# batch-runner.js is part of one of the test apps which reads in SQL
|
||||
@ -1026,9 +978,6 @@ emcc.speedtest1.common += -sABORTING_MALLOC
|
||||
emcc.speedtest1.common += -sSTRICT_JS=0
|
||||
emcc.speedtest1.common += -sMODULARIZE
|
||||
emcc.speedtest1.common += -Wno-limited-postlink-optimizations
|
||||
emcc.speedtest1.common += -Wno-unused-main
|
||||
# ^^^^ -Wno-unused-main is for emcc 3.1.52+. speedtest1 has a wasm_main() which is
|
||||
# exported and called by the JS code.
|
||||
EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1)
|
||||
emcc.speedtest1.common += -sSTACK_SIZE=512KB
|
||||
emcc.speedtest1.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
|
||||
@ -174,17 +174,11 @@ const Rx = newObj({
|
||||
squiggly: /[{}]/
|
||||
});
|
||||
|
||||
|
||||
|
||||
const Util = newObj({
|
||||
toss,
|
||||
|
||||
unlink: function f(fn){
|
||||
if(!f.unlink){
|
||||
f.unlink = sqlite3.wasm.xWrap('sqlite3__wasm_vfs_unlink','int',
|
||||
['*','string']);
|
||||
}
|
||||
return 0==f.unlink(0,fn);
|
||||
unlink: function(fn){
|
||||
return 0==sqlite3.wasm.sqlite3_wasm_vfs_unlink(0,fn);
|
||||
},
|
||||
|
||||
argvToString: (list)=>{
|
||||
@ -203,7 +197,7 @@ const Util = newObj({
|
||||
|
||||
utf8Encode: (str)=>__utf8Encoder.encode(str),
|
||||
|
||||
strglob: sqlite3.wasm.xWrap('sqlite3__wasm_SQLTester_strglob','int',
|
||||
strglob: sqlite3.wasm.xWrap('sqlite3_wasm_SQLTester_strglob','int',
|
||||
['string','string'])
|
||||
})/*Util*/;
|
||||
|
||||
|
||||
@ -78,12 +78,10 @@ browser client:
|
||||
a Promise-based interface into the Worker #1 API. This is
|
||||
a far user-friendlier way to interface with databases running
|
||||
in a Worker thread.
|
||||
- **`sqlite3-vfs-helper.js`**\
|
||||
Installs the `sqlite3.vfs` namespace, which contain helpers for use
|
||||
by downstream code which creates `sqlite3_vfs` implementations.
|
||||
- **`sqlite3-vtab-helper.js`**\
|
||||
Installs the `sqlite3.vtab` namespace, which contain helpers for use
|
||||
by downstream code which creates `sqlite3_module` implementations.
|
||||
- **`sqlite3-v-helper.js`**\
|
||||
Installs `sqlite3.vfs` and `sqlite3.vtab`, namespaces which contain
|
||||
helpers for use by downstream code which creates `sqlite3_vfs`
|
||||
and `sqlite3_module` implementations.
|
||||
- **`sqlite3-vfs-opfs.c-pp.js`**\
|
||||
is an sqlite3 VFS implementation which supports the Origin-Private
|
||||
FileSystem (OPFS) as a storage layer to provide persistent storage
|
||||
|
||||
@ -19,10 +19,8 @@ Module.postRun.push(function(Module/*the Emscripten-style module object*/){
|
||||
- sqlite3-api-glue.js => glues previous parts together
|
||||
- sqlite3-api-oo.js => SQLite3 OO API #1
|
||||
- sqlite3-api-worker1.js => Worker-based API
|
||||
- sqlite3-vfs-helper.c-pp.js => Utilities for VFS impls
|
||||
- sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls
|
||||
- sqlite3-vfs-opfs.c-pp.js => OPFS VFS
|
||||
- sqlite3-vfs-opfs-sahpool.c-pp.js => OPFS SAHPool VFS
|
||||
- sqlite3-vfs-helper.js => Internal-use utilities for...
|
||||
- sqlite3-vfs-opfs.js => OPFS VFS
|
||||
- sqlite3-api-cleanup.js => final API cleanup
|
||||
- post-js-footer.js => closes this postRun() function
|
||||
*/
|
||||
|
||||
@ -14,8 +14,7 @@
|
||||
previous steps of the sqlite3-api.js bootstrapping process:
|
||||
sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It
|
||||
initializes the main API pieces so that the downstream components
|
||||
(e.g. sqlite3-api-oo1.js) have all of the infrastructure that they
|
||||
need.
|
||||
(e.g. sqlite3-api-oo1.js) have all that they need.
|
||||
*/
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
'use strict';
|
||||
@ -329,16 +328,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]);
|
||||
}
|
||||
|
||||
//#if enable-see
|
||||
if(wasm.exports.sqlite3_key_v2 instanceof Function){
|
||||
/**
|
||||
This code is capable of using an SEE build but note that an SEE
|
||||
WASM build is generally incompatible with SEE's license
|
||||
conditions. It is permitted for use internally in organizations
|
||||
which have licensed SEE, but not for public sites because
|
||||
exposing an SEE build of sqlite3.wasm effectively provides all
|
||||
clients with a working copy of the commercial SEE code.
|
||||
*/
|
||||
if(wasm.exports.sqlite3_activate_see instanceof Function){
|
||||
wasm.bindingSignatures.push(
|
||||
["sqlite3_key", "int", "sqlite3*", "string", "int"],
|
||||
["sqlite3_key_v2","int","sqlite3*","string","*","int"],
|
||||
@ -347,14 +337,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
["sqlite3_activate_see", undefined, "string"]
|
||||
);
|
||||
}
|
||||
//#endif enable-see
|
||||
|
||||
/**
|
||||
Functions which require BigInt (int64) support are separated from
|
||||
the others because we need to conditionally bind them or apply
|
||||
dummy impls, depending on the capabilities of the environment.
|
||||
(That said: we never actually build without BigInt support,
|
||||
and such builds are untested.)
|
||||
|
||||
Note that not all of these functions directly require int64
|
||||
but are only for use with APIs which require int64. For example,
|
||||
@ -373,10 +359,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
/* Careful! Short version: de/serialize() are problematic because they
|
||||
might use a different allocator than the user for managing the
|
||||
deserialized block. de/serialize() are ONLY safe to use with
|
||||
sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. Because
|
||||
of this, the canonical builds of sqlite3.wasm/js guarantee that
|
||||
sqlite3.wasm.alloc() and friends use those allocators. Custom builds
|
||||
may not guarantee that, however. */,
|
||||
sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */,
|
||||
["sqlite3_drop_modules", "int", ["sqlite3*", "**"]],
|
||||
["sqlite3_last_insert_rowid", "i64", ["sqlite3*"]],
|
||||
["sqlite3_malloc64", "*","i64"],
|
||||
@ -439,6 +422,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
// Add session/changeset APIs...
|
||||
if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){
|
||||
/* ACHTUNG: 2022-12-23: the session/changeset API bindings are
|
||||
COMPLETELY UNTESTED. */
|
||||
/**
|
||||
FuncPtrAdapter options for session-related callbacks with the
|
||||
native signature "i(ps)". This proxy converts the 2nd argument
|
||||
@ -616,25 +601,16 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
/**
|
||||
Functions which are intended solely for API-internal use by the
|
||||
WASM components, not client code. These get installed into
|
||||
sqlite3.util. Some of them get exposed to clients via variants
|
||||
in sqlite3_js_...().
|
||||
|
||||
2024-01-11: these were renamed, with two underscores in the
|
||||
prefix, to ensure that clients do not accidentally depend on
|
||||
them. They have always been documented as internal-use-only, so
|
||||
no clients "should" be depending on the old names.
|
||||
sqlite3.wasm. Some of them get exposed to clients via variants
|
||||
named sqlite3_js_...().
|
||||
*/
|
||||
wasm.bindingSignatures.wasmInternal = [
|
||||
["sqlite3__wasm_db_reset", "int", "sqlite3*"],
|
||||
["sqlite3__wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
|
||||
[/* DO NOT USE. This is deprecated since 2023-08-11 because it can
|
||||
trigger assert() in debug builds when used with file sizes
|
||||
which are not sizes to a multiple of a valid db page size. */
|
||||
"sqlite3__wasm_vfs_create_file", "int", "sqlite3_vfs*","string","*", "int"
|
||||
],
|
||||
["sqlite3__wasm_posix_create_file", "int", "string","*", "int"],
|
||||
["sqlite3__wasm_vfs_unlink", "int", "sqlite3_vfs*","string"],
|
||||
["sqlite3__wasm_qfmt_token","string:dealloc", "string","int"]
|
||||
wasm.bindingSignatures.wasm = [
|
||||
["sqlite3_wasm_db_reset", "int", "sqlite3*"],
|
||||
["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
|
||||
["sqlite3_wasm_vfs_create_file", "int",
|
||||
"sqlite3_vfs*","string","*", "int"],
|
||||
["sqlite3_wasm_posix_create_file", "int", "string","*", "int"],
|
||||
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
|
||||
];
|
||||
|
||||
/**
|
||||
@ -676,7 +652,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
Use case: sqlite3_bind_pointer() and sqlite3_result_pointer()
|
||||
call for "a static string and preferably a string
|
||||
literal." This converter is used to ensure that the string
|
||||
literal". This converter is used to ensure that the string
|
||||
value seen by those functions is long-lived and behaves as they
|
||||
need it to.
|
||||
*/
|
||||
@ -698,15 +674,14 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
`sqlite3_vfs*` via capi.sqlite3_vfs.pointer.
|
||||
*/
|
||||
const __xArgPtr = wasm.xWrap.argAdapter('*');
|
||||
const nilType = function(){
|
||||
/*a class which no value can ever be an instance of*/
|
||||
};
|
||||
const nilType = function(){}/*a class no value can ever be an instance of*/;
|
||||
wasm.xWrap.argAdapter('sqlite3_filename', __xArgPtr)
|
||||
('sqlite3_context*', __xArgPtr)
|
||||
('sqlite3_value*', __xArgPtr)
|
||||
('void*', __xArgPtr)
|
||||
('sqlite3_changegroup*', __xArgPtr)
|
||||
('sqlite3_changeset_iter*', __xArgPtr)
|
||||
//('sqlite3_rebaser*', __xArgPtr)
|
||||
('sqlite3_session*', __xArgPtr)
|
||||
('sqlite3_stmt*', (v)=>
|
||||
__xArgPtr((v instanceof (sqlite3?.oo1?.Stmt || nilType))
|
||||
@ -767,8 +742,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
for(const e of wasm.bindingSignatures){
|
||||
capi[e[0]] = wasm.xWrap.apply(null, e);
|
||||
}
|
||||
for(const e of wasm.bindingSignatures.wasmInternal){
|
||||
util[e[0]] = wasm.xWrap.apply(null, e);
|
||||
for(const e of wasm.bindingSignatures.wasm){
|
||||
wasm[e[0]] = wasm.xWrap.apply(null, e);
|
||||
}
|
||||
|
||||
/* For C API functions which cannot work properly unless
|
||||
@ -790,9 +765,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
implicitly making it part of the public interface. */
|
||||
delete wasm.bindingSignatures;
|
||||
|
||||
if(wasm.exports.sqlite3__wasm_db_error){
|
||||
if(wasm.exports.sqlite3_wasm_db_error){
|
||||
const __db_err = wasm.xWrap(
|
||||
'sqlite3__wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
|
||||
'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string'
|
||||
);
|
||||
/**
|
||||
Sets the given db's error state. Accepts:
|
||||
@ -810,7 +785,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Returns the resulting code. Pass (pDb,0,0) to clear the error
|
||||
state.
|
||||
*/
|
||||
util.sqlite3__wasm_db_error = function(pDb, resultCode, message){
|
||||
util.sqlite3_wasm_db_error = function(pDb, resultCode, message){
|
||||
if(resultCode instanceof sqlite3.WasmAllocError){
|
||||
resultCode = capi.SQLITE_NOMEM;
|
||||
message = 0 /*avoid allocating message string*/;
|
||||
@ -821,17 +796,17 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return pDb ? __db_err(pDb, resultCode, message) : resultCode;
|
||||
};
|
||||
}else{
|
||||
util.sqlite3__wasm_db_error = function(pDb,errCode,msg){
|
||||
console.warn("sqlite3__wasm_db_error() is not exported.",arguments);
|
||||
util.sqlite3_wasm_db_error = function(pDb,errCode,msg){
|
||||
console.warn("sqlite3_wasm_db_error() is not exported.",arguments);
|
||||
return errCode;
|
||||
};
|
||||
}
|
||||
}/*xWrap() bindings*/
|
||||
|
||||
{/* Import C-level constants and structs... */
|
||||
const cJson = wasm.xCall('sqlite3__wasm_enum_json');
|
||||
const cJson = wasm.xCall('sqlite3_wasm_enum_json');
|
||||
if(!cJson){
|
||||
toss("Maintenance required: increase sqlite3__wasm_enum_json()'s",
|
||||
toss("Maintenance required: increase sqlite3_wasm_enum_json()'s",
|
||||
"static buffer size!");
|
||||
}
|
||||
//console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
|
||||
@ -902,7 +877,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
delete capi[k];
|
||||
}
|
||||
capi.sqlite3_vtab_config = wasm.xWrap(
|
||||
'sqlite3__wasm_vtab_config','int',[
|
||||
'sqlite3_wasm_vtab_config','int',[
|
||||
'sqlite3*', 'int', 'int']
|
||||
);
|
||||
}/* end vtab-related setup */
|
||||
@ -914,7 +889,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
consistency with non-special-case wrappings.
|
||||
*/
|
||||
const __dbArgcMismatch = (pDb,f,n)=>{
|
||||
return util.sqlite3__wasm_db_error(pDb, capi.SQLITE_MISUSE,
|
||||
return util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
|
||||
f+"() requires "+n+" argument"+
|
||||
(1===n?"":'s')+".");
|
||||
};
|
||||
@ -923,7 +898,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
argument and require SQLITE_UTF8. Sets the db error code to
|
||||
SQLITE_FORMAT and returns that code. */
|
||||
const __errEncoding = (pDb)=>{
|
||||
return util.sqlite3__wasm_db_error(
|
||||
return util.sqlite3_wasm_db_error(
|
||||
pDb, capi.SQLITE_FORMAT, "SQLITE_UTF8 is the only supported encoding."
|
||||
);
|
||||
};
|
||||
@ -1153,7 +1128,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
return rc;
|
||||
}catch(e){
|
||||
return util.sqlite3__wasm_db_error(pDb, e);
|
||||
return util.sqlite3_wasm_db_error(pDb, e);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1279,7 +1254,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return rc;
|
||||
}catch(e){
|
||||
console.error("sqlite3_create_function_v2() setup threw:",e);
|
||||
return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: "+e);
|
||||
return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1324,7 +1299,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return rc;
|
||||
}catch(e){
|
||||
console.error("sqlite3_create_window_function() setup threw:",e);
|
||||
return util.sqlite3__wasm_db_error(pDb, e, "Creation of UDF threw: "+e);
|
||||
return util.sqlite3_wasm_db_error(pDb, e, "Creation of UDF threw: "+e);
|
||||
}
|
||||
};
|
||||
/**
|
||||
@ -1419,7 +1394,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
|
||||
case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
|
||||
default:
|
||||
return util.sqlite3__wasm_db_error(
|
||||
return util.sqlite3_wasm_db_error(
|
||||
pDb, capi.SQLITE_MISUSE,
|
||||
"Invalid SQL argument type for sqlite3_prepare_v2/v3()."
|
||||
);
|
||||
@ -1463,7 +1438,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}else if('string'===typeof text){
|
||||
[p, n] = wasm.allocCString(text);
|
||||
}else{
|
||||
return util.sqlite3__wasm_db_error(
|
||||
return util.sqlite3_wasm_db_error(
|
||||
capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE,
|
||||
"Invalid 3rd argument type for sqlite3_bind_text()."
|
||||
);
|
||||
@ -1471,7 +1446,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return __bindText(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC);
|
||||
}catch(e){
|
||||
wasm.dealloc(p);
|
||||
return util.sqlite3__wasm_db_error(
|
||||
return util.sqlite3_wasm_db_error(
|
||||
capi.sqlite3_db_handle(pStmt), e
|
||||
);
|
||||
}
|
||||
@ -1497,7 +1472,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}else if('string'===typeof pMem){
|
||||
[p, n] = wasm.allocCString(pMem);
|
||||
}else{
|
||||
return util.sqlite3__wasm_db_error(
|
||||
return util.sqlite3_wasm_db_error(
|
||||
capi.sqlite3_db_handle(pStmt), capi.SQLITE_MISUSE,
|
||||
"Invalid 3rd argument type for sqlite3_bind_blob()."
|
||||
);
|
||||
@ -1505,7 +1480,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return __bindBlob(pStmt, iCol, p, n, capi.SQLITE_WASM_DEALLOC);
|
||||
}catch(e){
|
||||
wasm.dealloc(p);
|
||||
return util.sqlite3__wasm_db_error(
|
||||
return util.sqlite3_wasm_db_error(
|
||||
capi.sqlite3_db_handle(pStmt), e
|
||||
);
|
||||
}
|
||||
@ -1529,11 +1504,11 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
case capi.SQLITE_CONFIG_SORTERREF_SIZE: // 28 /* int nByte */
|
||||
case capi.SQLITE_CONFIG_STMTJRNL_SPILL: // 26 /* int nByte */
|
||||
case capi.SQLITE_CONFIG_URI:// 17 /* int */
|
||||
return wasm.exports.sqlite3__wasm_config_i(op, args[0]);
|
||||
return wasm.exports.sqlite3_wasm_config_i(op, args[0]);
|
||||
case capi.SQLITE_CONFIG_LOOKASIDE: // 13 /* int int */
|
||||
return wasm.exports.sqlite3__wasm_config_ii(op, args[0], args[1]);
|
||||
return wasm.exports.sqlite3_wasm_config_ii(op, args[0], args[1]);
|
||||
case capi.SQLITE_CONFIG_MEMDB_MAXSIZE: // 29 /* sqlite3_int64 */
|
||||
return wasm.exports.sqlite3__wasm_config_j(op, args[0]);
|
||||
return wasm.exports.sqlite3_wasm_config_j(op, args[0]);
|
||||
case capi.SQLITE_CONFIG_GETMALLOC: // 5 /* sqlite3_mem_methods* */
|
||||
case capi.SQLITE_CONFIG_GETMUTEX: // 11 /* sqlite3_mutex_methods* */
|
||||
case capi.SQLITE_CONFIG_GETPCACHE2: // 19 /* sqlite3_pcache_methods2* */
|
||||
@ -1554,10 +1529,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
case capi.SQLITE_CONFIG_SQLLOG: // 21 /* xSqllog, void* */
|
||||
case capi.SQLITE_CONFIG_WIN32_HEAPSIZE: // 23 /* int nByte */
|
||||
default:
|
||||
/* maintenance note: we specifically do not include
|
||||
SQLITE_CONFIG_ROWID_IN_VIEW here, on the grounds that
|
||||
it's only for legacy support and no apps written with
|
||||
this API require that. */
|
||||
return capi.SQLITE_NOTFOUND;
|
||||
}
|
||||
};
|
||||
@ -1603,11 +1574,11 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
if( pKvvfs ){/* kvvfs-specific glue */
|
||||
if(util.isUIThread()){
|
||||
const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
|
||||
wasm.exports.sqlite3__wasm_kvvfs_methods()
|
||||
wasm.exports.sqlite3_wasm_kvvfs_methods()
|
||||
);
|
||||
delete capi.sqlite3_kvvfs_methods;
|
||||
|
||||
const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack,
|
||||
const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
|
||||
pstack = wasm.pstack;
|
||||
|
||||
const kvvfsStorage = (zClass)=>
|
||||
@ -1616,7 +1587,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
/**
|
||||
Implementations for members of the object referred to by
|
||||
sqlite3__wasm_kvvfs_methods(). We swap out the native
|
||||
sqlite3_wasm_kvvfs_methods(). We swap out the native
|
||||
implementations with these, which use localStorage or
|
||||
sessionStorage for their backing store.
|
||||
*/
|
||||
@ -1696,181 +1667,5 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
}/*pKvvfs*/
|
||||
|
||||
/* Warn if client-level code makes use of FuncPtrAdapter. */
|
||||
wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
|
||||
|
||||
const StructBinder = sqlite3.StructBinder
|
||||
/* we require a local alias b/c StructBinder is removed from the sqlite3
|
||||
object during the final steps of the API cleanup. */;
|
||||
/**
|
||||
Installs a StructBinder-bound function pointer member of the
|
||||
given name and function in the given StructBinder.StructType
|
||||
target object.
|
||||
|
||||
It creates a WASM proxy for the given function and arranges for
|
||||
that proxy to be cleaned up when tgt.dispose() is called. Throws
|
||||
on the slightest hint of error, e.g. tgt is-not-a StructType,
|
||||
name does not map to a struct-bound member, etc.
|
||||
|
||||
As a special case, if the given function is a pointer, then
|
||||
`wasm.functionEntry()` is used to validate that it is a known
|
||||
function. If so, it is used as-is with no extra level of proxying
|
||||
or cleanup, else an exception is thrown. It is legal to pass a
|
||||
value of 0, indicating a NULL pointer, with the caveat that 0
|
||||
_is_ a legal function pointer in WASM but it will not be accepted
|
||||
as such _here_. (Justification: the function at address zero must
|
||||
be one which initially came from the WASM module, not a method we
|
||||
want to bind to a virtual table or VFS.)
|
||||
|
||||
This function returns a proxy for itself which is bound to tgt
|
||||
and takes 2 args (name,func). That function returns the same
|
||||
thing as this one, permitting calls to be chained.
|
||||
|
||||
If called with only 1 arg, it has no side effects but returns a
|
||||
func with the same signature as described above.
|
||||
|
||||
ACHTUNG: because we cannot generically know how to transform JS
|
||||
exceptions into result codes, the installed functions do no
|
||||
automatic catching of exceptions. It is critical, to avoid
|
||||
undefined behavior in the C layer, that methods mapped via
|
||||
this function do not throw. The exception, as it were, to that
|
||||
rule is...
|
||||
|
||||
If applyArgcCheck is true then each JS function (as opposed to
|
||||
function pointers) gets wrapped in a proxy which asserts that it
|
||||
is passed the expected number of arguments, throwing if the
|
||||
argument count does not match expectations. That is only intended
|
||||
for dev-time usage for sanity checking, and may leave the C
|
||||
environment in an undefined state.
|
||||
*/
|
||||
const installMethod = function callee(
|
||||
tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck
|
||||
){
|
||||
if(!(tgt instanceof StructBinder.StructType)){
|
||||
toss("Usage error: target object is-not-a StructType.");
|
||||
}else if(!(func instanceof Function) && !wasm.isPtr(func)){
|
||||
toss("Usage errror: expecting a Function or WASM pointer to one.");
|
||||
}
|
||||
if(1===arguments.length){
|
||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||
}
|
||||
if(!callee.argcProxy){
|
||||
callee.argcProxy = function(tgt, funcName, func,sig){
|
||||
return function(...args){
|
||||
if(func.length!==arguments.length){
|
||||
toss("Argument mismatch for",
|
||||
tgt.structInfo.name+"::"+funcName
|
||||
+": Native signature is:",sig);
|
||||
}
|
||||
return func.apply(this, args);
|
||||
}
|
||||
};
|
||||
/* An ondispose() callback for use with
|
||||
StructBinder-created types. */
|
||||
callee.removeFuncList = function(){
|
||||
if(this.ondispose.__removeFuncList){
|
||||
this.ondispose.__removeFuncList.forEach(
|
||||
(v,ndx)=>{
|
||||
if('number'===typeof v){
|
||||
try{wasm.uninstallFunction(v)}
|
||||
catch(e){/*ignore*/}
|
||||
}
|
||||
/* else it's a descriptive label for the next number in
|
||||
the list. */
|
||||
}
|
||||
);
|
||||
delete this.ondispose.__removeFuncList;
|
||||
}
|
||||
};
|
||||
}/*static init*/
|
||||
const sigN = tgt.memberSignature(name);
|
||||
if(sigN.length<2){
|
||||
toss("Member",name,"does not have a function pointer signature:",sigN);
|
||||
}
|
||||
const memKey = tgt.memberKey(name);
|
||||
const fProxy = (applyArgcCheck && !wasm.isPtr(func))
|
||||
/** This middle-man proxy is only for use during development, to
|
||||
confirm that we always pass the proper number of
|
||||
arguments. We know that the C-level code will always use the
|
||||
correct argument count. */
|
||||
? callee.argcProxy(tgt, memKey, func, sigN)
|
||||
: func;
|
||||
if(wasm.isPtr(fProxy)){
|
||||
if(fProxy && !wasm.functionEntry(fProxy)){
|
||||
toss("Pointer",fProxy,"is not a WASM function table entry.");
|
||||
}
|
||||
tgt[memKey] = fProxy;
|
||||
}else{
|
||||
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
||||
tgt[memKey] = pFunc;
|
||||
if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){
|
||||
tgt.addOnDispose('ondispose.__removeFuncList handler',
|
||||
callee.removeFuncList);
|
||||
tgt.ondispose.__removeFuncList = [];
|
||||
}
|
||||
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
||||
}
|
||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||
}/*installMethod*/;
|
||||
installMethod.installMethodArgcCheck = false;
|
||||
|
||||
/**
|
||||
Installs methods into the given StructBinder.StructType-type
|
||||
instance. Each entry in the given methods object must map to a
|
||||
known member of the given StructType, else an exception will be
|
||||
triggered. See installMethod() for more details, including the
|
||||
semantics of the 3rd argument.
|
||||
|
||||
As an exception to the above, if any two or more methods in the
|
||||
2nd argument are the exact same function, installMethod() is
|
||||
_not_ called for the 2nd and subsequent instances, and instead
|
||||
those instances get assigned the same method pointer which is
|
||||
created for the first instance. This optimization is primarily to
|
||||
accommodate special handling of sqlite3_module::xConnect and
|
||||
xCreate methods.
|
||||
|
||||
On success, returns its first argument. Throws on error.
|
||||
*/
|
||||
const installMethods = function(
|
||||
structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||
){
|
||||
const seen = new Map /* map of <Function, memberName> */;
|
||||
for(const k of Object.keys(methods)){
|
||||
const m = methods[k];
|
||||
const prior = seen.get(m);
|
||||
if(prior){
|
||||
const mkey = structInstance.memberKey(k);
|
||||
structInstance[mkey] = structInstance[structInstance.memberKey(prior)];
|
||||
}else{
|
||||
installMethod(structInstance, k, m, applyArgcCheck);
|
||||
seen.set(m, k);
|
||||
}
|
||||
}
|
||||
return structInstance;
|
||||
};
|
||||
|
||||
/**
|
||||
Equivalent to calling installMethod(this,...arguments) with a
|
||||
first argument of this object. If called with 1 or 2 arguments
|
||||
and the first is an object, it's instead equivalent to calling
|
||||
installMethods(this,...arguments).
|
||||
*/
|
||||
StructBinder.StructType.prototype.installMethod = function callee(
|
||||
name, func, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||
){
|
||||
return (arguments.length < 3 && name && 'object'===typeof name)
|
||||
? installMethods(this, ...arguments)
|
||||
: installMethod(this, ...arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
Equivalent to calling installMethods() with a first argument
|
||||
of this object.
|
||||
*/
|
||||
StructBinder.StructType.prototype.installMethods = function(
|
||||
methods, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||
){
|
||||
return installMethods(this, methods, applyArgcCheck);
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@ -87,104 +87,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
*/
|
||||
const __vfsPostOpenSql = Object.create(null);
|
||||
|
||||
//#if enable-see
|
||||
/**
|
||||
Converts ArrayBuffer or Uint8Array ba into a string of hex
|
||||
digits.
|
||||
*/
|
||||
const byteArrayToHex = function(ba){
|
||||
if( ba instanceof ArrayBuffer ){
|
||||
ba = new Uint8Array(ba);
|
||||
}
|
||||
const li = [];
|
||||
const digits = "0123456789abcdef";
|
||||
for( const d of ba ){
|
||||
li.push( digits[(d & 0xf0) >> 4], digits[d & 0x0f] );
|
||||
}
|
||||
return li.join('');
|
||||
};
|
||||
|
||||
/**
|
||||
Internal helper to apply an SEE key to a just-opened
|
||||
database. Requires that db be-a DB object which has just been
|
||||
opened, opt be the options object processed by its ctor, and opt
|
||||
must have either the key, hexkey, or textkey properties, either
|
||||
as a string, an ArrayBuffer, or a Uint8Array.
|
||||
|
||||
This is a no-op in non-SEE builds. It throws on error and returns
|
||||
without side effects if none of the key/textkey/hexkey options
|
||||
are set. It throws if more than one is set or if any are set to
|
||||
values of an invalid type.
|
||||
|
||||
Returns true if it applies the key, else an unspecified falsy value.
|
||||
*/
|
||||
const dbCtorApplySEEKey = function(db,opt){
|
||||
if( !capi.sqlite3_key_v2 ) return;
|
||||
let keytype;
|
||||
let key;
|
||||
const check = (opt.key ? 1 : 0) + (opt.hexkey ? 1 : 0) + (opt.textkey ? 1 : 0);
|
||||
if( !check ) return;
|
||||
else if( check>1 ){
|
||||
toss3(capi.SQLITE_MISUSE,
|
||||
"Only ONE of (key, hexkey, textkey) may be provided.");
|
||||
}
|
||||
if( opt.key ){
|
||||
/* It is not legal to bind an argument to PRAGMA key=?, so we
|
||||
convert it to a hexkey... */
|
||||
keytype = 'key';
|
||||
key = opt.key;
|
||||
if('string'===typeof key){
|
||||
key = new TextEncoder('utf-8').encode(key);
|
||||
}
|
||||
if((key instanceof ArrayBuffer) || (key instanceof Uint8Array)){
|
||||
key = byteArrayToHex(key);
|
||||
keytype = 'hexkey';
|
||||
}else{
|
||||
toss3(capi.SQLITE_MISUSE,
|
||||
"Invalid value for the 'key' option. Expecting a string,",
|
||||
"ArrayBuffer, or Uint8Array.");
|
||||
return;
|
||||
}
|
||||
}else if( opt.textkey ){
|
||||
/* For textkey we need it to be in string form, so convert it to
|
||||
a string if it's a byte array... */
|
||||
keytype = 'textkey';
|
||||
key = opt.textkey;
|
||||
if(key instanceof ArrayBuffer){
|
||||
key = new Uint8Array(key);
|
||||
}
|
||||
if(key instanceof Uint8Array){
|
||||
key = new TextDecoder('utf-8').decode(key);
|
||||
}else if('string'!==typeof key){
|
||||
toss3(capi.SQLITE_MISUSE,
|
||||
"Invalid value for the 'textkey' option. Expecting a string,",
|
||||
"ArrayBuffer, or Uint8Array.");
|
||||
}
|
||||
}else if( opt.hexkey ){
|
||||
keytype = 'hexkey';
|
||||
key = opt.hexkey;
|
||||
if((key instanceof ArrayBuffer) || (key instanceof Uint8Array)){
|
||||
key = byteArrayToHex(key);
|
||||
}else if('string'!==typeof key){
|
||||
toss3(capi.SQLITE_MISUSE,
|
||||
"Invalid value for the 'hexkey' option. Expecting a string,",
|
||||
"ArrayBuffer, or Uint8Array.");
|
||||
}
|
||||
/* else assume it's valid hex codes */
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
let stmt;
|
||||
try{
|
||||
stmt = db.prepare("PRAGMA "+keytype+"="+util.sqlite3__wasm_qfmt_token(key, 1));
|
||||
stmt.step();
|
||||
}finally{
|
||||
if(stmt) stmt.finalize();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
//#endif enable-see
|
||||
|
||||
/**
|
||||
A proxy for DB class constructors. It must be called with the
|
||||
being-construct DB object as its "this". See the DB constructor
|
||||
@ -273,28 +175,16 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
__ptrMap.set(this, pDb);
|
||||
__stmtMap.set(this, Object.create(null));
|
||||
try{
|
||||
//#if enable-see
|
||||
dbCtorApplySEEKey(this,opt);
|
||||
//#endif
|
||||
// Check for per-VFS post-open SQL/callback...
|
||||
const pVfs = capi.sqlite3_js_db_vfs(pDb)
|
||||
|| toss3("Internal error: cannot get VFS for new db handle.");
|
||||
const pVfs = capi.sqlite3_js_db_vfs(pDb);
|
||||
if(!pVfs) toss3("Internal error: cannot get VFS for new db handle.");
|
||||
const postInitSql = __vfsPostOpenSql[pVfs];
|
||||
if(postInitSql){
|
||||
/**
|
||||
Reminder: if this db is encrypted and the client did _not_ pass
|
||||
in the key, any init code will fail, causing the ctor to throw.
|
||||
We don't actually know whether the db is encrypted, so we cannot
|
||||
sensibly apply any heuristics which skip the init code only for
|
||||
encrypted databases for which no key has yet been supplied.
|
||||
*/
|
||||
if(postInitSql instanceof Function){
|
||||
postInitSql(this, sqlite3);
|
||||
}else{
|
||||
checkSqlite3Rc(
|
||||
pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)
|
||||
);
|
||||
}
|
||||
if(postInitSql instanceof Function){
|
||||
postInitSql(this, sqlite3);
|
||||
}else if(postInitSql){
|
||||
checkSqlite3Rc(
|
||||
pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)
|
||||
);
|
||||
}
|
||||
}catch(e){
|
||||
this.close();
|
||||
@ -390,36 +280,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
- `flags`: open-mode flags
|
||||
- `vfs`: the VFS fname
|
||||
|
||||
//#if enable-see
|
||||
|
||||
SEE-capable builds optionally support ONE of the following
|
||||
additional options:
|
||||
|
||||
- `key`, `hexkey`, or `textkey`: encryption key as a string,
|
||||
ArrayBuffer, or Uint8Array. These flags function as documented
|
||||
for the SEE pragmas of the same names. Using a byte array for
|
||||
`hexkey` is equivalent to the same series of hex codes in
|
||||
string form, so `'666f6f'` is equivalent to
|
||||
`Uint8Array([0x66,0x6f,0x6f])`. A `textkey` byte array is
|
||||
assumed to be UTF-8. A `key` string is transformed into a UTF-8
|
||||
byte array, and a `key` byte array is transformed into a
|
||||
`hexkey` with the same bytes.
|
||||
|
||||
In non-SEE builds, these options are ignored. In SEE builds,
|
||||
`PRAGMA key/textkey/hexkey=X` is executed immediately after
|
||||
opening the db. If more than one of the options is provided,
|
||||
or any option has an invalid argument type, an exception is
|
||||
thrown.
|
||||
|
||||
Note that some DB subclasses may run post-initialization SQL
|
||||
code, e.g. to set a busy-handler timeout or tweak the page cache
|
||||
size. Such code is run _after_ the SEE key is applied. If no key
|
||||
is supplied and the database is encrypted, execution of the
|
||||
post-initialization SQL will fail, causing the constructor to
|
||||
throw.
|
||||
|
||||
//#endif enable-see
|
||||
|
||||
The `filename` and `vfs` arguments may be either JS strings or
|
||||
C-strings allocated via WASM. `flags` is required to be a JS
|
||||
string (because it's specific to this API, which is specific
|
||||
@ -428,8 +288,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
For purposes of passing a DB instance to C-style sqlite3
|
||||
functions, the DB object's read-only `pointer` property holds its
|
||||
`sqlite3*` pointer value. That property can also be used to check
|
||||
whether this DB instance is still open: it will evaluate to
|
||||
`undefined` after the DB object's close() method is called.
|
||||
whether this DB instance is still open.
|
||||
|
||||
In the main window thread, the filenames `":localStorage:"` and
|
||||
`":sessionStorage:"` are special: they cause the db to use either
|
||||
@ -574,56 +433,40 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
out.returnVal = ()=>opt.resultRows;
|
||||
}
|
||||
if(opt.callback || opt.resultRows){
|
||||
switch((undefined===opt.rowMode) ? 'array' : opt.rowMode) {
|
||||
case 'object':
|
||||
out.cbArg = (stmt,cache)=>{
|
||||
if( !cache.columnNames ) cache.columnNames = stmt.getColumnNames([]);
|
||||
/* https://sqlite.org/forum/forumpost/3632183d2470617d:
|
||||
conversion of rows to objects (key/val pairs) is
|
||||
somewhat expensive for large data sets because of the
|
||||
native-to-JS conversion of the column names. If we
|
||||
instead cache the names and build objects from that
|
||||
list of strings, it can run twice as fast. The
|
||||
difference is not noticeable for small data sets but
|
||||
becomes human-perceivable when enough rows are
|
||||
involved. */
|
||||
const row = stmt.get([]);
|
||||
const rv = Object.create(null);
|
||||
for( const i in cache.columnNames ) rv[cache.columnNames[i]] = row[i];
|
||||
return rv;
|
||||
};
|
||||
break;
|
||||
case 'array': out.cbArg = (stmt)=>stmt.get([]); break;
|
||||
case 'stmt':
|
||||
if(Array.isArray(opt.resultRows)){
|
||||
toss3("exec(): invalid rowMode for a resultRows array: must",
|
||||
"be one of 'array', 'object',",
|
||||
"a result column number, or column name reference.");
|
||||
}
|
||||
out.cbArg = (stmt)=>stmt;
|
||||
break;
|
||||
default:
|
||||
if(util.isInt32(opt.rowMode)){
|
||||
out.cbArg = (stmt)=>stmt.get(opt.rowMode);
|
||||
switch((undefined===opt.rowMode)
|
||||
? 'array' : opt.rowMode) {
|
||||
case 'object': out.cbArg = (stmt)=>stmt.get(Object.create(null)); break;
|
||||
case 'array': out.cbArg = (stmt)=>stmt.get([]); break;
|
||||
case 'stmt':
|
||||
if(Array.isArray(opt.resultRows)){
|
||||
toss3("exec(): invalid rowMode for a resultRows array: must",
|
||||
"be one of 'array', 'object',",
|
||||
"a result column number, or column name reference.");
|
||||
}
|
||||
out.cbArg = (stmt)=>stmt;
|
||||
break;
|
||||
}else if('string'===typeof opt.rowMode
|
||||
&& opt.rowMode.length>1
|
||||
&& '$'===opt.rowMode[0]){
|
||||
/* "$X": fetch column named "X" (case-sensitive!). Prior
|
||||
to 2022-12-14 ":X" and "@X" were also permitted, but
|
||||
having so many options is unnecessary and likely to
|
||||
cause confusion. */
|
||||
const $colName = opt.rowMode.substr(1);
|
||||
out.cbArg = (stmt)=>{
|
||||
const rc = stmt.get(Object.create(null))[$colName];
|
||||
return (undefined===rc)
|
||||
? toss3(capi.SQLITE_NOTFOUND,
|
||||
"exec(): unknown result column:",$colName)
|
||||
: rc;
|
||||
};
|
||||
break;
|
||||
}
|
||||
toss3("Invalid rowMode:",opt.rowMode);
|
||||
default:
|
||||
if(util.isInt32(opt.rowMode)){
|
||||
out.cbArg = (stmt)=>stmt.get(opt.rowMode);
|
||||
break;
|
||||
}else if('string'===typeof opt.rowMode
|
||||
&& opt.rowMode.length>1
|
||||
&& '$'===opt.rowMode[0]){
|
||||
/* "$X": fetch column named "X" (case-sensitive!). Prior
|
||||
to 2022-12-14 ":X" and "@X" were also permitted, but
|
||||
having so many options is unnecessary and likely to
|
||||
cause confusion. */
|
||||
const $colName = opt.rowMode.substr(1);
|
||||
out.cbArg = (stmt)=>{
|
||||
const rc = stmt.get(Object.create(null))[$colName];
|
||||
return (undefined===rc)
|
||||
? toss3(capi.SQLITE_NOTFOUND,
|
||||
"exec(): unknown result column:",$colName)
|
||||
: rc;
|
||||
};
|
||||
break;
|
||||
}
|
||||
toss3("Invalid rowMode:",opt.rowMode);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
@ -1041,15 +884,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
and names. */) ? 0 : 1;
|
||||
evalFirstResult = false;
|
||||
if(arg.cbArg || resultRows){
|
||||
const cbArgCache = Object.create(null)
|
||||
/* 2nd arg for arg.cbArg, used by (at least) row-to-object
|
||||
converter */;
|
||||
for(; stmt.step(); stmt._lockedByExec = false){
|
||||
if(0===gotColNames++){
|
||||
stmt.getColumnNames(cbArgCache.columnNames = (opt.columnNames || []));
|
||||
}
|
||||
if(0===gotColNames++) stmt.getColumnNames(opt.columnNames);
|
||||
stmt._lockedByExec = true;
|
||||
const row = arg.cbArg(stmt,cbArgCache);
|
||||
const row = arg.cbArg(stmt);
|
||||
if(resultRows) resultRows.push(row);
|
||||
if(callback && false === callback.call(opt, row, stmt)){
|
||||
break;
|
||||
@ -1684,7 +1522,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
they are larger than 32 bits, else double or int32, depending
|
||||
on whether they have a fractional part. Booleans are bound as
|
||||
integer 0 or 1. It is not expected the distinction of binding
|
||||
doubles which have no fractional parts and integers is
|
||||
doubles which have no fractional parts is integers is
|
||||
significant for the majority of clients due to sqlite3's data
|
||||
typing model. If [BigInt] support is enabled then this
|
||||
routine will bind BigInt values as 64-bit integers if they'll
|
||||
@ -1868,7 +1706,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
an exception is thrown.
|
||||
|
||||
By default it will determine the data type of the result
|
||||
automatically. If passed a second argument, it must be one
|
||||
automatically. If passed a second arugment, it must be one
|
||||
of the enumeration values for sqlite3 types, which are
|
||||
defined as members of the sqlite3 module: SQLITE_INTEGER,
|
||||
SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value,
|
||||
@ -2068,26 +1906,16 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Functionally equivalent to DB(storageName,'c','kvvfs') except
|
||||
that it throws if the given storage name is not one of 'local'
|
||||
or 'session'.
|
||||
|
||||
As of version 3.46, the argument may optionally be an options
|
||||
object in the form:
|
||||
|
||||
{
|
||||
filename: 'session'|'local',
|
||||
... etc. (all options supported by the DB ctor)
|
||||
}
|
||||
|
||||
noting that the 'vfs' option supported by main DB
|
||||
constructor is ignored here: the vfs is always 'kvvfs'.
|
||||
*/
|
||||
sqlite3.oo1.JsStorageDb = function(storageName='session'){
|
||||
const opt = dbCtorHelper.normalizeArgs(...arguments);
|
||||
storageName = opt.filename;
|
||||
if('session'!==storageName && 'local'!==storageName){
|
||||
toss3("JsStorageDb db name must be one of 'session' or 'local'.");
|
||||
}
|
||||
opt.vfs = 'kvvfs';
|
||||
dbCtorHelper.call(this, opt);
|
||||
dbCtorHelper.call(this, {
|
||||
filename: storageName,
|
||||
flags: 'c',
|
||||
vfs: "kvvfs"
|
||||
});
|
||||
};
|
||||
const jdb = sqlite3.oo1.JsStorageDb;
|
||||
jdb.prototype = Object.create(DB.prototype);
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
This function expects a configuration object, intended to abstract
|
||||
away details specific to any given WASM environment, primarily so
|
||||
that it can be used without any direct dependency on
|
||||
that it can be used without any _direct_ dependency on
|
||||
Emscripten. (Note the default values for the config object!) The
|
||||
config object is only honored the first time this is
|
||||
called. Subsequent calls ignore the argument and return the same
|
||||
@ -98,37 +98,14 @@
|
||||
|
||||
The returned object is the top-level sqlite3 namespace object.
|
||||
|
||||
|
||||
Client code may optionally assign sqlite3ApiBootstrap.defaultConfig
|
||||
an object-type value before calling sqlite3ApiBootstrap() (without
|
||||
arguments) in order to tell that call to use this object as its
|
||||
default config value. The intention of this is to provide
|
||||
downstream clients with a reasonably flexible approach for plugging
|
||||
in an environment-suitable configuration without having to define a
|
||||
new global-scope symbol.
|
||||
|
||||
However, because clients who access this library via an
|
||||
Emscripten-hosted module will not have an opportunity to call
|
||||
sqlite3ApiBootstrap() themselves, nor to access it before it is
|
||||
called, an alternative option for setting the configuration is to
|
||||
define globalThis.sqlite3ApiConfig to an object. If it is set, it
|
||||
is used instead of sqlite3ApiBootstrap.defaultConfig if
|
||||
sqlite3ApiBootstrap() is called without arguments.
|
||||
|
||||
Both sqlite3ApiBootstrap.defaultConfig and
|
||||
globalThis.sqlite3ApiConfig get deleted by sqlite3ApiBootstrap()
|
||||
because any changes to them made after that point would have no
|
||||
useful effect.
|
||||
*/
|
||||
'use strict';
|
||||
globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
apiConfig = (globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig)
|
||||
){
|
||||
if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */
|
||||
(sqlite3ApiBootstrap.sqlite3.config || console).warn(
|
||||
"sqlite3ApiBootstrap() called multiple times.",
|
||||
"Config and external initializers are ignored on calls after the first."
|
||||
);
|
||||
console.warn("sqlite3ApiBootstrap() called multiple times.",
|
||||
"Config and external initializers are ignored on calls after the first.");
|
||||
return sqlite3ApiBootstrap.sqlite3;
|
||||
}
|
||||
const config = Object.assign(Object.create(null),{
|
||||
@ -137,16 +114,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
bigIntEnabled: (()=>{
|
||||
if('undefined'!==typeof Module){
|
||||
/* Emscripten module will contain HEAPU64 when built with
|
||||
-sWASM_BIGINT=1, else it will not.
|
||||
|
||||
As of emsdk 3.1.55, when building in strict mode, HEAPxyz
|
||||
are only available if _explicitly_ included in the exports,
|
||||
else they are not. We do not (as of 2024-03-04) use -sSTRICT
|
||||
for the canonical builds.
|
||||
*/
|
||||
if( !!Module.HEAPU64 ) return true;
|
||||
/* Else fall through and hope for the best. Nobody _really_
|
||||
builds this without BigInt support, do they? */
|
||||
-sWASM_BIGINT=1, else it will not. */
|
||||
return !!Module.HEAPU64;
|
||||
}
|
||||
return !!globalThis.BigInt64Array;
|
||||
})(),
|
||||
@ -180,15 +149,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
config[k] = config[k]();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
Eliminate any confusion about whether these config objects may
|
||||
be used after library initialization by eliminating the outward-facing
|
||||
objects...
|
||||
*/
|
||||
delete globalThis.sqlite3ApiConfig;
|
||||
delete sqlite3ApiBootstrap.defaultConfig;
|
||||
|
||||
/**
|
||||
The main sqlite3 binding API gets installed into this object,
|
||||
mimicking the C API as closely as we can. The numerous members
|
||||
@ -245,7 +205,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
|
||||
The exception's message is created by concatenating its
|
||||
arguments with a space between each, except for the
|
||||
two-args-with-an-object form and that the first argument will
|
||||
two-args-with-an-objec form and that the first argument will
|
||||
get coerced to a string, as described above, if it's an
|
||||
integer.
|
||||
|
||||
@ -1101,7 +1061,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
are undefined if the passed-in value did not come from
|
||||
this.pointer.
|
||||
*/
|
||||
restore: wasm.exports.sqlite3__wasm_pstack_restore,
|
||||
restore: wasm.exports.sqlite3_wasm_pstack_restore,
|
||||
/**
|
||||
Attempts to allocate the given number of bytes from the
|
||||
pstack. On success, it zeroes out a block of memory of the
|
||||
@ -1123,7 +1083,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
if('string'===typeof n && !(n = wasm.sizeofIR(n))){
|
||||
WasmAllocError.toss("Invalid value for pstack.alloc(",arguments[0],")");
|
||||
}
|
||||
return wasm.exports.sqlite3__wasm_pstack_alloc(n)
|
||||
return wasm.exports.sqlite3_wasm_pstack_alloc(n)
|
||||
|| WasmAllocError.toss("Could not allocate",n,
|
||||
"bytes from the pstack.");
|
||||
},
|
||||
@ -1203,10 +1163,10 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
*/
|
||||
pointer: {
|
||||
configurable: false, iterable: true, writeable: false,
|
||||
get: wasm.exports.sqlite3__wasm_pstack_ptr
|
||||
get: wasm.exports.sqlite3_wasm_pstack_ptr
|
||||
//Whether or not a setter as an alternative to restore() is
|
||||
//clearer or would just lead to confusion is unclear.
|
||||
//set: wasm.exports.sqlite3__wasm_pstack_restore
|
||||
//set: wasm.exports.sqlite3_wasm_pstack_restore
|
||||
},
|
||||
/**
|
||||
sqlite3.wasm.pstack.quota to the total number of bytes
|
||||
@ -1215,7 +1175,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
*/
|
||||
quota: {
|
||||
configurable: false, iterable: true, writeable: false,
|
||||
get: wasm.exports.sqlite3__wasm_pstack_quota
|
||||
get: wasm.exports.sqlite3_wasm_pstack_quota
|
||||
},
|
||||
/**
|
||||
sqlite3.wasm.pstack.remaining resolves to the amount of space
|
||||
@ -1223,7 +1183,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
*/
|
||||
remaining: {
|
||||
configurable: false, iterable: true, writeable: false,
|
||||
get: wasm.exports.sqlite3__wasm_pstack_remaining
|
||||
get: wasm.exports.sqlite3_wasm_pstack_remaining
|
||||
}
|
||||
})/*wasm.pstack properties*/;
|
||||
|
||||
@ -1296,14 +1256,14 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
}
|
||||
try{
|
||||
if(pdir && 0===wasm.xCallWrapped(
|
||||
'sqlite3__wasm_init_wasmfs', 'i32', ['string'], pdir
|
||||
'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir
|
||||
)){
|
||||
return __wasmfsOpfsDir = pdir;
|
||||
}else{
|
||||
return __wasmfsOpfsDir = "";
|
||||
}
|
||||
}catch(e){
|
||||
// sqlite3__wasm_init_wasmfs() is not available
|
||||
// sqlite3_wasm_init_wasmfs() is not available
|
||||
return __wasmfsOpfsDir = "";
|
||||
}
|
||||
};
|
||||
@ -1405,7 +1365,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
const zSchema = schema
|
||||
? (wasm.isPtr(schema) ? schema : wasm.scopedAllocCString(''+schema))
|
||||
: 0;
|
||||
let rc = wasm.exports.sqlite3__wasm_db_serialize(
|
||||
let rc = wasm.exports.sqlite3_wasm_db_serialize(
|
||||
pDb, zSchema, ppOut, pSize, 0
|
||||
);
|
||||
if(rc){
|
||||
@ -1431,7 +1391,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
or not provided, then "main" is assumed.
|
||||
*/
|
||||
capi.sqlite3_js_db_vfs =
|
||||
(dbPointer, dbName=0)=>util.sqlite3__wasm_db_vfs(dbPointer, dbName);
|
||||
(dbPointer, dbName=0)=>wasm.sqlite3_wasm_db_vfs(dbPointer, dbName);
|
||||
|
||||
/**
|
||||
A thin wrapper around capi.sqlite3_aggregate_context() which
|
||||
@ -1489,7 +1449,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
if(!util.isInt32(dataLen) || dataLen<0){
|
||||
SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file().");
|
||||
}
|
||||
const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen);
|
||||
const rc = wasm.sqlite3_wasm_posix_create_file(filename, pData, dataLen);
|
||||
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
|
||||
capi.sqlite3_js_rc_str(rc));
|
||||
}finally{
|
||||
@ -1591,7 +1551,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file().");
|
||||
}
|
||||
try{
|
||||
const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen);
|
||||
const rc = wasm.sqlite3_wasm_vfs_create_file(vfs, filename, pData, dataLen);
|
||||
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
|
||||
capi.sqlite3_js_rc_str(rc));
|
||||
}finally{
|
||||
@ -1712,12 +1672,12 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
*/
|
||||
capi.sqlite3_db_config = function(pDb, op, ...args){
|
||||
if(!this.s){
|
||||
this.s = wasm.xWrap('sqlite3__wasm_db_config_s','int',
|
||||
this.s = wasm.xWrap('sqlite3_wasm_db_config_s','int',
|
||||
['sqlite3*', 'int', 'string:static']
|
||||
/* MAINDBNAME requires a static string */);
|
||||
this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int',
|
||||
this.pii = wasm.xWrap('sqlite3_wasm_db_config_pii', 'int',
|
||||
['sqlite3*', 'int', '*','int', 'int']);
|
||||
this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int',
|
||||
this.ip = wasm.xWrap('sqlite3_wasm_db_config_ip','int',
|
||||
['sqlite3*', 'int', 'int','*']);
|
||||
}
|
||||
switch(op){
|
||||
@ -1838,7 +1798,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
/**
|
||||
Calls either sqlite3_result_error_nomem(), if e is-a
|
||||
WasmAllocError, or sqlite3_result_error(). In the latter case,
|
||||
the second argument is coerced to a string to create the error
|
||||
the second arugment is coerced to a string to create the error
|
||||
message.
|
||||
|
||||
The first argument is a (sqlite3_context*). Returns void.
|
||||
|
||||
@ -359,7 +359,6 @@
|
||||
|
||||
*/
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const util = sqlite3.util;
|
||||
sqlite3.initWorker1API = function(){
|
||||
'use strict';
|
||||
const toss = (...args)=>{throw new Error(args.join(' '))};
|
||||
@ -410,12 +409,12 @@ sqlite3.initWorker1API = function(){
|
||||
if(db){
|
||||
delete this.dbs[getDbId(db)];
|
||||
const filename = db.filename;
|
||||
const pVfs = util.sqlite3__wasm_db_vfs(db.pointer, 0);
|
||||
const pVfs = sqlite3.wasm.sqlite3_wasm_db_vfs(db.pointer, 0);
|
||||
db.close();
|
||||
const ddNdx = this.dbList.indexOf(db);
|
||||
if(ddNdx>=0) this.dbList.splice(ddNdx, 1);
|
||||
if(alsoUnlink && filename && pVfs){
|
||||
util.sqlite3__wasm_vfs_unlink(pVfs, filename);
|
||||
sqlite3.wasm.sqlite3_wasm_vfs_unlink(pVfs, filename);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -459,6 +458,11 @@ sqlite3.initWorker1API = function(){
|
||||
return wState.dbList[0] && getDbId(wState.dbList[0]);
|
||||
};
|
||||
|
||||
const guessVfs = function(filename){
|
||||
const m = /^file:.+(vfs=(\w+))/.exec(filename);
|
||||
return sqlite3.capi.sqlite3_vfs_find(m ? m[2] : 0);
|
||||
};
|
||||
|
||||
const isSpecialDbFilename = (n)=>{
|
||||
return ""===n || ':'===n[0];
|
||||
};
|
||||
@ -480,8 +484,36 @@ sqlite3.initWorker1API = function(){
|
||||
toss("Throwing because of simulateError flag.");
|
||||
}
|
||||
const rc = Object.create(null);
|
||||
let byteArray, pVfs;
|
||||
oargs.vfs = args.vfs;
|
||||
oargs.filename = args.filename || "";
|
||||
if(isSpecialDbFilename(args.filename)){
|
||||
oargs.filename = args.filename || "";
|
||||
}else{
|
||||
oargs.filename = args.filename;
|
||||
byteArray = args.byteArray;
|
||||
if(byteArray) pVfs = guessVfs(args.filename);
|
||||
}
|
||||
if(pVfs){
|
||||
/* 2022-11-02: this feature is as-yet untested except that
|
||||
sqlite3_wasm_vfs_create_file() has been tested from the
|
||||
browser dev console. */
|
||||
let pMem;
|
||||
try{
|
||||
pMem = sqlite3.wasm.allocFromTypedArray(byteArray);
|
||||
const rc = sqlite3.wasm.sqlite3_wasm_vfs_create_file(
|
||||
pVfs, oargs.filename, pMem, byteArray.byteLength
|
||||
);
|
||||
if(rc) sqlite3.SQLite3Error.toss(rc);
|
||||
}catch(e){
|
||||
throw new sqlite3.SQLite3Error(
|
||||
e.name+' creating '+args.filename+": "+e.message, {
|
||||
cause: e
|
||||
}
|
||||
);
|
||||
}finally{
|
||||
if(pMem) sqlite3.wasm.dealloc(pMem);
|
||||
}
|
||||
}
|
||||
const db = wState.open(oargs);
|
||||
rc.filename = db.filename;
|
||||
rc.persistent = !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs");
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
*/
|
||||
"use strict";
|
||||
const wPost = (type,...args)=>postMessage({type, payload:args});
|
||||
const installAsyncProxy = function(){
|
||||
const installAsyncProxy = function(self){
|
||||
const toss = function(...args){throw new Error(args.join(' '))};
|
||||
if(globalThis.window === globalThis){
|
||||
toss("This code cannot run from the main thread.",
|
||||
@ -87,6 +87,35 @@ const installAsyncProxy = function(){
|
||||
const log = (...args)=>logImpl(2, ...args);
|
||||
const warn = (...args)=>logImpl(1, ...args);
|
||||
const error = (...args)=>logImpl(0, ...args);
|
||||
const metrics = Object.create(null);
|
||||
metrics.reset = ()=>{
|
||||
let k;
|
||||
const r = (m)=>(m.count = m.time = m.wait = 0);
|
||||
for(k in state.opIds){
|
||||
r(metrics[k] = Object.create(null));
|
||||
}
|
||||
let s = metrics.s11n = Object.create(null);
|
||||
s = s.serialize = Object.create(null);
|
||||
s.count = s.time = 0;
|
||||
s = metrics.s11n.deserialize = Object.create(null);
|
||||
s.count = s.time = 0;
|
||||
};
|
||||
metrics.dump = ()=>{
|
||||
let k, n = 0, t = 0, w = 0;
|
||||
for(k in state.opIds){
|
||||
const m = metrics[k];
|
||||
n += m.count;
|
||||
t += m.time;
|
||||
w += m.wait;
|
||||
m.avgTime = (m.count && m.time) ? (m.time / m.count) : 0;
|
||||
}
|
||||
console.log(globalThis?.location?.href,
|
||||
"metrics for",globalThis?.location?.href,":\n",
|
||||
metrics,
|
||||
"\nTotal of",n,"op(s) for",t,"ms",
|
||||
"approx",w,"ms spent waiting on OPFS APIs.");
|
||||
console.log("Serialization metrics:",metrics.s11n);
|
||||
};
|
||||
|
||||
/**
|
||||
__openFiles is a map of sqlite3_file pointers (integers) to
|
||||
@ -236,34 +265,23 @@ const installAsyncProxy = function(){
|
||||
this.name = 'GetSyncHandleError';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Attempts to find a suitable SQLITE_xyz result code for Error
|
||||
object e. Returns either such a translation or rc if if it does
|
||||
not know how to translate the exception.
|
||||
*/
|
||||
GetSyncHandleError.convertRc = (e,rc)=>{
|
||||
if( e instanceof GetSyncHandleError ){
|
||||
if( e.cause.name==='NoModificationAllowedError'
|
||||
/* Inconsistent exception.name from Chrome/ium with the
|
||||
same exception.message text: */
|
||||
|| (e.cause.name==='DOMException'
|
||||
&& 0===e.cause.message.indexOf('Access Handles cannot')) ){
|
||||
return state.sq3Codes.SQLITE_BUSY;
|
||||
}else if( 'NotFoundError'===e.cause.name ){
|
||||
/**
|
||||
Maintenance reminder: SQLITE_NOTFOUND, though it looks like
|
||||
a good match, has different semantics than NotFoundError
|
||||
and is not suitable here.
|
||||
*/
|
||||
return state.sq3Codes.SQLITE_CANTOPEN;
|
||||
}
|
||||
}else if( 'NotFoundError'===e?.name ){
|
||||
return state.sq3Codes.SQLITE_CANTOPEN;
|
||||
if(1){
|
||||
return (
|
||||
e instanceof GetSyncHandleError
|
||||
&& ((e.cause.name==='NoModificationAllowedError')
|
||||
/* Inconsistent exception.name from Chrome/ium with the
|
||||
same exception.message text: */
|
||||
|| (e.cause.name==='DOMException'
|
||||
&& 0===e.cause.message.indexOf('Access Handles cannot')))
|
||||
) ? (
|
||||
/*console.warn("SQLITE_BUSY",e),*/
|
||||
state.sq3Codes.SQLITE_BUSY
|
||||
) : rc;
|
||||
}else{
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
Returns the sync access handle associated with the given file
|
||||
handle object (which must be a valid handle object, as created by
|
||||
@ -329,6 +347,37 @@ const installAsyncProxy = function(){
|
||||
if(fh.readOnly) toss(opName+"(): File is read-only: "+fh.filenameAbs);
|
||||
};
|
||||
|
||||
/**
|
||||
We track 2 different timers: the "metrics" timer records how much
|
||||
time we spend performing work. The "wait" timer records how much
|
||||
time we spend waiting on the underlying OPFS timer. See the calls
|
||||
to mTimeStart(), mTimeEnd(), wTimeStart(), and wTimeEnd()
|
||||
throughout this file to see how they're used.
|
||||
*/
|
||||
const __mTimer = Object.create(null);
|
||||
__mTimer.op = undefined;
|
||||
__mTimer.start = undefined;
|
||||
const mTimeStart = (op)=>{
|
||||
__mTimer.start = performance.now();
|
||||
__mTimer.op = op;
|
||||
//metrics[op] || toss("Maintenance required: missing metrics for",op);
|
||||
++metrics[op].count;
|
||||
};
|
||||
const mTimeEnd = ()=>(
|
||||
metrics[__mTimer.op].time += performance.now() - __mTimer.start
|
||||
);
|
||||
const __wTimer = Object.create(null);
|
||||
__wTimer.op = undefined;
|
||||
__wTimer.start = undefined;
|
||||
const wTimeStart = (op)=>{
|
||||
__wTimer.start = performance.now();
|
||||
__wTimer.op = op;
|
||||
//metrics[op] || toss("Maintenance required: missing metrics for",op);
|
||||
};
|
||||
const wTimeEnd = ()=>(
|
||||
metrics[__wTimer.op].wait += performance.now() - __wTimer.start
|
||||
);
|
||||
|
||||
/**
|
||||
Gets set to true by the 'opfs-async-shutdown' command to quit the
|
||||
wait loop. This is only intended for debugging purposes: we cannot
|
||||
@ -339,24 +388,37 @@ const installAsyncProxy = function(){
|
||||
|
||||
/**
|
||||
Asynchronous wrappers for sqlite3_vfs and sqlite3_io_methods
|
||||
methods, as well as helpers like mkdir().
|
||||
methods, as well as helpers like mkdir(). Maintenance reminder:
|
||||
members are in alphabetical order to simplify finding them.
|
||||
*/
|
||||
const vfsAsyncImpls = {
|
||||
'opfs-async-metrics': async ()=>{
|
||||
mTimeStart('opfs-async-metrics');
|
||||
metrics.dump();
|
||||
storeAndNotify('opfs-async-metrics', 0);
|
||||
mTimeEnd();
|
||||
},
|
||||
'opfs-async-shutdown': async ()=>{
|
||||
flagAsyncShutdown = true;
|
||||
storeAndNotify('opfs-async-shutdown', 0);
|
||||
},
|
||||
mkdir: async (dirname)=>{
|
||||
mTimeStart('mkdir');
|
||||
let rc = 0;
|
||||
wTimeStart('mkdir');
|
||||
try {
|
||||
await getDirForFilename(dirname+"/filepart", true);
|
||||
}catch(e){
|
||||
state.s11n.storeException(2,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR;
|
||||
}finally{
|
||||
wTimeEnd();
|
||||
}
|
||||
storeAndNotify('mkdir', rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xAccess: async (filename)=>{
|
||||
mTimeStart('xAccess');
|
||||
/* OPFS cannot support the full range of xAccess() queries
|
||||
sqlite3 calls for. We can essentially just tell if the file
|
||||
is accessible, but if it is then it's automatically writable
|
||||
@ -369,20 +431,26 @@ const installAsyncProxy = function(){
|
||||
accessible, non-0 means not accessible.
|
||||
*/
|
||||
let rc = 0;
|
||||
wTimeStart('xAccess');
|
||||
try{
|
||||
const [dh, fn] = await getDirForFilename(filename);
|
||||
await dh.getFileHandle(fn);
|
||||
}catch(e){
|
||||
state.s11n.storeException(2,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR;
|
||||
}finally{
|
||||
wTimeEnd();
|
||||
}
|
||||
storeAndNotify('xAccess', rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xClose: async function(fid/*sqlite3_file pointer*/){
|
||||
const opName = 'xClose';
|
||||
mTimeStart(opName);
|
||||
__implicitLocks.delete(fid);
|
||||
const fh = __openFiles[fid];
|
||||
let rc = 0;
|
||||
wTimeStart(opName);
|
||||
if(fh){
|
||||
delete __openFiles[fid];
|
||||
await closeSyncHandle(fh);
|
||||
@ -394,11 +462,15 @@ const installAsyncProxy = function(){
|
||||
state.s11n.serialize();
|
||||
rc = state.sq3Codes.SQLITE_NOTFOUND;
|
||||
}
|
||||
wTimeEnd();
|
||||
storeAndNotify(opName, rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xDelete: async function(...args){
|
||||
mTimeStart('xDelete');
|
||||
const rc = await vfsAsyncImpls.xDeleteNoWait(...args);
|
||||
storeAndNotify('xDelete', rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xDeleteNoWait: async function(filename, syncDir = 0, recursive = false){
|
||||
/* The syncDir flag is, for purposes of the VFS API's semantics,
|
||||
@ -414,6 +486,7 @@ const installAsyncProxy = function(){
|
||||
is false.
|
||||
*/
|
||||
let rc = 0;
|
||||
wTimeStart('xDelete');
|
||||
try {
|
||||
while(filename){
|
||||
const [hDir, filenamePart] = await getDirForFilename(filename, false);
|
||||
@ -429,11 +502,14 @@ const installAsyncProxy = function(){
|
||||
state.s11n.storeException(2,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_DELETE;
|
||||
}
|
||||
wTimeEnd();
|
||||
return rc;
|
||||
},
|
||||
xFileSize: async function(fid/*sqlite3_file pointer*/){
|
||||
mTimeStart('xFileSize');
|
||||
const fh = __openFiles[fid];
|
||||
let rc = 0;
|
||||
wTimeStart('xFileSize');
|
||||
try{
|
||||
const sz = await (await getSyncHandle(fh,'xFileSize')).getSize();
|
||||
state.s11n.serialize(Number(sz));
|
||||
@ -442,15 +518,19 @@ const installAsyncProxy = function(){
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
wTimeEnd();
|
||||
storeAndNotify('xFileSize', rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xLock: async function(fid/*sqlite3_file pointer*/,
|
||||
lockType/*SQLITE_LOCK_...*/){
|
||||
mTimeStart('xLock');
|
||||
const fh = __openFiles[fid];
|
||||
let rc = 0;
|
||||
const oldLockType = fh.xLock;
|
||||
fh.xLock = lockType;
|
||||
if( !fh.syncHandle ){
|
||||
wTimeStart('xLock');
|
||||
try {
|
||||
await getSyncHandle(fh,'xLock');
|
||||
__implicitLocks.delete(fid);
|
||||
@ -459,14 +539,18 @@ const installAsyncProxy = function(){
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_LOCK);
|
||||
fh.xLock = oldLockType;
|
||||
}
|
||||
wTimeEnd();
|
||||
}
|
||||
storeAndNotify('xLock',rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xOpen: async function(fid/*sqlite3_file pointer*/, filename,
|
||||
flags/*SQLITE_OPEN_...*/,
|
||||
opfsFlags/*OPFS_...*/){
|
||||
const opName = 'xOpen';
|
||||
mTimeStart(opName);
|
||||
const create = (state.sq3Codes.SQLITE_OPEN_CREATE & flags);
|
||||
wTimeStart('xOpen');
|
||||
try{
|
||||
let hDir, filenamePart;
|
||||
try {
|
||||
@ -474,17 +558,12 @@ const installAsyncProxy = function(){
|
||||
}catch(e){
|
||||
state.s11n.storeException(1,e);
|
||||
storeAndNotify(opName, state.sq3Codes.SQLITE_NOTFOUND);
|
||||
mTimeEnd();
|
||||
wTimeEnd();
|
||||
return;
|
||||
}
|
||||
if( state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN & opfsFlags ){
|
||||
try{
|
||||
await hDir.removeEntry(filenamePart);
|
||||
}catch(e){
|
||||
/* ignoring */
|
||||
//warn("Ignoring failed Unlink of",filename,":",e);
|
||||
}
|
||||
}
|
||||
const hFile = await hDir.getFileHandle(filenamePart, {create});
|
||||
wTimeEnd();
|
||||
const fh = Object.assign(Object.create(null),{
|
||||
fid: fid,
|
||||
filenameAbs: filename,
|
||||
@ -499,50 +578,76 @@ const installAsyncProxy = function(){
|
||||
fh.releaseImplicitLocks =
|
||||
(opfsFlags & state.opfsFlags.OPFS_UNLOCK_ASAP)
|
||||
|| state.opfsFlags.defaultUnlockAsap;
|
||||
if(0 /* this block is modelled after something wa-sqlite
|
||||
does but it leads to immediate contention on journal files.
|
||||
Update: this approach reportedly only works for DELETE journal
|
||||
mode. */
|
||||
&& (0===(flags & state.sq3Codes.SQLITE_OPEN_MAIN_DB))){
|
||||
/* sqlite does not lock these files, so go ahead and grab an OPFS
|
||||
lock. */
|
||||
fh.xLock = "xOpen"/* Truthy value to keep entry from getting
|
||||
flagged as auto-locked. String value so
|
||||
that we can easily distinguish is later
|
||||
if needed. */;
|
||||
await getSyncHandle(fh,'xOpen');
|
||||
}
|
||||
__openFiles[fid] = fh;
|
||||
storeAndNotify(opName, 0);
|
||||
}catch(e){
|
||||
wTimeEnd();
|
||||
error(opName,e);
|
||||
state.s11n.storeException(1,e);
|
||||
storeAndNotify(opName, state.sq3Codes.SQLITE_IOERR);
|
||||
}
|
||||
mTimeEnd();
|
||||
},
|
||||
xRead: async function(fid/*sqlite3_file pointer*/,n,offset64){
|
||||
mTimeStart('xRead');
|
||||
let rc = 0, nRead;
|
||||
const fh = __openFiles[fid];
|
||||
try{
|
||||
wTimeStart('xRead');
|
||||
nRead = (await getSyncHandle(fh,'xRead')).read(
|
||||
fh.sabView.subarray(0, n),
|
||||
{at: Number(offset64)}
|
||||
);
|
||||
wTimeEnd();
|
||||
if(nRead < n){/* Zero-fill remaining bytes */
|
||||
fh.sabView.fill(0, nRead, n);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_SHORT_READ;
|
||||
}
|
||||
}catch(e){
|
||||
if(undefined===nRead) wTimeEnd();
|
||||
error("xRead() failed",e,fh);
|
||||
state.s11n.storeException(1,e);
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_READ);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
storeAndNotify('xRead',rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xSync: async function(fid/*sqlite3_file pointer*/,flags/*ignored*/){
|
||||
mTimeStart('xSync');
|
||||
const fh = __openFiles[fid];
|
||||
let rc = 0;
|
||||
if(!fh.readOnly && fh.syncHandle){
|
||||
try {
|
||||
wTimeStart('xSync');
|
||||
await fh.syncHandle.flush();
|
||||
}catch(e){
|
||||
state.s11n.storeException(2,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_FSYNC;
|
||||
}
|
||||
wTimeEnd();
|
||||
}
|
||||
storeAndNotify('xSync',rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xTruncate: async function(fid/*sqlite3_file pointer*/,size){
|
||||
mTimeStart('xTruncate');
|
||||
let rc = 0;
|
||||
const fh = __openFiles[fid];
|
||||
wTimeStart('xTruncate');
|
||||
try{
|
||||
affirmNotRO('xTruncate', fh);
|
||||
await (await getSyncHandle(fh,'xTruncate')).truncate(size);
|
||||
@ -552,25 +657,33 @@ const installAsyncProxy = function(){
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_TRUNCATE);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
wTimeEnd();
|
||||
storeAndNotify('xTruncate',rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xUnlock: async function(fid/*sqlite3_file pointer*/,
|
||||
lockType/*SQLITE_LOCK_...*/){
|
||||
mTimeStart('xUnlock');
|
||||
let rc = 0;
|
||||
const fh = __openFiles[fid];
|
||||
if( state.sq3Codes.SQLITE_LOCK_NONE===lockType
|
||||
&& fh.syncHandle ){
|
||||
wTimeStart('xUnlock');
|
||||
try { await closeSyncHandle(fh) }
|
||||
catch(e){
|
||||
state.s11n.storeException(1,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_UNLOCK;
|
||||
}
|
||||
wTimeEnd();
|
||||
}
|
||||
storeAndNotify('xUnlock',rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
xWrite: async function(fid/*sqlite3_file pointer*/,n,offset64){
|
||||
mTimeStart('xWrite');
|
||||
let rc;
|
||||
const fh = __openFiles[fid];
|
||||
wTimeStart('xWrite');
|
||||
try{
|
||||
affirmNotRO('xWrite', fh);
|
||||
rc = (
|
||||
@ -584,7 +697,9 @@ const installAsyncProxy = function(){
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_WRITE);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
wTimeEnd();
|
||||
storeAndNotify('xWrite',rc);
|
||||
mTimeEnd();
|
||||
}
|
||||
}/*vfsAsyncImpls*/;
|
||||
|
||||
@ -618,6 +733,8 @@ const installAsyncProxy = function(){
|
||||
}
|
||||
};
|
||||
state.s11n.deserialize = function(clear=false){
|
||||
++metrics.s11n.deserialize.count;
|
||||
const t = performance.now();
|
||||
const argc = viewU8[0];
|
||||
const rc = argc ? [] : null;
|
||||
if(argc){
|
||||
@ -642,9 +759,12 @@ const installAsyncProxy = function(){
|
||||
}
|
||||
if(clear) viewU8[0] = 0;
|
||||
//log("deserialize:",argc, rc);
|
||||
metrics.s11n.deserialize.time += performance.now() - t;
|
||||
return rc;
|
||||
};
|
||||
state.s11n.serialize = function(...args){
|
||||
const t = performance.now();
|
||||
++metrics.s11n.serialize.count;
|
||||
if(args.length){
|
||||
//log("serialize():",args);
|
||||
const typeIds = [];
|
||||
@ -675,6 +795,7 @@ const installAsyncProxy = function(){
|
||||
}else{
|
||||
viewU8[0] = 0;
|
||||
}
|
||||
metrics.s11n.serialize.time += performance.now() - t;
|
||||
};
|
||||
|
||||
state.s11n.storeException = state.asyncS11nExceptions
|
||||
@ -756,6 +877,7 @@ const installAsyncProxy = function(){
|
||||
}
|
||||
});
|
||||
initS11n();
|
||||
metrics.reset();
|
||||
log("init state",state);
|
||||
wPost('opfs-async-inited');
|
||||
waitLoop();
|
||||
@ -768,6 +890,9 @@ const installAsyncProxy = function(){
|
||||
waitLoop();
|
||||
}
|
||||
break;
|
||||
case 'opfs-async-metrics':
|
||||
metrics.dump();
|
||||
break;
|
||||
}
|
||||
};
|
||||
wPost('opfs-async-loaded');
|
||||
@ -786,5 +911,5 @@ if(!globalThis.SharedArrayBuffer){
|
||||
!navigator?.storage?.getDirectory){
|
||||
wPost('opfs-unavailable',"Missing required OPFS APIs.");
|
||||
}else{
|
||||
installAsyncProxy();
|
||||
installAsyncProxy(self);
|
||||
}
|
||||
|
||||
@ -10,13 +10,19 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
This file installs sqlite3.vtab, a namespace of helpers for use in
|
||||
the creation of JavaScript implementations virtual tables.
|
||||
This file installs sqlite3.vfs, and object which exists to assist
|
||||
in the creation of JavaScript implementations of sqlite3_vfs, along
|
||||
with its virtual table counterpart, sqlite3.vtab.
|
||||
*/
|
||||
'use strict';
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
|
||||
const vtab = Object.create(null);
|
||||
const vfs = Object.create(null), vtab = Object.create(null);
|
||||
|
||||
const StructBinder = sqlite3.StructBinder
|
||||
/* we require a local alias b/c StructBinder is removed from the sqlite3
|
||||
object during the final steps of the API cleanup. */;
|
||||
sqlite3.vfs = vfs;
|
||||
sqlite3.vtab = vtab;
|
||||
|
||||
const sii = capi.sqlite3_index_info;
|
||||
@ -66,6 +72,257 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
return asPtr ? ptr : new sii.sqlite3_index_orderby(ptr);
|
||||
};
|
||||
|
||||
/**
|
||||
Installs a StructBinder-bound function pointer member of the
|
||||
given name and function in the given StructType target object.
|
||||
|
||||
It creates a WASM proxy for the given function and arranges for
|
||||
that proxy to be cleaned up when tgt.dispose() is called. Throws
|
||||
on the slightest hint of error, e.g. tgt is-not-a StructType,
|
||||
name does not map to a struct-bound member, etc.
|
||||
|
||||
As a special case, if the given function is a pointer, then
|
||||
`wasm.functionEntry()` is used to validate that it is a known
|
||||
function. If so, it is used as-is with no extra level of proxying
|
||||
or cleanup, else an exception is thrown. It is legal to pass a
|
||||
value of 0, indicating a NULL pointer, with the caveat that 0
|
||||
_is_ a legal function pointer in WASM but it will not be accepted
|
||||
as such _here_. (Justification: the function at address zero must
|
||||
be one which initially came from the WASM module, not a method we
|
||||
want to bind to a virtual table or VFS.)
|
||||
|
||||
This function returns a proxy for itself which is bound to tgt
|
||||
and takes 2 args (name,func). That function returns the same
|
||||
thing as this one, permitting calls to be chained.
|
||||
|
||||
If called with only 1 arg, it has no side effects but returns a
|
||||
func with the same signature as described above.
|
||||
|
||||
ACHTUNG: because we cannot generically know how to transform JS
|
||||
exceptions into result codes, the installed functions do no
|
||||
automatic catching of exceptions. It is critical, to avoid
|
||||
undefined behavior in the C layer, that methods mapped via
|
||||
this function do not throw. The exception, as it were, to that
|
||||
rule is...
|
||||
|
||||
If applyArgcCheck is true then each JS function (as opposed to
|
||||
function pointers) gets wrapped in a proxy which asserts that it
|
||||
is passed the expected number of arguments, throwing if the
|
||||
argument count does not match expectations. That is only intended
|
||||
for dev-time usage for sanity checking, and will leave the C
|
||||
environment in an undefined state.
|
||||
*/
|
||||
const installMethod = function callee(
|
||||
tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck
|
||||
){
|
||||
if(!(tgt instanceof StructBinder.StructType)){
|
||||
toss("Usage error: target object is-not-a StructType.");
|
||||
}else if(!(func instanceof Function) && !wasm.isPtr(func)){
|
||||
toss("Usage errror: expecting a Function or WASM pointer to one.");
|
||||
}
|
||||
if(1===arguments.length){
|
||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||
}
|
||||
if(!callee.argcProxy){
|
||||
callee.argcProxy = function(tgt, funcName, func,sig){
|
||||
return function(...args){
|
||||
if(func.length!==arguments.length){
|
||||
toss("Argument mismatch for",
|
||||
tgt.structInfo.name+"::"+funcName
|
||||
+": Native signature is:",sig);
|
||||
}
|
||||
return func.apply(this, args);
|
||||
}
|
||||
};
|
||||
/* An ondispose() callback for use with
|
||||
StructBinder-created types. */
|
||||
callee.removeFuncList = function(){
|
||||
if(this.ondispose.__removeFuncList){
|
||||
this.ondispose.__removeFuncList.forEach(
|
||||
(v,ndx)=>{
|
||||
if('number'===typeof v){
|
||||
try{wasm.uninstallFunction(v)}
|
||||
catch(e){/*ignore*/}
|
||||
}
|
||||
/* else it's a descriptive label for the next number in
|
||||
the list. */
|
||||
}
|
||||
);
|
||||
delete this.ondispose.__removeFuncList;
|
||||
}
|
||||
};
|
||||
}/*static init*/
|
||||
const sigN = tgt.memberSignature(name);
|
||||
if(sigN.length<2){
|
||||
toss("Member",name,"does not have a function pointer signature:",sigN);
|
||||
}
|
||||
const memKey = tgt.memberKey(name);
|
||||
const fProxy = (applyArgcCheck && !wasm.isPtr(func))
|
||||
/** This middle-man proxy is only for use during development, to
|
||||
confirm that we always pass the proper number of
|
||||
arguments. We know that the C-level code will always use the
|
||||
correct argument count. */
|
||||
? callee.argcProxy(tgt, memKey, func, sigN)
|
||||
: func;
|
||||
if(wasm.isPtr(fProxy)){
|
||||
if(fProxy && !wasm.functionEntry(fProxy)){
|
||||
toss("Pointer",fProxy,"is not a WASM function table entry.");
|
||||
}
|
||||
tgt[memKey] = fProxy;
|
||||
}else{
|
||||
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
||||
tgt[memKey] = pFunc;
|
||||
if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){
|
||||
tgt.addOnDispose('ondispose.__removeFuncList handler',
|
||||
callee.removeFuncList);
|
||||
tgt.ondispose.__removeFuncList = [];
|
||||
}
|
||||
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
||||
}
|
||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||
}/*installMethod*/;
|
||||
installMethod.installMethodArgcCheck = false;
|
||||
|
||||
/**
|
||||
Installs methods into the given StructType-type instance. Each
|
||||
entry in the given methods object must map to a known member of
|
||||
the given StructType, else an exception will be triggered. See
|
||||
installMethod() for more details, including the semantics of the
|
||||
3rd argument.
|
||||
|
||||
As an exception to the above, if any two or more methods in the
|
||||
2nd argument are the exact same function, installMethod() is
|
||||
_not_ called for the 2nd and subsequent instances, and instead
|
||||
those instances get assigned the same method pointer which is
|
||||
created for the first instance. This optimization is primarily to
|
||||
accommodate special handling of sqlite3_module::xConnect and
|
||||
xCreate methods.
|
||||
|
||||
On success, returns its first argument. Throws on error.
|
||||
*/
|
||||
const installMethods = function(
|
||||
structInstance, methods, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||
){
|
||||
const seen = new Map /* map of <Function, memberName> */;
|
||||
for(const k of Object.keys(methods)){
|
||||
const m = methods[k];
|
||||
const prior = seen.get(m);
|
||||
if(prior){
|
||||
const mkey = structInstance.memberKey(k);
|
||||
structInstance[mkey] = structInstance[structInstance.memberKey(prior)];
|
||||
}else{
|
||||
installMethod(structInstance, k, m, applyArgcCheck);
|
||||
seen.set(m, k);
|
||||
}
|
||||
}
|
||||
return structInstance;
|
||||
};
|
||||
|
||||
/**
|
||||
Equivalent to calling installMethod(this,...arguments) with a
|
||||
first argument of this object. If called with 1 or 2 arguments
|
||||
and the first is an object, it's instead equivalent to calling
|
||||
installMethods(this,...arguments).
|
||||
*/
|
||||
StructBinder.StructType.prototype.installMethod = function callee(
|
||||
name, func, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||
){
|
||||
return (arguments.length < 3 && name && 'object'===typeof name)
|
||||
? installMethods(this, ...arguments)
|
||||
: installMethod(this, ...arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
Equivalent to calling installMethods() with a first argument
|
||||
of this object.
|
||||
*/
|
||||
StructBinder.StructType.prototype.installMethods = function(
|
||||
methods, applyArgcCheck = installMethod.installMethodArgcCheck
|
||||
){
|
||||
return installMethods(this, methods, applyArgcCheck);
|
||||
};
|
||||
|
||||
/**
|
||||
Uses sqlite3_vfs_register() to register this
|
||||
sqlite3.capi.sqlite3_vfs. This object must have already been
|
||||
filled out properly. If the first argument is truthy, the VFS is
|
||||
registered as the default VFS, else it is not.
|
||||
|
||||
On success, returns this object. Throws on error.
|
||||
*/
|
||||
capi.sqlite3_vfs.prototype.registerVfs = function(asDefault=false){
|
||||
if(!(this instanceof sqlite3.capi.sqlite3_vfs)){
|
||||
toss("Expecting a sqlite3_vfs-type argument.");
|
||||
}
|
||||
const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0);
|
||||
if(rc){
|
||||
toss("sqlite3_vfs_register(",this,") failed with rc",rc);
|
||||
}
|
||||
if(this.pointer !== capi.sqlite3_vfs_find(this.$zName)){
|
||||
toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS",
|
||||
this);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
A wrapper for installMethods() or registerVfs() to reduce
|
||||
installation of a VFS and/or its I/O methods to a single
|
||||
call.
|
||||
|
||||
Accepts an object which contains the properties "io" and/or
|
||||
"vfs", each of which is itself an object with following properties:
|
||||
|
||||
- `struct`: an sqlite3.StructType-type struct. This must be a
|
||||
populated (except for the methods) object of type
|
||||
sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the
|
||||
"vfs" entry).
|
||||
|
||||
- `methods`: an object mapping sqlite3_io_methods method names
|
||||
(e.g. 'xClose') to JS implementations of those methods. The JS
|
||||
implementations must be call-compatible with their native
|
||||
counterparts.
|
||||
|
||||
For each of those object, this function passes its (`struct`,
|
||||
`methods`, (optional) `applyArgcCheck`) properties to
|
||||
installMethods().
|
||||
|
||||
If the `vfs` entry is set then:
|
||||
|
||||
- Its `struct` property's registerVfs() is called. The
|
||||
`vfs` entry may optionally have an `asDefault` property, which
|
||||
gets passed as the argument to registerVfs().
|
||||
|
||||
- If `struct.$zName` is falsy and the entry has a string-type
|
||||
`name` property, `struct.$zName` is set to the C-string form of
|
||||
that `name` value before registerVfs() is called. That string
|
||||
gets added to the on-dispose state of the struct.
|
||||
|
||||
On success returns this object. Throws on error.
|
||||
*/
|
||||
vfs.installVfs = function(opt){
|
||||
let count = 0;
|
||||
const propList = ['io','vfs'];
|
||||
for(const key of propList){
|
||||
const o = opt[key];
|
||||
if(o){
|
||||
++count;
|
||||
installMethods(o.struct, o.methods, !!o.applyArgcCheck);
|
||||
if('vfs'===key){
|
||||
if(!o.struct.$zName && 'string'===typeof o.name){
|
||||
o.struct.addOnDispose(
|
||||
o.struct.$zName = wasm.allocCString(o.name)
|
||||
);
|
||||
}
|
||||
o.struct.registerVfs(!!o.asDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!count) toss("Misuse: installVfs() options object requires at least",
|
||||
"one of:", propList);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
Internal factory function for xVtab and xCursor impls.
|
||||
*/
|
||||
@ -199,6 +456,30 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
*/
|
||||
vtab.xIndexInfo = (pIdxInfo)=>new capi.sqlite3_index_info(pIdxInfo);
|
||||
|
||||
/**
|
||||
Given an error object, this function returns
|
||||
sqlite3.capi.SQLITE_NOMEM if (e instanceof
|
||||
sqlite3.WasmAllocError), else it returns its
|
||||
second argument. Its intended usage is in the methods
|
||||
of a sqlite3_vfs or sqlite3_module:
|
||||
|
||||
```
|
||||
try{
|
||||
let rc = ...
|
||||
return rc;
|
||||
}catch(e){
|
||||
return sqlite3.vtab.exceptionToRc(e, sqlite3.capi.SQLITE_XYZ);
|
||||
// where SQLITE_XYZ is some call-appropriate result code.
|
||||
}
|
||||
```
|
||||
*/
|
||||
/**vfs.exceptionToRc = vtab.exceptionToRc =
|
||||
(e, defaultRc=capi.SQLITE_ERROR)=>(
|
||||
(e instanceof sqlite3.WasmAllocError)
|
||||
? capi.SQLITE_NOMEM
|
||||
: defaultRc
|
||||
);*/
|
||||
|
||||
/**
|
||||
Given an sqlite3_module method name and error object, this
|
||||
function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof
|
||||
@ -244,6 +525,20 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
};
|
||||
vtab.xError.errorReporter = 1 ? console.error.bind(console) : false;
|
||||
|
||||
/**
|
||||
"The problem" with this is that it introduces an outer function with
|
||||
a different arity than the passed-in method callback. That means we
|
||||
cannot do argc validation on these. Additionally, some methods (namely
|
||||
xConnect) may have call-specific error handling. It would be a shame to
|
||||
hard-coded that per-method support in this function.
|
||||
*/
|
||||
/** vtab.methodCatcher = function(methodName, method, defaultErrRc=capi.SQLITE_ERROR){
|
||||
return function(...args){
|
||||
try { method(...args); }
|
||||
}catch(e){ return vtab.xError(methodName, e, defaultRc) }
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
A helper for sqlite3_vtab::xRowid() and xUpdate()
|
||||
implementations. It must be passed the final argument to one of
|
||||
@ -390,12 +685,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
remethods[k] = fwrap(k, m);
|
||||
}
|
||||
}
|
||||
mod.installMethods(remethods, false);
|
||||
installMethods(mod, remethods, false);
|
||||
}else{
|
||||
// No automatic exception handling. Trust the client
|
||||
// to not throw.
|
||||
mod.installMethods(
|
||||
methods, !!opt.applyArgcCheck/*undocumented option*/
|
||||
installMethods(
|
||||
mod, methods, !!opt.applyArgcCheck/*undocumented option*/
|
||||
);
|
||||
}
|
||||
if(0===mod.$iVersion){
|
||||
@ -1,103 +0,0 @@
|
||||
/*
|
||||
** 2022-11-30
|
||||
**
|
||||
** 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 installs sqlite3.vfs, a namespace of helpers for use in
|
||||
the creation of JavaScript implementations of sqlite3_vfs.
|
||||
*/
|
||||
'use strict';
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
|
||||
const vfs = Object.create(null);
|
||||
sqlite3.vfs = vfs;
|
||||
|
||||
/**
|
||||
Uses sqlite3_vfs_register() to register this
|
||||
sqlite3.capi.sqlite3_vfs instance. This object must have already
|
||||
been filled out properly. If the first argument is truthy, the
|
||||
VFS is registered as the default VFS, else it is not.
|
||||
|
||||
On success, returns this object. Throws on error.
|
||||
*/
|
||||
capi.sqlite3_vfs.prototype.registerVfs = function(asDefault=false){
|
||||
if(!(this instanceof sqlite3.capi.sqlite3_vfs)){
|
||||
toss("Expecting a sqlite3_vfs-type argument.");
|
||||
}
|
||||
const rc = capi.sqlite3_vfs_register(this, asDefault ? 1 : 0);
|
||||
if(rc){
|
||||
toss("sqlite3_vfs_register(",this,") failed with rc",rc);
|
||||
}
|
||||
if(this.pointer !== capi.sqlite3_vfs_find(this.$zName)){
|
||||
toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS",
|
||||
this);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
A wrapper for
|
||||
sqlite3.StructBinder.StructType.prototype.installMethods() or
|
||||
registerVfs() to reduce installation of a VFS and/or its I/O
|
||||
methods to a single call.
|
||||
|
||||
Accepts an object which contains the properties "io" and/or
|
||||
"vfs", each of which is itself an object with following properties:
|
||||
|
||||
- `struct`: an sqlite3.StructBinder.StructType-type struct. This
|
||||
must be a populated (except for the methods) object of type
|
||||
sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the
|
||||
"vfs" entry).
|
||||
|
||||
- `methods`: an object mapping sqlite3_io_methods method names
|
||||
(e.g. 'xClose') to JS implementations of those methods. The JS
|
||||
implementations must be call-compatible with their native
|
||||
counterparts.
|
||||
|
||||
For each of those object, this function passes its (`struct`,
|
||||
`methods`, (optional) `applyArgcCheck`) properties to
|
||||
installMethods().
|
||||
|
||||
If the `vfs` entry is set then:
|
||||
|
||||
- Its `struct` property's registerVfs() is called. The
|
||||
`vfs` entry may optionally have an `asDefault` property, which
|
||||
gets passed as the argument to registerVfs().
|
||||
|
||||
- If `struct.$zName` is falsy and the entry has a string-type
|
||||
`name` property, `struct.$zName` is set to the C-string form of
|
||||
that `name` value before registerVfs() is called. That string
|
||||
gets added to the on-dispose state of the struct.
|
||||
|
||||
On success returns this object. Throws on error.
|
||||
*/
|
||||
vfs.installVfs = function(opt){
|
||||
let count = 0;
|
||||
const propList = ['io','vfs'];
|
||||
for(const key of propList){
|
||||
const o = opt[key];
|
||||
if(o){
|
||||
++count;
|
||||
o.struct.installMethods(o.methods, !!o.applyArgcCheck);
|
||||
if('vfs'===key){
|
||||
if(!o.struct.$zName && 'string'===typeof o.name){
|
||||
o.struct.addOnDispose(
|
||||
o.struct.$zName = wasm.allocCString(o.name)
|
||||
);
|
||||
}
|
||||
o.struct.registerVfs(!!o.asDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!count) toss("Misuse: installVfs() options object requires at least",
|
||||
"one of:", propList);
|
||||
return this;
|
||||
};
|
||||
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
||||
@ -1137,9 +1137,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
existing content. Throws if the pool has no available file slots,
|
||||
on I/O error, or if the input does not appear to be a
|
||||
database. In the latter case, only a cursory examination is made.
|
||||
Results are undefined if the given db name refers to an opened
|
||||
db. Note that this routine is _only_ for importing database
|
||||
files, not arbitrary files, the reason being that this VFS will
|
||||
Note that this routine is _only_ for importing database files,
|
||||
not arbitrary files, the reason being that this VFS will
|
||||
automatically clean up any non-database files so importing them
|
||||
is pointless.
|
||||
|
||||
|
||||
@ -245,8 +245,7 @@ const installOpfsVfs = function callee(options){
|
||||
opfsIoMethods.$iVersion = 1;
|
||||
opfsVfs.$iVersion = 2/*yes, two*/;
|
||||
opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
|
||||
opfsVfs.$mxPathname = 1024/* sure, why not? The OPFS name length limit
|
||||
is undocumented/unspecified. */;
|
||||
opfsVfs.$mxPathname = 1024/*sure, why not?*/;
|
||||
opfsVfs.$zName = wasm.allocCString("opfs");
|
||||
// All C-side memory of opfsVfs is zeroed out, but just to be explicit:
|
||||
opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null;
|
||||
@ -392,7 +391,6 @@ const installOpfsVfs = function callee(options){
|
||||
'SQLITE_ACCESS_EXISTS',
|
||||
'SQLITE_ACCESS_READWRITE',
|
||||
'SQLITE_BUSY',
|
||||
'SQLITE_CANTOPEN',
|
||||
'SQLITE_ERROR',
|
||||
'SQLITE_IOERR',
|
||||
'SQLITE_IOERR_ACCESS',
|
||||
@ -424,28 +422,13 @@ const installOpfsVfs = function callee(options){
|
||||
});
|
||||
state.opfsFlags = Object.assign(Object.create(null),{
|
||||
/**
|
||||
Flag for use with xOpen(). URI flag "opfs-unlock-asap=1"
|
||||
enables this. See defaultUnlockAsap, below.
|
||||
Flag for use with xOpen(). "opfs-unlock-asap=1" enables
|
||||
this. See defaultUnlockAsap, below.
|
||||
*/
|
||||
OPFS_UNLOCK_ASAP: 0x01,
|
||||
/**
|
||||
Flag for use with xOpen(). URI flag "delete-before-open=1"
|
||||
tells the VFS to delete the db file before attempting to open
|
||||
it. This can be used, e.g., to replace a db which has been
|
||||
corrupted (without forcing us to expose a delete/unlink()
|
||||
function in the public API).
|
||||
|
||||
Failure to unlink the file is ignored but may lead to
|
||||
downstream errors. An unlink can fail if, e.g., another tab
|
||||
has the handle open.
|
||||
|
||||
It goes without saying that deleting a file out from under another
|
||||
instance results in Undefined Behavior.
|
||||
*/
|
||||
OPFS_UNLINK_BEFORE_OPEN: 0x02,
|
||||
/**
|
||||
If true, any async routine which implicitly acquires a sync
|
||||
access handle (i.e. an OPFS lock) will release that lock at
|
||||
access handle (i.e. an OPFS lock) will release that locks at
|
||||
the end of the call which acquires it. If false, such
|
||||
"autolocks" are not released until the VFS is idle for some
|
||||
brief amount of time.
|
||||
@ -472,22 +455,9 @@ const installOpfsVfs = function callee(options){
|
||||
Atomics.notify(state.sabOPView, state.opIds.whichOp)
|
||||
/* async thread will take over here */;
|
||||
const t = performance.now();
|
||||
while('not-equal'!==Atomics.wait(state.sabOPView, state.opIds.rc, -1)){
|
||||
/*
|
||||
The reason for this loop is buried in the details of a long
|
||||
discussion at:
|
||||
|
||||
https://github.com/sqlite/sqlite-wasm/issues/12
|
||||
|
||||
Summary: in at least one browser flavor, under high loads,
|
||||
the wait()/notify() pairings can get out of sync. Calling
|
||||
wait() here until it returns 'not-equal' gets them back in
|
||||
sync.
|
||||
*/
|
||||
}
|
||||
/* When the above wait() call returns 'not-equal', the async
|
||||
half will have completed the operation and reported its results
|
||||
in the state.opIds.rc slot of the SAB. */
|
||||
Atomics.wait(state.sabOPView, state.opIds.rc, -1)
|
||||
/* When this wait() call returns, the async half will have
|
||||
completed the operation and reported its results. */;
|
||||
const rc = Atomics.load(state.sabOPView, state.opIds.rc);
|
||||
metrics[op].wait += performance.now() - t;
|
||||
if(rc && state.asyncS11nExceptions){
|
||||
@ -734,18 +704,9 @@ const installOpfsVfs = function callee(options){
|
||||
involve an inherent race condition. For the time being,
|
||||
pending a better solution, we simply report whether the
|
||||
given pFile is open.
|
||||
|
||||
Update 2024-06-12: based on forum discussions, this
|
||||
function now always sets pOut to 0 (false):
|
||||
|
||||
https://sqlite.org/forum/forumpost/a2f573b00cda1372
|
||||
*/
|
||||
if(1){
|
||||
wasm.poke(pOut, 0, 'i32');
|
||||
}else{
|
||||
const f = __openFiles[pFile];
|
||||
wasm.poke(pOut, f.lockType ? 1 : 0, 'i32');
|
||||
}
|
||||
const f = __openFiles[pFile];
|
||||
wasm.poke(pOut, f.lockType ? 1 : 0, 'i32');
|
||||
return 0;
|
||||
},
|
||||
xClose: function(pFile){
|
||||
@ -761,6 +722,7 @@ const installOpfsVfs = function callee(options){
|
||||
return rc;
|
||||
},
|
||||
xDeviceCharacteristics: function(pFile){
|
||||
//debug("xDeviceCharacteristics(",pFile,")");
|
||||
return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
||||
},
|
||||
xFileControl: function(pFile, opId, pArg){
|
||||
@ -912,17 +874,13 @@ const installOpfsVfs = function callee(options){
|
||||
let opfsFlags = 0;
|
||||
if(0===zName){
|
||||
zName = randomFilename();
|
||||
}else if(wasm.isPtr(zName)){
|
||||
}else if('number'===typeof zName){
|
||||
if(capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)){
|
||||
/* -----------------------^^^^^ MUST pass the untranslated
|
||||
C-string here. */
|
||||
opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP;
|
||||
}
|
||||
if(capi.sqlite3_uri_boolean(zName, "delete-before-open", 0)){
|
||||
opfsFlags |= state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN;
|
||||
}
|
||||
zName = wasm.cstrToJs(zName);
|
||||
//warn("xOpen zName =",zName, "opfsFlags =",opfsFlags);
|
||||
}
|
||||
const fh = Object.create(null);
|
||||
fh.fid = pFile;
|
||||
@ -1035,6 +993,27 @@ const installOpfsVfs = function callee(options){
|
||||
*/
|
||||
opfsUtil.randomFilename = randomFilename;
|
||||
|
||||
/**
|
||||
Re-registers the OPFS VFS. This is intended only for odd use
|
||||
cases which have to call sqlite3_shutdown() as part of their
|
||||
initialization process, which will unregister the VFS
|
||||
registered by installOpfsVfs(). If passed a truthy value, the
|
||||
OPFS VFS is registered as the default VFS, else it is not made
|
||||
the default. Returns the result of the the
|
||||
sqlite3_vfs_register() call.
|
||||
|
||||
Design note: the problem of having to re-register things after
|
||||
a shutdown/initialize pair is more general. How to best plug
|
||||
that in to the library is unclear. In particular, we cannot
|
||||
hook in to any C-side calls to sqlite3_initialize(), so we
|
||||
cannot add an after-initialize callback mechanism.
|
||||
*/
|
||||
opfsUtil.registerVfs = (asDefault=false)=>{
|
||||
return wasm.exports.sqlite3_vfs_register(
|
||||
opfsVfs.pointer, asDefault ? 1 : 0
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
Returns a promise which resolves to an object which represents
|
||||
all files and directories in the OPFS tree. The top-most object
|
||||
@ -1234,18 +1213,16 @@ const installOpfsVfs = function callee(options){
|
||||
Asynchronously imports the given bytes (a byte array or
|
||||
ArrayBuffer) into the given database file.
|
||||
|
||||
Results are undefined if the given db name refers to an opened
|
||||
db.
|
||||
|
||||
If passed a function for its second argument, its behaviour
|
||||
changes: imports its data in chunks fed to it by the given
|
||||
callback function. It calls the callback (which may be async)
|
||||
repeatedly, expecting either a Uint8Array or ArrayBuffer (to
|
||||
denote new input) or undefined (to denote EOF). For so long as
|
||||
the callback continues to return non-undefined, it will append
|
||||
incoming data to the given VFS-hosted database file. When
|
||||
called this way, the resolved value of the returned Promise is
|
||||
the number of bytes written to the target file.
|
||||
changes to async and it imports its data in chunks fed to it by
|
||||
the given callback function. It calls the callback (which may
|
||||
be async) repeatedly, expecting either a Uint8Array or
|
||||
ArrayBuffer (to denote new input) or undefined (to denote
|
||||
EOF). For so long as the callback continues to return
|
||||
non-undefined, it will append incoming data to the given
|
||||
VFS-hosted database file. When called this way, the resolved
|
||||
value of the returned Promise is the number of bytes written to
|
||||
the target file.
|
||||
|
||||
It very specifically requires the input to be an SQLite3
|
||||
database and throws if that's not the case. It does so in
|
||||
|
||||
@ -238,28 +238,28 @@
|
||||
** Another option is to malloc() a chunk of our own and call that our
|
||||
** "stack".
|
||||
*/
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_end(void){
|
||||
SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_end(void){
|
||||
extern void __heap_base
|
||||
/* see https://stackoverflow.com/questions/10038964 */;
|
||||
return &__heap_base;
|
||||
}
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_begin(void){
|
||||
SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_begin(void){
|
||||
extern void __data_end;
|
||||
return &__data_end;
|
||||
}
|
||||
static void * pWasmStackPtr = 0;
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_ptr(void){
|
||||
if(!pWasmStackPtr) pWasmStackPtr = sqlite3__wasm_stack_end();
|
||||
SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_ptr(void){
|
||||
if(!pWasmStackPtr) pWasmStackPtr = sqlite3_wasm_stack_end();
|
||||
return pWasmStackPtr;
|
||||
}
|
||||
SQLITE_WASM_EXPORT void sqlite3__wasm_stack_restore(void * p){
|
||||
SQLITE_WASM_EXPORT void sqlite3_wasm_stack_restore(void * p){
|
||||
pWasmStackPtr = p;
|
||||
}
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_alloc(int n){
|
||||
SQLITE_WASM_EXPORT void * sqlite3_wasm_stack_alloc(int n){
|
||||
if(n<=0) return 0;
|
||||
n = (n + 7) & ~7 /* align to 8-byte boundary */;
|
||||
unsigned char * const p = (unsigned char *)sqlite3__wasm_stack_ptr();
|
||||
unsigned const char * const b = (unsigned const char *)sqlite3__wasm_stack_begin();
|
||||
unsigned char * const p = (unsigned char *)sqlite3_wasm_stack_ptr();
|
||||
unsigned const char * const b = (unsigned const char *)sqlite3_wasm_stack_begin();
|
||||
if(b + n >= p || b + n < b/*overflow*/) return 0;
|
||||
return pWasmStackPtr = p - n;
|
||||
}
|
||||
@ -267,7 +267,7 @@ SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_alloc(int n){
|
||||
|
||||
/*
|
||||
** State for the "pseudo-stack" allocator implemented in
|
||||
** sqlite3__wasm_pstack_xyz(). In order to avoid colliding with
|
||||
** sqlite3_wasm_pstack_xyz(). In order to avoid colliding with
|
||||
** Emscripten-controled stack space, it carves out a bit of stack
|
||||
** memory to use for that purpose. This memory ends up in the
|
||||
** WASM-managed memory, such that routines which manipulate the wasm
|
||||
@ -291,14 +291,14 @@ static struct {
|
||||
/*
|
||||
** Returns the current pstack position.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_ptr(void){
|
||||
SQLITE_WASM_EXPORT void * sqlite3_wasm_pstack_ptr(void){
|
||||
return PStack.pPos;
|
||||
}
|
||||
/*
|
||||
** Sets the pstack position poitner to p. Results are undefined if the
|
||||
** given value did not come from sqlite3__wasm_pstack_ptr().
|
||||
** given value did not come from sqlite3_wasm_pstack_ptr().
|
||||
*/
|
||||
SQLITE_WASM_EXPORT void sqlite3__wasm_pstack_restore(unsigned char * p){
|
||||
SQLITE_WASM_EXPORT void sqlite3_wasm_pstack_restore(unsigned char * p){
|
||||
assert(p>=PStack.pBegin && p<=PStack.pEnd && p>=PStack.pPos);
|
||||
assert(0==((unsigned long long)p & 0x7));
|
||||
if(p>=PStack.pBegin && p<=PStack.pEnd /*&& p>=PStack.pPos*/){
|
||||
@ -313,7 +313,7 @@ SQLITE_WASM_EXPORT void sqlite3__wasm_pstack_restore(unsigned char * p){
|
||||
** JS code from having to do so, and most uses of the pstack will
|
||||
** call for doing so).
|
||||
*/
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_alloc(int n){
|
||||
SQLITE_WASM_EXPORT void * sqlite3_wasm_pstack_alloc(int n){
|
||||
if( n<=0 ) return 0;
|
||||
//if( n & 0x7 ) n += 8 - (n & 0x7) /* align to 8-byte boundary */;
|
||||
n = (n + 7) & ~7 /* align to 8-byte boundary */;
|
||||
@ -324,9 +324,9 @@ SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_alloc(int n){
|
||||
}
|
||||
/*
|
||||
** Return the number of bytes left which can be
|
||||
** sqlite3__wasm_pstack_alloc()'d.
|
||||
** sqlite3_wasm_pstack_alloc()'d.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_remaining(void){
|
||||
SQLITE_WASM_EXPORT int sqlite3_wasm_pstack_remaining(void){
|
||||
assert(PStack.pPos >= PStack.pBegin);
|
||||
assert(PStack.pPos <= PStack.pEnd);
|
||||
return (int)(PStack.pPos - PStack.pBegin);
|
||||
@ -337,7 +337,7 @@ SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_remaining(void){
|
||||
** any space which is currently allocated. This value is a
|
||||
** compile-time constant.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_quota(void){
|
||||
SQLITE_WASM_EXPORT int sqlite3_wasm_pstack_quota(void){
|
||||
return (int)(PStack.pEnd - PStack.pBegin);
|
||||
}
|
||||
|
||||
@ -356,7 +356,7 @@ SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_quota(void){
|
||||
** Returns err_code.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_error(sqlite3*db, int err_code, const char *zMsg){
|
||||
int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){
|
||||
if( db!=0 ){
|
||||
if( 0!=zMsg ){
|
||||
const int nMsg = sqlite3Strlen30(zMsg);
|
||||
@ -380,7 +380,7 @@ struct WasmTestStruct {
|
||||
};
|
||||
typedef struct WasmTestStruct WasmTestStruct;
|
||||
SQLITE_WASM_EXPORT
|
||||
void sqlite3__wasm_test_struct(WasmTestStruct * s){
|
||||
void sqlite3_wasm_test_struct(WasmTestStruct * s){
|
||||
if(s){
|
||||
s->v4 *= 2;
|
||||
s->v8 = s->v4 * 2;
|
||||
@ -408,7 +408,7 @@ void sqlite3__wasm_test_struct(WasmTestStruct * s){
|
||||
** increased. In debug builds that will trigger an assert().
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
const char * sqlite3__wasm_enum_json(void){
|
||||
const char * sqlite3_wasm_enum_json(void){
|
||||
static char aBuffer[1024 * 20] = {0} /* where the JSON goes */;
|
||||
int n = 0, nChildren = 0, nStruct = 0
|
||||
/* output counters for figuring out where commas go */;
|
||||
@ -425,7 +425,7 @@ const char * sqlite3__wasm_enum_json(void){
|
||||
|
||||
/* Core output macros... */
|
||||
#define lenCheck assert(zPos < zEnd - 128 \
|
||||
&& "sqlite3__wasm_enum_json() buffer is too small."); \
|
||||
&& "sqlite3_wasm_enum_json() buffer is too small."); \
|
||||
if( zPos >= zEnd - 128 ) return 0
|
||||
#define outf(format,...) \
|
||||
zPos += snprintf(zPos, ((size_t)(zEnd - zPos)), format, __VA_ARGS__); \
|
||||
@ -545,10 +545,6 @@ const char * sqlite3__wasm_enum_json(void){
|
||||
DefInt(SQLITE_CONFIG_SMALL_MALLOC);
|
||||
DefInt(SQLITE_CONFIG_SORTERREF_SIZE);
|
||||
DefInt(SQLITE_CONFIG_MEMDB_MAXSIZE);
|
||||
/* maintenance note: we specifically do not include
|
||||
SQLITE_CONFIG_ROWID_IN_VIEW here, on the grounds that
|
||||
it's only for legacy support and no apps written with
|
||||
this API require that. */
|
||||
} _DefGroup;
|
||||
|
||||
DefGroup(dataTypes) {
|
||||
@ -1224,7 +1220,7 @@ const char * sqlite3__wasm_enum_json(void){
|
||||
** call is returned.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_vfs_unlink(sqlite3_vfs *pVfs, const char *zName){
|
||||
int sqlite3_wasm_vfs_unlink(sqlite3_vfs *pVfs, const char *zName){
|
||||
int rc = SQLITE_MISUSE /* ??? */;
|
||||
if( 0==pVfs && 0!=zName ) pVfs = sqlite3_vfs_find(0);
|
||||
if( zName && pVfs && pVfs->xDelete ){
|
||||
@ -1242,7 +1238,7 @@ int sqlite3__wasm_vfs_unlink(sqlite3_vfs *pVfs, const char *zName){
|
||||
** given name is open.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
sqlite3_vfs * sqlite3__wasm_db_vfs(sqlite3 *pDb, const char *zDbName){
|
||||
sqlite3_vfs * sqlite3_wasm_db_vfs(sqlite3 *pDb, const char *zDbName){
|
||||
sqlite3_vfs * pVfs = 0;
|
||||
sqlite3_file_control(pDb, zDbName ? zDbName : "main",
|
||||
SQLITE_FCNTL_VFS_POINTER, &pVfs);
|
||||
@ -1265,7 +1261,7 @@ sqlite3_vfs * sqlite3__wasm_db_vfs(sqlite3 *pDb, const char *zDbName){
|
||||
** SQLITE_MISUSE if pDb is NULL.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_reset(sqlite3 *pDb){
|
||||
int sqlite3_wasm_db_reset(sqlite3 *pDb){
|
||||
int rc = SQLITE_MISUSE;
|
||||
if( pDb ){
|
||||
sqlite3_table_column_metadata(pDb, "main", 0, 0, 0, 0, 0, 0, 0);
|
||||
@ -1292,11 +1288,11 @@ int sqlite3__wasm_db_reset(sqlite3 *pDb){
|
||||
** takes no measures to ensure that is the case.
|
||||
**
|
||||
** This implementation appears to work fine, but
|
||||
** sqlite3__wasm_db_serialize() is arguably the better way to achieve
|
||||
** sqlite3_wasm_db_serialize() is arguably the better way to achieve
|
||||
** this.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_export_chunked( sqlite3* pDb,
|
||||
int sqlite3_wasm_db_export_chunked( sqlite3* pDb,
|
||||
int (*xCallback)(unsigned const char *zOut, int n) ){
|
||||
sqlite3_int64 nSize = 0;
|
||||
sqlite3_int64 nPos = 0;
|
||||
@ -1347,7 +1343,7 @@ int sqlite3__wasm_db_export_chunked( sqlite3* pDb,
|
||||
** sqlite3_free() to free it.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
int sqlite3_wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
unsigned char **pOut,
|
||||
sqlite3_int64 *nOut, unsigned int mFlags ){
|
||||
unsigned char * z;
|
||||
@ -1370,7 +1366,7 @@ int sqlite3__wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
** this function's out-of-scope use of the sqlite3_vfs/file/io_methods
|
||||
** APIs leads to triggering of assertions in the core library. Its use
|
||||
** is now deprecated and VFS-specific APIs for importing files need to
|
||||
** be found to replace it. sqlite3__wasm_posix_create_file() is
|
||||
** be found to replace it. sqlite3_wasm_posix_create_file() is
|
||||
** suitable for the "unix" family of VFSes.
|
||||
**
|
||||
** Creates a new file using the I/O API of the given VFS, containing
|
||||
@ -1411,7 +1407,7 @@ int sqlite3__wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
** support is disabled or unavailable.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
const char *zFilename,
|
||||
const unsigned char * pData,
|
||||
int nData ){
|
||||
@ -1501,7 +1497,7 @@ int sqlite3__wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
** SQLITE_IOERR on error.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_posix_create_file( const char *zFilename,
|
||||
int sqlite3_wasm_posix_create_file( const char *zFilename,
|
||||
const unsigned char * pData,
|
||||
int nData ){
|
||||
int rc;
|
||||
@ -1524,17 +1520,17 @@ int sqlite3__wasm_posix_create_file( const char *zFilename,
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** Allocates sqlite3KvvfsMethods.nKeySize bytes from
|
||||
** sqlite3__wasm_pstack_alloc() and returns 0 if that allocation fails,
|
||||
** sqlite3_wasm_pstack_alloc() and returns 0 if that allocation fails,
|
||||
** else it passes that string to kvstorageMakeKey() and returns a
|
||||
** NUL-terminated pointer to that string. It is up to the caller to
|
||||
** use sqlite3__wasm_pstack_restore() to free the returned pointer.
|
||||
** use sqlite3_wasm_pstack_restore() to free the returned pointer.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
char * sqlite3__wasm_kvvfsMakeKeyOnPstack(const char *zClass,
|
||||
char * sqlite3_wasm_kvvfsMakeKeyOnPstack(const char *zClass,
|
||||
const char *zKeyIn){
|
||||
assert(sqlite3KvvfsMethods.nKeySize>24);
|
||||
char *zKeyOut =
|
||||
(char *)sqlite3__wasm_pstack_alloc(sqlite3KvvfsMethods.nKeySize);
|
||||
(char *)sqlite3_wasm_pstack_alloc(sqlite3KvvfsMethods.nKeySize);
|
||||
if(zKeyOut){
|
||||
kvstorageMakeKey(zClass, zKeyIn, zKeyOut);
|
||||
}
|
||||
@ -1549,7 +1545,7 @@ char * sqlite3__wasm_kvvfsMakeKeyOnPstack(const char *zClass,
|
||||
** I/O methods and associated state.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
sqlite3_kvvfs_methods * sqlite3__wasm_kvvfs_methods(void){
|
||||
sqlite3_kvvfs_methods * sqlite3_wasm_kvvfs_methods(void){
|
||||
return &sqlite3KvvfsMethods;
|
||||
}
|
||||
|
||||
@ -1564,7 +1560,7 @@ sqlite3_kvvfs_methods * sqlite3__wasm_kvvfs_methods(void){
|
||||
** valid value.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_vtab_config(sqlite3 *pDb, int op, int arg){
|
||||
int sqlite3_wasm_vtab_config(sqlite3 *pDb, int op, int arg){
|
||||
switch(op){
|
||||
case SQLITE_VTAB_DIRECTONLY:
|
||||
case SQLITE_VTAB_INNOCUOUS:
|
||||
@ -1584,7 +1580,7 @@ int sqlite3__wasm_vtab_config(sqlite3 *pDb, int op, int arg){
|
||||
** (int,int*) variadic args.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){
|
||||
int sqlite3_wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){
|
||||
switch(op){
|
||||
case SQLITE_DBCONFIG_ENABLE_FKEY:
|
||||
case SQLITE_DBCONFIG_ENABLE_TRIGGER:
|
||||
@ -1617,7 +1613,7 @@ int sqlite3__wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){
|
||||
** (void*,int,int) variadic args.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_config_pii(sqlite3 *pDb, int op, void * pArg1, int arg2, int arg3){
|
||||
int sqlite3_wasm_db_config_pii(sqlite3 *pDb, int op, void * pArg1, int arg2, int arg3){
|
||||
switch(op){
|
||||
case SQLITE_DBCONFIG_LOOKASIDE:
|
||||
return sqlite3_db_config(pDb, op, pArg1, arg2, arg3);
|
||||
@ -1633,7 +1629,7 @@ int sqlite3__wasm_db_config_pii(sqlite3 *pDb, int op, void * pArg1, int arg2, in
|
||||
** (const char *) variadic args.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_config_s(sqlite3 *pDb, int op, const char *zArg){
|
||||
int sqlite3_wasm_db_config_s(sqlite3 *pDb, int op, const char *zArg){
|
||||
switch(op){
|
||||
case SQLITE_DBCONFIG_MAINDBNAME:
|
||||
return sqlite3_db_config(pDb, op, zArg);
|
||||
@ -1650,7 +1646,7 @@ int sqlite3__wasm_db_config_s(sqlite3 *pDb, int op, const char *zArg){
|
||||
** a single integer argument.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_config_i(int op, int arg){
|
||||
int sqlite3_wasm_config_i(int op, int arg){
|
||||
return sqlite3_config(op, arg);
|
||||
}
|
||||
|
||||
@ -1662,7 +1658,7 @@ int sqlite3__wasm_config_i(int op, int arg){
|
||||
** two int arguments.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_config_ii(int op, int arg1, int arg2){
|
||||
int sqlite3_wasm_config_ii(int op, int arg1, int arg2){
|
||||
return sqlite3_config(op, arg1, arg2);
|
||||
}
|
||||
|
||||
@ -1674,28 +1670,39 @@ int sqlite3__wasm_config_ii(int op, int arg1, int arg2){
|
||||
** a single i64 argument.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_config_j(int op, sqlite3_int64 arg){
|
||||
int sqlite3_wasm_config_j(int op, sqlite3_int64 arg){
|
||||
return sqlite3_config(op, arg);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Pending removal after verification of a workaround discussed in the
|
||||
// forum post linked to below.
|
||||
/*
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** If z is not NULL, returns the result of passing z to
|
||||
** sqlite3_mprintf()'s %Q modifier (if addQuotes is true) or %q (if
|
||||
** addQuotes is 0). Returns NULL if z is NULL or on OOM.
|
||||
*/
|
||||
** Returns a pointer to sqlite3_free(). In compliant browsers the
|
||||
** return value, when passed to sqlite3.wasm.exports.functionEntry(),
|
||||
** must resolve to the same function as
|
||||
** sqlite3.wasm.exports.sqlite3_free. i.e. from a dev console where
|
||||
** sqlite3 is exported globally, the following must be true:
|
||||
**
|
||||
** ```
|
||||
** sqlite3.wasm.functionEntry(
|
||||
** sqlite3.wasm.exports.sqlite3_wasm_ptr_to_sqlite3_free()
|
||||
** ) === sqlite3.wasm.exports.sqlite3_free
|
||||
** ```
|
||||
**
|
||||
** Using a function to return this pointer, as opposed to exporting it
|
||||
** via sqlite3_wasm_enum_json(), is an attempt to work around a
|
||||
** Safari-specific quirk covered at
|
||||
** https://sqlite.org/forum/info/e5b20e1feb37a19a.
|
||||
**/
|
||||
SQLITE_WASM_EXPORT
|
||||
char * sqlite3__wasm_qfmt_token(char *z, int addQuotes){
|
||||
char * rc = 0;
|
||||
if( z ){
|
||||
rc = addQuotes
|
||||
? sqlite3_mprintf("%Q", z)
|
||||
: sqlite3_mprintf("%q", z);
|
||||
}
|
||||
return rc;
|
||||
void * sqlite3_wasm_ptr_to_sqlite3_free(void){
|
||||
return (void*)sqlite3_free;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__) && defined(SQLITE_ENABLE_WASMFS)
|
||||
#include <emscripten/wasmfs.h>
|
||||
@ -1722,7 +1729,7 @@ char * sqlite3__wasm_qfmt_token(char *z, int addQuotes){
|
||||
** defined, SQLITE_NOTFOUND is returned without side effects.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_init_wasmfs(const char *zMountPoint){
|
||||
int sqlite3_wasm_init_wasmfs(const char *zMountPoint){
|
||||
static backend_t pOpfs = 0;
|
||||
if( !zMountPoint || !*zMountPoint ) zMountPoint = "/opfs";
|
||||
if( !pOpfs ){
|
||||
@ -1742,7 +1749,7 @@ int sqlite3__wasm_init_wasmfs(const char *zMountPoint){
|
||||
}
|
||||
#else
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_init_wasmfs(const char *zUnused){
|
||||
int sqlite3_wasm_init_wasmfs(const char *zUnused){
|
||||
//emscripten_console_warn("WASMFS OPFS is not compiled in.");
|
||||
if(zUnused){/*unused*/}
|
||||
return SQLITE_NOTFOUND;
|
||||
@ -1752,51 +1759,51 @@ int sqlite3__wasm_init_wasmfs(const char *zUnused){
|
||||
#if SQLITE_WASM_TESTS
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_test_intptr(int * p){
|
||||
int sqlite3_wasm_test_intptr(int * p){
|
||||
return *p = *p * 2;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
void * sqlite3__wasm_test_voidptr(void * p){
|
||||
void * sqlite3_wasm_test_voidptr(void * p){
|
||||
return p;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64_max(void){
|
||||
int64_t sqlite3_wasm_test_int64_max(void){
|
||||
return (int64_t)0x7fffffffffffffff;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64_min(void){
|
||||
return ~sqlite3__wasm_test_int64_max();
|
||||
int64_t sqlite3_wasm_test_int64_min(void){
|
||||
return ~sqlite3_wasm_test_int64_max();
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64_times2(int64_t x){
|
||||
int64_t sqlite3_wasm_test_int64_times2(int64_t x){
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
void sqlite3__wasm_test_int64_minmax(int64_t * min, int64_t *max){
|
||||
*max = sqlite3__wasm_test_int64_max();
|
||||
*min = sqlite3__wasm_test_int64_min();
|
||||
void sqlite3_wasm_test_int64_minmax(int64_t * min, int64_t *max){
|
||||
*max = sqlite3_wasm_test_int64_max();
|
||||
*min = sqlite3_wasm_test_int64_min();
|
||||
/*printf("minmax: min=%lld, max=%lld\n", *min, *max);*/
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64ptr(int64_t * p){
|
||||
/*printf("sqlite3__wasm_test_int64ptr( @%lld = 0x%llx )\n", (int64_t)p, *p);*/
|
||||
int64_t sqlite3_wasm_test_int64ptr(int64_t * p){
|
||||
/*printf("sqlite3_wasm_test_int64ptr( @%lld = 0x%llx )\n", (int64_t)p, *p);*/
|
||||
return *p = *p * 2;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
void sqlite3__wasm_test_stack_overflow(int recurse){
|
||||
if(recurse) sqlite3__wasm_test_stack_overflow(recurse);
|
||||
void sqlite3_wasm_test_stack_overflow(int recurse){
|
||||
if(recurse) sqlite3_wasm_test_stack_overflow(recurse);
|
||||
}
|
||||
|
||||
/* For testing the 'string:dealloc' whwasmutil.xWrap() conversion. */
|
||||
SQLITE_WASM_EXPORT
|
||||
char * sqlite3__wasm_test_str_hello(int fail){
|
||||
char * sqlite3_wasm_test_str_hello(int fail){
|
||||
char * s = fail ? 0 : (char *)sqlite3_malloc(6);
|
||||
if(s){
|
||||
memcpy(s, "hello", 5);
|
||||
@ -1831,12 +1838,12 @@ char * sqlite3__wasm_test_str_hello(int fail){
|
||||
** optional + or - sign in front, or a hexadecimal
|
||||
** literal of the form 0x...
|
||||
*/
|
||||
static int sqlite3__wasm_SQLTester_strnotglob(const char *zGlob, const char *z){
|
||||
static int sqlite3_wasm_SQLTester_strnotglob(const char *zGlob, const char *z){
|
||||
int c, c2;
|
||||
int invert;
|
||||
int seen;
|
||||
typedef int (*recurse_f)(const char *,const char *);
|
||||
static const recurse_f recurse = sqlite3__wasm_SQLTester_strnotglob;
|
||||
static const recurse_f recurse = sqlite3_wasm_SQLTester_strnotglob;
|
||||
|
||||
while( (c = (*(zGlob++)))!=0 ){
|
||||
if( c=='*' ){
|
||||
@ -1911,10 +1918,11 @@ static int sqlite3__wasm_SQLTester_strnotglob(const char *zGlob, const char *z){
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_SQLTester_strglob(const char *zGlob, const char *z){
|
||||
return !sqlite3__wasm_SQLTester_strnotglob(zGlob, z);
|
||||
int sqlite3_wasm_SQLTester_strglob(const char *zGlob, const char *z){
|
||||
return !sqlite3_wasm_SQLTester_strnotglob(zGlob, z);
|
||||
}
|
||||
|
||||
|
||||
#endif /* SQLITE_WASM_TESTS */
|
||||
|
||||
#undef SQLITE_WASM_EXPORT
|
||||
|
||||
@ -42,13 +42,9 @@
|
||||
- `onready` (optional, but...): this callback is called with no
|
||||
arguments when the worker fires its initial
|
||||
'sqlite3-api'/'worker1-ready' message, which it does when
|
||||
sqlite3.initWorker1API() completes its initialization. This is the
|
||||
simplest way to tell the worker to kick off work at the earliest
|
||||
opportunity, and the only way to know when the worker module has
|
||||
completed loading. The irony of using a callback for this, instead
|
||||
of returning a promise from sqlite3Worker1Promiser() is not lost on
|
||||
the developers: see sqlite3Worker1Promiser.v2() which uses a
|
||||
Promise instead.
|
||||
sqlite3.initWorker1API() completes its initialization. This is
|
||||
the simplest way to tell the worker to kick off work at the
|
||||
earliest opportunity.
|
||||
|
||||
- `onunhandled` (optional): a callback which gets passed the
|
||||
message event object for any worker.onmessage() events which
|
||||
@ -160,7 +156,6 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
|
||||
if(!config.worker) config.worker = callee.defaultConfig.worker;
|
||||
if('function'===typeof config.worker) config.worker = config.worker();
|
||||
let dbId;
|
||||
let promiserFunc;
|
||||
config.worker.onmessage = function(ev){
|
||||
ev = ev.data;
|
||||
debug('worker1.onmessage',ev);
|
||||
@ -168,7 +163,7 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
|
||||
if(!msgHandler){
|
||||
if(ev && 'sqlite3-api'===ev.type && 'worker1-ready'===ev.result) {
|
||||
/*fired one time when the Worker1 API initializes*/
|
||||
if(config.onready) config.onready(promiserFunc);
|
||||
if(config.onready) config.onready();
|
||||
return;
|
||||
}
|
||||
msgHandler = handlerMap[ev.type] /* check for exec per-row callback */;
|
||||
@ -197,7 +192,7 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
|
||||
try {msgHandler.resolve(ev)}
|
||||
catch(e){msgHandler.reject(e)}
|
||||
}/*worker.onmessage()*/;
|
||||
return promiserFunc = function(/*(msgType, msgArgs) || (msgEnvelope)*/){
|
||||
return function(/*(msgType, msgArgs) || (msgEnvelope)*/){
|
||||
let msg;
|
||||
if(1===arguments.length){
|
||||
msg = arguments[0];
|
||||
@ -207,7 +202,7 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
|
||||
msg.args = arguments[1];
|
||||
msg.dbId = msg.args.dbId;
|
||||
}else{
|
||||
toss("Invalid arguments for sqlite3Worker1Promiser()-created factory.");
|
||||
toss("Invalid arugments for sqlite3Worker1Promiser()-created factory.");
|
||||
}
|
||||
if(!msg.dbId && msg.type!=='open') msg.dbId = dbId;
|
||||
msg.messageId = genMsgId(msg);
|
||||
@ -251,10 +246,9 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi
|
||||
return p;
|
||||
};
|
||||
}/*sqlite3Worker1Promiser()*/;
|
||||
|
||||
globalThis.sqlite3Worker1Promiser.defaultConfig = {
|
||||
worker: function(){
|
||||
//#if target=es6-module
|
||||
//#if target=es6-bundler-friendly
|
||||
return new Worker(new URL("sqlite3-worker1-bundler-friendly.mjs", import.meta.url),{
|
||||
type: 'module'
|
||||
});
|
||||
@ -275,72 +269,14 @@ globalThis.sqlite3Worker1Promiser.defaultConfig = {
|
||||
return new Worker(theJs + globalThis.location.search);
|
||||
//#endif
|
||||
}
|
||||
//#ifnot target=es6-module
|
||||
//#ifnot target=es6-bundler-friendly
|
||||
.bind({
|
||||
currentScript: globalThis?.document?.currentScript
|
||||
})
|
||||
//#endif
|
||||
,
|
||||
onerror: (...args)=>console.error('worker1 promiser error',...args)
|
||||
}/*defaultConfig*/;
|
||||
|
||||
/**
|
||||
sqlite3Worker1Promiser.v2(), added in 3.46, works identically to
|
||||
sqlite3Worker1Promiser() except that it returns a Promise instead
|
||||
of relying an an onready callback in the config object. The Promise
|
||||
resolves to the same factory function which
|
||||
sqlite3Worker1Promiser() returns.
|
||||
|
||||
If config is-a function or is an object which contains an onready
|
||||
function, that function is replaced by a proxy which will resolve
|
||||
after calling the original function and will reject if that
|
||||
function throws.
|
||||
*/
|
||||
sqlite3Worker1Promiser.v2 = function(config){
|
||||
let oldFunc;
|
||||
if( 'function' == typeof config ){
|
||||
oldFunc = config;
|
||||
config = {};
|
||||
}else if('function'===typeof config?.onready){
|
||||
oldFunc = config.onready;
|
||||
delete config.onready;
|
||||
}
|
||||
const promiseProxy = Object.create(null);
|
||||
config = Object.assign((config || Object.create(null)),{
|
||||
onready: async function(func){
|
||||
try {
|
||||
if( oldFunc ) await oldFunc(func);
|
||||
promiseProxy.resolve(func);
|
||||
}
|
||||
catch(e){promiseProxy.reject(e)}
|
||||
}
|
||||
});
|
||||
const p = new Promise(function(resolve,reject){
|
||||
promiseProxy.resolve = resolve;
|
||||
promiseProxy.reject = reject;
|
||||
});
|
||||
try{
|
||||
this.original(config);
|
||||
}catch(e){
|
||||
promiseProxy.reject(e);
|
||||
}
|
||||
return p;
|
||||
}.bind({
|
||||
/* We do this because clients are
|
||||
recommended to delete globalThis.sqlite3Worker1Promiser. */
|
||||
original: sqlite3Worker1Promiser
|
||||
});
|
||||
|
||||
//#if target=es6-module
|
||||
/**
|
||||
When built as a module, we export sqlite3Worker1Promiser.v2()
|
||||
instead of sqlite3Worker1Promise() because (A) its interface is more
|
||||
conventional for ESM usage and (B) the ESM option export option for
|
||||
this API did not exist until v2 was created, so there's no backwards
|
||||
incompatibility.
|
||||
*/
|
||||
export default sqlite3Worker1Promiser.v2;
|
||||
//#endif /* target=es6-module */
|
||||
};
|
||||
//#else
|
||||
/* Built with the omit-oo1 flag. */
|
||||
//#endif ifnot omit-oo1
|
||||
|
||||
@ -1382,19 +1382,15 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
conversion of argument or return types, but see xWrap() and
|
||||
xCallWrapped() for variants which do.
|
||||
|
||||
If the first argument is a function is is assumed to be
|
||||
a WASM-bound function and is used as-is instead of looking up
|
||||
the function via xGet().
|
||||
|
||||
As a special case, if passed only 1 argument after the name and
|
||||
that argument in an Array, that array's entries become the
|
||||
function arguments. (This is not an ambiguous case because it's
|
||||
not legal to pass an Array object to a WASM function.)
|
||||
*/
|
||||
target.xCall = function(fname, ...args){
|
||||
const f = (fname instanceof Function) ? fname : target.xGet(fname);
|
||||
const f = target.xGet(fname);
|
||||
if(!(f instanceof Function)) toss("Exported symbol",fname,"is not a function.");
|
||||
if(f.length!==args.length) __argcMismatch(((f===fname) ? f.name : fname),f.length)
|
||||
if(f.length!==args.length) __argcMismatch(fname,f.length)
|
||||
/* This is arguably over-pedantic but we want to help clients keep
|
||||
from shooting themselves in the foot when calling C APIs. */;
|
||||
return (2===arguments.length && Array.isArray(arguments[1]))
|
||||
@ -1541,7 +1537,7 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
jsFuncToWasm().
|
||||
|
||||
- bindScope (string): one of ('transient', 'context',
|
||||
'singleton', 'permanent'). Bind scopes are:
|
||||
'singleton'). Bind scopes are:
|
||||
|
||||
- 'transient': it will convert JS functions to WASM only for
|
||||
the duration of the xWrap()'d function call, using
|
||||
@ -1791,29 +1787,11 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
const __xResultAdapterCheck =
|
||||
(t)=>xResult.get(t) || toss("Result adapter not found:",t);
|
||||
|
||||
/**
|
||||
Fetches the xWrap() argument adapter mapped to t, calls it,
|
||||
passing in all remaining arguments, and returns the result.
|
||||
Throws if t is not mapped to an argument converter.
|
||||
*/
|
||||
cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);
|
||||
/**
|
||||
Identical to convertArg() except that it does not perform
|
||||
an is-defined check on the mapping to t before invoking it.
|
||||
*/
|
||||
cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args);
|
||||
|
||||
/**
|
||||
Fetches the xWrap() result adapter mapped to t, calls it, passing
|
||||
it v, and returns the result. Throws if t is not mapped to an
|
||||
argument converter.
|
||||
*/
|
||||
cache.xWrap.convertResult =
|
||||
(t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
|
||||
/**
|
||||
Identical to convertResult() except that it does not perform an
|
||||
is-defined check on the mapping to t before invoking it.
|
||||
*/
|
||||
cache.xWrap.convertResultNoCheck =
|
||||
(t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined));
|
||||
|
||||
@ -1925,15 +1903,15 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
const C-string, encoded as UTF-8, copies it to a JS string,
|
||||
and returns that JS string.
|
||||
|
||||
- `string:dealloc` or `utf8:dealloc` (results): treats the result
|
||||
value as a non-const UTF-8 C-string, ownership of which has
|
||||
just been transfered to the caller. It copies the C-string to a
|
||||
JS string, frees the C-string, and returns the JS string. If
|
||||
such a result value is NULL, the JS result is `null`. Achtung:
|
||||
when using an API which returns results from a specific
|
||||
allocator, e.g. `my_malloc()`, this conversion _is not
|
||||
legal_. Instead, an equivalent conversion which uses the
|
||||
appropriate deallocator is required. For example:
|
||||
- `string:dealloc` or `utf8:dealloc) (results): treats the result value
|
||||
as a non-const UTF-8 C-string, ownership of which has just been
|
||||
transfered to the caller. It copies the C-string to a JS
|
||||
string, frees the C-string, and returns the JS string. If such
|
||||
a result value is NULL, the JS result is `null`. Achtung: when
|
||||
using an API which returns results from a specific allocator,
|
||||
e.g. `my_malloc()`, this conversion _is not legal_. Instead, an
|
||||
equivalent conversion which uses the appropriate deallocator is
|
||||
required. For example:
|
||||
|
||||
```js
|
||||
target.xWrap.resultAdapter('string:my_free',(i)=>{
|
||||
@ -2034,12 +2012,8 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
arguments may be passed in after that one, and what those
|
||||
arguments are, is _not_ part of the public interface and is
|
||||
_not_ stable.
|
||||
|
||||
Maintenance reminder: the Ember framework modifies the core
|
||||
Array type, breaking for-in loops.
|
||||
*/
|
||||
let i = 0;
|
||||
for(; i < args.length; ++i) args[i] = cxw.convertArgNoCheck(
|
||||
for(const i in args) args[i] = cxw.convertArgNoCheck(
|
||||
argTypes[i], args[i], args, i
|
||||
);
|
||||
return cxw.convertResultNoCheck(resultType, xf.apply(null,args));
|
||||
|
||||
@ -6,11 +6,7 @@
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<link rel="stylesheet" href="common/emscripten.css"/>
|
||||
<link rel="stylesheet" href="common/testing.css"/>
|
||||
//#if target=es6-module
|
||||
<title>worker-promise (via ESM) tests</title>
|
||||
//#else
|
||||
<title>worker-promise tests</title>
|
||||
//#endif
|
||||
</head>
|
||||
<body>
|
||||
<header id='titlebar'><span>worker-promise tests</span></header>
|
||||
@ -26,17 +22,13 @@
|
||||
</figure>
|
||||
<div class="emscripten" id="module-status">Downloading...</div>
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
|
||||
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
|
||||
</div><!-- /emscripten bits -->
|
||||
<div>Most stuff on this page happens in the dev console.</div>
|
||||
<hr>
|
||||
<div id='test-output'></div>
|
||||
<script src="common/SqliteTestUtil.js"></script>
|
||||
//#if target=es6-module
|
||||
<script src="demo-worker1-promiser.mjs" type="module"></script>
|
||||
//#else
|
||||
<script src="jswasm/sqlite3-worker1-promiser.js"></script>
|
||||
<script src="demo-worker1-promiser.js"></script>
|
||||
//#endif
|
||||
</body>
|
||||
</html>
|
||||
@ -13,15 +13,9 @@
|
||||
Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based
|
||||
proxy for for the sqlite3 Worker #1 API.
|
||||
*/
|
||||
//#if target=es6-module
|
||||
import {default as promiserFactory} from "./jswasm/sqlite3-worker1-promiser.mjs";
|
||||
//#else
|
||||
"use strict";
|
||||
const promiserFactory = globalThis.sqlite3Worker1Promiser.v2;
|
||||
delete globalThis.sqlite3Worker1Promiser;
|
||||
//#endif
|
||||
(async function(){
|
||||
const T = globalThis.SqliteTestUtil;
|
||||
'use strict';
|
||||
(function(){
|
||||
const T = self.SqliteTestUtil;
|
||||
const eOutput = document.querySelector('#test-output');
|
||||
const warn = console.warn.bind(console);
|
||||
const error = console.error.bind(console);
|
||||
@ -39,35 +33,31 @@ delete globalThis.sqlite3Worker1Promiser;
|
||||
logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms");
|
||||
};
|
||||
|
||||
//why is this triggered even when we catch() a Promise?
|
||||
//window.addEventListener('unhandledrejection', function(event) {
|
||||
// warn('unhandledrejection',event);
|
||||
//});
|
||||
|
||||
const promiserConfig = {
|
||||
//#ifnot target=es6-module
|
||||
/**
|
||||
The v1 interfaces uses an onready function. The v2 interface optionally
|
||||
accepts one but does not require it. If provided, it is called _before_
|
||||
the promise is resolved, and the promise is rejected if onready() throws.
|
||||
*/
|
||||
onready: function(f){
|
||||
/* f === the function returned by promiserFactory().
|
||||
Ostensibly (f === workerPromise) but this function is
|
||||
called before the promiserFactory() Promise resolves, so
|
||||
before workerPromise is set. */
|
||||
console.warn("This is the v2 interface - you don't need an onready() function.");
|
||||
worker: ()=>{
|
||||
const w = new Worker("jswasm/sqlite3-worker1.js");
|
||||
w.onerror = (event)=>error("worker.onerror",event);
|
||||
return w;
|
||||
},
|
||||
//#endif
|
||||
debug: 1 ? undefined : (...args)=>console.debug('worker debug',...args),
|
||||
onunhandled: function(ev){
|
||||
error("Unhandled worker message:",ev.data);
|
||||
},
|
||||
onready: function(){
|
||||
self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/;
|
||||
runTests();
|
||||
},
|
||||
onerror: function(ev){
|
||||
error("worker1 error:",ev);
|
||||
}
|
||||
};
|
||||
const workerPromise = await promiserFactory(promiserConfig)
|
||||
.then((func)=>{
|
||||
console.log("Init complete. Starting tests momentarily.");
|
||||
globalThis.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/;
|
||||
return func;
|
||||
});
|
||||
const workerPromise = self.sqlite3Worker1Promiser(promiserConfig);
|
||||
delete self.sqlite3Worker1Promiser;
|
||||
|
||||
const wtest = async function(msgType, msgArgs, callback){
|
||||
if(2===arguments.length && 'function'===typeof msgArgs){
|
||||
@ -281,5 +271,5 @@ delete globalThis.sqlite3Worker1Promiser;
|
||||
}).finally(()=>logHtml('',"That's all, folks!"));
|
||||
}/*runTests2()*/;
|
||||
|
||||
runTests();
|
||||
log("Init complete, but async init bits may still be running.");
|
||||
})();
|
||||
@ -19,15 +19,10 @@ MAKEFILE.dist := $(lastword $(MAKEFILE_LIST))
|
||||
# built, and won't be built until we expand the dependencies. Thus we
|
||||
# have to use a temporary name for the archive until we can get
|
||||
# that binary built.
|
||||
ifeq (1,$(SQLITE_C_IS_SEE))
|
||||
dist-name-extra := -see
|
||||
else
|
||||
dist-name-extra :=
|
||||
endif
|
||||
ifeq (,$(filter snapshot,$(MAKECMDGOALS)))
|
||||
dist-name-prefix := sqlite-wasm$(dist-name-extra)
|
||||
dist-name-prefix := sqlite-wasm
|
||||
else
|
||||
dist-name-prefix := sqlite-wasm$(dist-name-extra)-snapshot-$(shell /usr/bin/date +%Y%m%d)
|
||||
dist-name-prefix := sqlite-wasm-snapshot-$(shell /usr/bin/date +%Y%m%d)
|
||||
endif
|
||||
dist-name := $(dist-name-prefix)-TEMP
|
||||
|
||||
@ -54,18 +49,12 @@ dist.top.extras := \
|
||||
tester1.js tester1.mjs \
|
||||
demo-jsstorage.html demo-jsstorage.js \
|
||||
demo-worker1.html demo-worker1.js \
|
||||
demo-worker1-promiser.html demo-worker1-promiser.js \
|
||||
demo-worker1-promiser-esm.html demo-worker1-promiser.mjs
|
||||
dist.jswasm.extras := $(sqlite3.wasm) \
|
||||
$(sqlite3-api.ext.jses)
|
||||
demo-worker1-promiser.html demo-worker1-promiser.js
|
||||
dist.jswasm.extras := $(sqlite3-api.ext.jses) $(sqlite3.wasm)
|
||||
dist.common.extras := \
|
||||
$(wildcard $(dir.common)/*.css) \
|
||||
$(dir.common)/SqliteTestUtil.js
|
||||
|
||||
#$(info sqlite3-worker1-promiser.mjs = $(sqlite3-worker1-promiser.mjs))
|
||||
#$(info sqlite3-worker1.js = $(sqlite3-worker1.js))
|
||||
#$(info sqlite3-api.ext.jses = $(sqlite3-api.ext.jses))
|
||||
#$(info dist.jswasm.extras = $(dist.jswasm.extras))
|
||||
.PHONY: dist snapshot
|
||||
# DIST_STRIP_COMMENTS $(call)able to be used in stripping C-style
|
||||
# from the dist copies of certain files.
|
||||
@ -78,8 +67,7 @@ endef
|
||||
# STRIP_K1.js = list of JS files which need to be passed through
|
||||
# $(bin.stripcomments) with a single -k flag.
|
||||
STRIP_K1.js := $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js) \
|
||||
$(sqlite3-worker1-bundler-friendly.js) \
|
||||
$(sqlite3-api.ext.jses)
|
||||
$(sqlite3-worker1-bundler-friendly.js) $(sqlite3-worker1-promiser-bundler-friendly.js)
|
||||
# STRIP_K2.js = list of JS files which need to be passed through
|
||||
# $(bin.stripcomments) with two -k flags.
|
||||
STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \
|
||||
@ -100,7 +88,6 @@ STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \
|
||||
dist: \
|
||||
$(bin.stripccomments) $(bin.version-info) \
|
||||
$(dist.build) $(STRIP_K1.js) $(STRIP_K2.js) \
|
||||
$(dist.jswasm.extras) $(dist.common.extras) \
|
||||
$(MAKEFILE) $(MAKEFILE.dist)
|
||||
@echo "Making end-user deliverables..."
|
||||
@rm -fr $(dist-dir.top)
|
||||
|
||||
@ -9,18 +9,16 @@ MAKEFILE.fiddle := $(lastword $(MAKEFILE_LIST))
|
||||
# shell.c and its build flags...
|
||||
make-np-0 := make -C $(dir.top) -n -p
|
||||
make-np-1 := sed -e 's/(TOP)/(dir.top)/g'
|
||||
# Extract SHELL_OPT and SHELL_DEP from the top-most makefile and import
|
||||
# them as vars here...
|
||||
$(eval $(shell $(make-np-0) | grep -e '^SHELL_OPT ' | $(make-np-1)))
|
||||
$(eval $(shell $(make-np-0) | grep -e '^SHELL_DEP ' | $(make-np-1)))
|
||||
$(eval $(shell $(make-np-0) | grep -e '^SHELL_SRC ' | $(make-np-1)))
|
||||
# ^^^ can't do that in 1 invocation b/c newlines get stripped
|
||||
ifeq (,$(SHELL_OPT))
|
||||
$(error Could not parse SHELL_OPT from $(dir.top)/Makefile.)
|
||||
endif
|
||||
ifeq (,$(SHELL_DEP))
|
||||
$(error Could not parse SHELL_DEP from $(dir.top)/Makefile.)
|
||||
ifeq (,$(SHELL_SRC))
|
||||
$(error Could not parse SHELL_SRC from $(dir.top)/Makefile.)
|
||||
endif
|
||||
$(dir.top)/shell.c: $(SHELL_DEP) $(dir.top)/tool/mkshellc.tcl $(sqlite3.c)
|
||||
$(dir.top)/shell.c: $(SHELL_SRC) $(dir.top)/tool/mkshellc.tcl $(sqlite3.c)
|
||||
$(MAKE) -C $(dir.top) shell.c
|
||||
# /shell.c
|
||||
########################################################################
|
||||
|
||||
@ -166,10 +166,11 @@
|
||||
stdout("SQLite version", capi.sqlite3_libversion(),
|
||||
capi.sqlite3_sourceid().substr(0,19));
|
||||
stdout('Welcome to the "fiddle" shell.');
|
||||
if(capi.sqlite3_vfs_find("opfs")){
|
||||
if(sqlite3.opfs){
|
||||
stdout("\nOPFS is available. To open a persistent db, use:\n\n",
|
||||
" .open file:name?vfs=opfs\n\nbut note that some",
|
||||
"features (e.g. upload) do not yet work with OPFS.");
|
||||
sqlite3.opfs.registerVfs();
|
||||
}
|
||||
stdout('\nEnter ".help" for usage hints.');
|
||||
this.exec([ // initialization commands...
|
||||
@ -316,7 +317,7 @@
|
||||
};
|
||||
console.warn("Unknown fiddle-worker message type:",ev);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
emscripten module for use with build mode -sMODULARIZE.
|
||||
*/
|
||||
@ -373,7 +374,9 @@
|
||||
"for use in the dev console.", sqlite3);
|
||||
globalThis.sqlite3 = sqlite3;
|
||||
const dbVfs = sqlite3.wasm.xWrap('fiddle_db_vfs', "*", ['string']);
|
||||
fiddleModule.fsUnlink = (fn)=>fiddleModule.FS.unlink(fn);
|
||||
fiddleModule.fsUnlink = (fn)=>{
|
||||
return sqlite3.wasm.sqlite3_wasm_vfs_unlink(dbVfs(0), fn);
|
||||
};
|
||||
wMsg('fiddle-ready');
|
||||
}).catch(e=>{
|
||||
console.error("Fiddle worker init failed:",e);
|
||||
|
||||
@ -403,10 +403,8 @@
|
||||
E('#btn-reset').addEventListener('click',()=>SF.resetDb());
|
||||
const taInput = E('#input');
|
||||
const btnClearIn = E('#btn-clear');
|
||||
const selectExamples = E('#select-examples');
|
||||
btnClearIn.addEventListener('click',function(){
|
||||
taInput.value = '';
|
||||
selectExamples.selectedIndex = 0;
|
||||
},false);
|
||||
// Ctrl-enter and shift-enter both run the current SQL.
|
||||
taInput.addEventListener('keydown',function(ev){
|
||||
@ -735,15 +733,16 @@
|
||||
]},
|
||||
//{name: "Timer on", sql: ".timer on"},
|
||||
// ^^^ re-enable if emscripten re-enables getrusage()
|
||||
{name: "Box Mode", sql: ".mode box"},
|
||||
{name: "Setup table T", sql:[
|
||||
".nullvalue NULL\n",
|
||||
"CREATE TABLE t(a,b);\n",
|
||||
"INSERT INTO t(a,b) VALUES('abc',123),('def',456),(NULL,789),('ghi',012);\n",
|
||||
"SELECT * FROM t;\n"
|
||||
]},
|
||||
{name: "sqlite_schema", sql: "select * from sqlite_schema"},
|
||||
{name: "Mandelbrot", sql:[
|
||||
{name: "Table list", sql: ".tables"},
|
||||
{name: "Box Mode", sql: ".mode box"},
|
||||
{name: "JSON Mode", sql: ".mode json"},
|
||||
{name: "Mandlebrot", sql:[
|
||||
"WITH RECURSIVE",
|
||||
" xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+0.05 FROM xaxis WHERE x<1.2),\n",
|
||||
" yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+0.1 FROM yaxis WHERE y<1.0),\n",
|
||||
@ -761,13 +760,7 @@
|
||||
" FROM m2 GROUP BY cy\n",
|
||||
" )\n",
|
||||
"SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;\n",
|
||||
]},
|
||||
{name: "JSON pretty-print",
|
||||
sql: "select json_pretty(json_object('ex',json('[52,3.14159]')))"
|
||||
},
|
||||
{name: "JSON pretty-print (with tabs)",
|
||||
sql: "select json_pretty(json_object('ex',json('[52,3.14159]')),char(0x09))"
|
||||
}
|
||||
]}
|
||||
];
|
||||
const newOpt = function(lbl,val){
|
||||
const o = document.createElement('option');
|
||||
|
||||
@ -97,8 +97,6 @@
|
||||
wrapper is significantly easier to use, however.</li>
|
||||
<li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>:
|
||||
a demo of the Promise-based wrapper of the Worker1 API.</li>
|
||||
<li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
|
||||
same as the previous demo except loads the promiser from an ESM module.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -84,8 +84,6 @@
|
||||
wrapper is significantly easier to use, however.</li>
|
||||
<li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>:
|
||||
a demo of the Promise-based wrapper of the Worker1 API.</li>
|
||||
<li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
|
||||
same as the previous demo except loads the promiser from an ESM module.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>speedtest1 ports (sqlite3's primary benchmarking tool)...
|
||||
|
||||
@ -111,6 +111,10 @@
|
||||
self.sqlite3InitModule(EmscriptenModule).then(async (sqlite3)=>{
|
||||
const S = globalThis.S = App.sqlite3 = sqlite3;
|
||||
log("Loaded speedtest1 module. Setting up...");
|
||||
App.vfsUnlink = function(pDb, fname){
|
||||
const pVfs = S.wasm.sqlite3_wasm_db_vfs(pDb, 0);
|
||||
if(pVfs) S.wasm.sqlite3_wasm_vfs_unlink(pVfs, fname||0);
|
||||
};
|
||||
App.pDir = wasmfsDir(S.wasm);
|
||||
App.wasm = S.wasm;
|
||||
//if(App.pDir) log("Persistent storage:",pDir);
|
||||
|
||||
@ -63,7 +63,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
/* Predicate for tests/groups. */
|
||||
const testIsTodo = ()=>false;
|
||||
const haveWasmCTests = ()=>{
|
||||
return !!wasm.exports.sqlite3__wasm_test_intptr;
|
||||
return !!wasm.exports.sqlite3_wasm_test_intptr;
|
||||
};
|
||||
const hasOpfs = ()=>{
|
||||
return globalThis.FileSystemHandle
|
||||
@ -722,7 +722,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
|
||||
//log("xCall()...");
|
||||
{
|
||||
const pJson = w.xCall('sqlite3__wasm_enum_json');
|
||||
const pJson = w.xCall('sqlite3_wasm_enum_json');
|
||||
T.assert(Number.isFinite(pJson)).assert(w.cstrlen(pJson)>300);
|
||||
}
|
||||
|
||||
@ -736,9 +736,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
T.mustThrowMatching(()=>fw(1), /requires 0 arg/);
|
||||
let rc = fw();
|
||||
T.assert('string'===typeof rc).assert(rc.length>5);
|
||||
rc = w.xCallWrapped('sqlite3__wasm_enum_json','*');
|
||||
rc = w.xCallWrapped('sqlite3_wasm_enum_json','*');
|
||||
T.assert(rc>0 && Number.isFinite(rc));
|
||||
rc = w.xCallWrapped('sqlite3__wasm_enum_json','utf8');
|
||||
rc = w.xCallWrapped('sqlite3_wasm_enum_json','utf8');
|
||||
T.assert('string'===typeof rc).assert(rc.length>300);
|
||||
|
||||
|
||||
@ -821,7 +821,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
|
||||
if(haveWasmCTests()){
|
||||
if(!sqlite3.config.useStdAlloc){
|
||||
fw = w.xWrap('sqlite3__wasm_test_str_hello', 'utf8:dealloc',['i32']);
|
||||
fw = w.xWrap('sqlite3_wasm_test_str_hello', 'utf8:dealloc',['i32']);
|
||||
rc = fw(0);
|
||||
T.assert('hello'===rc);
|
||||
rc = fw(1);
|
||||
@ -831,14 +831,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
if(w.bigIntEnabled){
|
||||
w.xWrap.resultAdapter('thrice', (v)=>3n*BigInt(v));
|
||||
w.xWrap.argAdapter('twice', (v)=>2n*BigInt(v));
|
||||
fw = w.xWrap('sqlite3__wasm_test_int64_times2','thrice','twice');
|
||||
fw = w.xWrap('sqlite3_wasm_test_int64_times2','thrice','twice');
|
||||
rc = fw(1);
|
||||
T.assert(12n===rc);
|
||||
|
||||
w.scopedAllocCall(function(){
|
||||
const pI1 = w.scopedAlloc(8), pI2 = pI1+4;
|
||||
w.pokePtr([pI1, pI2], 0);
|
||||
const f = w.xWrap('sqlite3__wasm_test_int64_minmax',undefined,['i64*','i64*']);
|
||||
const f = w.xWrap('sqlite3_wasm_test_int64_minmax',undefined,['i64*','i64*']);
|
||||
const [r1, r2] = w.peek64([pI1, pI2]);
|
||||
T.assert(!Number.isSafeInteger(r1)).assert(!Number.isSafeInteger(r2));
|
||||
});
|
||||
@ -942,7 +942,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
assert(wts.pointer>0).assert(0===wts.$v4).assert(0n===wts.$v8).
|
||||
assert(0===wts.$ppV).assert(0===wts.$xFunc);
|
||||
const testFunc =
|
||||
W.xGet('sqlite3__wasm_test_struct'/*name gets mangled in -O3 builds!*/);
|
||||
W.xGet('sqlite3_wasm_test_struct'/*name gets mangled in -O3 builds!*/);
|
||||
let counter = 0;
|
||||
//log("wts.pointer =",wts.pointer);
|
||||
const wtsFunc = function(arg){
|
||||
@ -1128,7 +1128,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
T.g('sqlite3.oo1')
|
||||
.t('Create db', function(sqlite3){
|
||||
const dbFile = '/tester1.db';
|
||||
sqlite3.util.sqlite3__wasm_vfs_unlink(0, dbFile);
|
||||
wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
|
||||
const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c');
|
||||
db.onclose = {
|
||||
disposeAfter: [],
|
||||
@ -1459,7 +1459,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
rv = db.exec("SELECT 1 WHERE 0",{rowMode: 0});
|
||||
T.assert(Array.isArray(rv)).assert(0===rv.length);
|
||||
if(wasm.bigIntEnabled && haveWasmCTests()){
|
||||
const mI = wasm.xCall('sqlite3__wasm_test_int64_max');
|
||||
const mI = wasm.xCall('sqlite3_wasm_test_int64_max');
|
||||
const b = BigInt(Number.MAX_SAFE_INTEGER * 2);
|
||||
T.assert(b === db.selectValue("SELECT "+b)).
|
||||
assert(b === db.selectValue("SELECT ?", b)).
|
||||
@ -1482,7 +1482,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
/*step() skipped intentionally*/.reset(true);
|
||||
} finally {
|
||||
T.assert(0===st.finalize())
|
||||
.assert(undefined===st.finalize());
|
||||
.assert(undefined===st.finalize());
|
||||
}
|
||||
|
||||
try {
|
||||
@ -1685,7 +1685,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert(n>0 && db2.selectValue(sql) === n);
|
||||
}finally{
|
||||
db2.close();
|
||||
sqlite3.util.sqlite3__wasm_vfs_unlink(0, filename);
|
||||
wasm.sqlite3_wasm_vfs_unlink(0, filename);
|
||||
}
|
||||
}
|
||||
}/*sqlite3_js_posix_create_file()*/)
|
||||
@ -2075,7 +2075,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
try{
|
||||
ptrInt = w.scopedAlloc(4);
|
||||
w.poke32(ptrInt,origValue);
|
||||
const cf = w.xGet('sqlite3__wasm_test_intptr');
|
||||
const cf = w.xGet('sqlite3_wasm_test_intptr');
|
||||
const oldPtrInt = ptrInt;
|
||||
T.assert(origValue === w.peek32(ptrInt));
|
||||
const rc = cf(ptrInt);
|
||||
@ -2090,13 +2090,13 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
const v64 = ()=>w.peek64(pi64)
|
||||
T.assert(v64() == o64);
|
||||
//T.assert(o64 === w.peek64(pi64));
|
||||
const cf64w = w.xGet('sqlite3__wasm_test_int64ptr');
|
||||
const cf64w = w.xGet('sqlite3_wasm_test_int64ptr');
|
||||
cf64w(pi64);
|
||||
T.assert(v64() == BigInt(2 * o64));
|
||||
cf64w(pi64);
|
||||
T.assert(v64() == BigInt(4 * o64));
|
||||
|
||||
const biTimes2 = w.xGet('sqlite3__wasm_test_int64_times2');
|
||||
const biTimes2 = w.xGet('sqlite3_wasm_test_int64_times2');
|
||||
T.assert(BigInt(2 * o64) ===
|
||||
biTimes2(BigInt(o64)/*explicit conv. required to avoid TypeError
|
||||
in the call :/ */));
|
||||
@ -2106,13 +2106,13 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
const g64 = (p)=>w.peek64(p);
|
||||
w.poke64([pMin, pMax], 0);
|
||||
const minMaxI64 = [
|
||||
w.xCall('sqlite3__wasm_test_int64_min'),
|
||||
w.xCall('sqlite3__wasm_test_int64_max')
|
||||
w.xCall('sqlite3_wasm_test_int64_min'),
|
||||
w.xCall('sqlite3_wasm_test_int64_max')
|
||||
];
|
||||
T.assert(minMaxI64[0] < BigInt(Number.MIN_SAFE_INTEGER)).
|
||||
assert(minMaxI64[1] > BigInt(Number.MAX_SAFE_INTEGER));
|
||||
//log("int64_min/max() =",minMaxI64, typeof minMaxI64[0]);
|
||||
w.xCall('sqlite3__wasm_test_int64_minmax', pMin, pMax);
|
||||
w.xCall('sqlite3_wasm_test_int64_minmax', pMin, pMax);
|
||||
T.assert(g64(pMin) === minMaxI64[0], "int64 mismatch").
|
||||
assert(g64(pMax) === minMaxI64[1], "int64 mismatch");
|
||||
//log("pMin",g64(pMin), "pMax",g64(pMax));
|
||||
@ -2560,7 +2560,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t('Close db', function(){
|
||||
T.assert(this.db).assert(wasm.isPtr(this.db.pointer));
|
||||
//wasm.sqlite3__wasm_db_reset(this.db); // will leak virtual tables!
|
||||
//wasm.sqlite3_wasm_db_reset(this.db); // will leak virtual tables!
|
||||
this.db.close();
|
||||
T.assert(!this.db.pointer);
|
||||
})
|
||||
@ -2587,7 +2587,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
const pVfs = capi.sqlite3_vfs_find('kvvfs');
|
||||
T.assert(pVfs);
|
||||
const JDb = this.JDb = sqlite3.oo1.JsStorageDb;
|
||||
const unlink = this.kvvfsUnlink = ()=>JDb.clearStorage(this.kvvfsDbFile);
|
||||
const unlink = this.kvvfsUnlink = ()=>{JDb.clearStorage(filename)};
|
||||
unlink();
|
||||
let db = new JDb(filename);
|
||||
try {
|
||||
@ -2605,60 +2605,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
}
|
||||
}
|
||||
}/*kvvfs sanity checks*/)
|
||||
//#if enable-see
|
||||
.t({
|
||||
name: 'kvvfs with SEE encryption',
|
||||
predicate: ()=>(isUIThread()
|
||||
|| "Only available in main thread."),
|
||||
test: function(sqlite3){
|
||||
this.kvvfsUnlink();
|
||||
let db;
|
||||
const encOpt1 = 1
|
||||
? {textkey: 'foo'}
|
||||
: {key: 'foo'};
|
||||
const encOpt2 = encOpt1.textkey
|
||||
? encOpt1
|
||||
: {hexkey: new Uint8Array([0x66,0x6f,0x6f]/*==>"foo"*/)}
|
||||
try{
|
||||
db = new this.JDb({
|
||||
filename: this.kvvfsDbFile,
|
||||
...encOpt1
|
||||
});
|
||||
db.exec([
|
||||
"create table t(a,b);",
|
||||
"insert into t(a,b) values(1,2),(3,4)"
|
||||
]);
|
||||
db.close();
|
||||
let err;
|
||||
try{
|
||||
db = new this.JDb({
|
||||
filename: this.kvvfsDbFile,
|
||||
flags: 'ct'
|
||||
});
|
||||
T.assert(db) /* opening is fine, but... */;
|
||||
db.exec("select 1 from sqlite_schema");
|
||||
console.warn("sessionStorage =",sessionStorage);
|
||||
}catch(e){
|
||||
err = e;
|
||||
}finally{
|
||||
db.close();
|
||||
}
|
||||
T.assert(err,"Expecting an exception")
|
||||
.assert(sqlite3.capi.SQLITE_NOTADB==err.resultCode,
|
||||
"Expecting NOTADB");
|
||||
db = new sqlite3.oo1.DB({
|
||||
filename: this.kvvfsDbFile,
|
||||
vfs: 'kvvfs',
|
||||
...encOpt2
|
||||
});
|
||||
T.assert( 4===db.selectValue('select sum(a) from t') );
|
||||
}finally{
|
||||
if( db ) db.close();
|
||||
this.kvvfsUnlink();
|
||||
}
|
||||
}
|
||||
})/*kvvfs with SEE*/
|
||||
//#endif enable-see
|
||||
;/* end kvvfs tests */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@ -2942,17 +2888,18 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.t({
|
||||
name: 'OPFS db sanity checks',
|
||||
test: async function(sqlite3){
|
||||
T.assert(capi.sqlite3_vfs_find('opfs'));
|
||||
const opfs = sqlite3.opfs;
|
||||
const filename = this.opfsDbFile = '/dir/sqlite3-tester1.db';
|
||||
const fileUri = 'file://'+filename+'?delete-before-open=1';
|
||||
const initSql = [
|
||||
'create table p(a);',
|
||||
'insert into p(a) values(1),(2),(3)'
|
||||
];
|
||||
let db = new sqlite3.oo1.OpfsDb(fileUri);
|
||||
const pVfs = this.opfsVfs = capi.sqlite3_vfs_find('opfs');
|
||||
T.assert(pVfs);
|
||||
const unlink = this.opfsUnlink =
|
||||
(fn=filename)=>{wasm.sqlite3_wasm_vfs_unlink(pVfs,fn)};
|
||||
unlink();
|
||||
let db = new sqlite3.oo1.OpfsDb(filename);
|
||||
try {
|
||||
db.exec(initSql);
|
||||
db.exec([
|
||||
'create table p(a);',
|
||||
'insert into p(a) values(1),(2),(3)'
|
||||
]);
|
||||
T.assert(3 === db.selectValue('select count(*) from p'));
|
||||
db.close();
|
||||
db = new sqlite3.oo1.OpfsDb(filename);
|
||||
@ -2964,14 +2911,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
&& 0===this.opfsDbExport.byteLength % 512);
|
||||
}finally{
|
||||
db.close();
|
||||
}
|
||||
T.assert(await opfs.entryExists(filename));
|
||||
try {
|
||||
db = new sqlite3.oo1.OpfsDb(fileUri);
|
||||
db.exec(initSql) /* will throw if delete-before-open did not work */;
|
||||
T.assert(3 === db.selectValue('select count(*) from p'));
|
||||
}finally{
|
||||
if(db) db.close();
|
||||
unlink();
|
||||
}
|
||||
}
|
||||
}/*OPFS db sanity checks*/)
|
||||
@ -2979,17 +2919,15 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
name: 'OPFS import',
|
||||
test: async function(sqlite3){
|
||||
let db;
|
||||
const filename = this.opfsDbFile;
|
||||
try {
|
||||
const exp = this.opfsDbExport;
|
||||
const filename = this.opfsDbFile;
|
||||
delete this.opfsDbExport;
|
||||
this.opfsImportSize = await sqlite3.oo1.OpfsDb.importDb(filename, exp);
|
||||
db = new sqlite3.oo1.OpfsDb(this.opfsDbFile);
|
||||
T.assert(6 === db.selectValue('select count(*) from p')).
|
||||
assert( this.opfsImportSize == exp.byteLength );
|
||||
db.close();
|
||||
const unlink = this.opfsUnlink =
|
||||
(fn=filename)=>sqlite3.util.sqlite3__wasm_vfs_unlink("opfs",fn);
|
||||
this.opfsUnlink(filename);
|
||||
T.assert(!(await sqlite3.opfs.entryExists(filename)));
|
||||
// Try again with a function as an input source:
|
||||
@ -3016,9 +2954,11 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
name: '(Internal-use) OPFS utility APIs',
|
||||
test: async function(sqlite3){
|
||||
const filename = this.opfsDbFile;
|
||||
const pVfs = this.opfsVfs;
|
||||
const unlink = this.opfsUnlink;
|
||||
T.assert(filename && !!unlink);
|
||||
T.assert(filename && pVfs && !!unlink);
|
||||
delete this.opfsDbFile;
|
||||
delete this.opfsVfs;
|
||||
delete this.opfsUnlink;
|
||||
/**************************************************************
|
||||
ATTENTION CLIENT-SIDE USERS: sqlite3.opfs is NOT intended
|
||||
@ -3269,7 +3209,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
print: log,
|
||||
printErr: error
|
||||
}).then(async function(sqlite3){
|
||||
TestUtil.assert(!!sqlite3.util);
|
||||
log("Done initializing WASM/JS bits. Running tests...");
|
||||
sqlite3.config.warn("Installing sqlite3 bits as global S for local dev/test purposes.");
|
||||
globalThis.S = sqlite3;
|
||||
@ -3288,9 +3227,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
logClass('warning',"BigInt/int64 support is disabled.");
|
||||
}
|
||||
if(haveWasmCTests()){
|
||||
log("sqlite3__wasm_test_...() APIs are available.");
|
||||
log("sqlite3_wasm_test_...() APIs are available.");
|
||||
}else{
|
||||
logClass('warning',"sqlite3__wasm_test_...() APIs unavailable.");
|
||||
logClass('warning',"sqlite3_wasm_test_...() APIs unavailable.");
|
||||
}
|
||||
log("registered vfs list =",capi.sqlite3_js_vfs_list().join(', '));
|
||||
TestUtil.runTests(sqlite3);
|
||||
|
||||
67
main.mk
67
main.mk
@ -230,6 +230,7 @@ SRC += \
|
||||
SRC += \
|
||||
$(TOP)/ext/misc/stmt.c
|
||||
|
||||
|
||||
# FTS5 things
|
||||
#
|
||||
FTS5_HDR = \
|
||||
@ -374,9 +375,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/rtree/test_rtreedoc.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/test_recover.c \
|
||||
$(TOP)/ext/intck/test_intck.c \
|
||||
$(TOP)/ext/intck/sqlite3intck.c
|
||||
$(TOP)/ext/recover/test_recover.c
|
||||
|
||||
|
||||
#TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c
|
||||
@ -660,7 +659,7 @@ target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl fts5.c
|
||||
touch target_source
|
||||
|
||||
sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl src-verify
|
||||
tclsh $(TOP)/tool/mksqlite3c.tcl $(EXTRA_SRC)
|
||||
tclsh $(TOP)/tool/mksqlite3c.tcl
|
||||
cp tsrc/sqlite3ext.h .
|
||||
cp $(TOP)/ext/session/sqlite3session.h .
|
||||
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
|
||||
@ -672,7 +671,7 @@ sqlite3ext.h: target_source
|
||||
cp tsrc/sqlite3ext.h .
|
||||
|
||||
sqlite3.c-debug: target_source $(TOP)/tool/mksqlite3c.tcl src-verify
|
||||
tclsh $(TOP)/tool/mksqlite3c.tcl --linemacros=1 $(EXTRA_SRC)
|
||||
tclsh $(TOP)/tool/mksqlite3c.tcl --linemacros=1
|
||||
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
|
||||
cat sqlite3.c >>tclsqlite3.c
|
||||
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
|
||||
@ -718,7 +717,8 @@ opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl
|
||||
tclsh $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c
|
||||
|
||||
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl
|
||||
cat parse.h $(TOP)/src/vdbe.c | tclsh $(TOP)/tool/mkopcodeh.tcl >opcodes.h
|
||||
cat parse.h $(TOP)/src/vdbe.c | \
|
||||
tclsh $(TOP)/tool/mkopcodeh.tcl >opcodes.h
|
||||
|
||||
# Rules to build parse.c and parse.h - the outputs of lemon.
|
||||
#
|
||||
@ -741,37 +741,32 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
./mkkeywordhash >keywordhash.h
|
||||
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/consio/console_io.c \
|
||||
$(TOP)/ext/consio/console_io.h \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/intck/sqlite3intck.c \
|
||||
$(TOP)/ext/intck/sqlite3intck.h \
|
||||
$(TOP)/ext/misc/appendvfs.c \
|
||||
$(TOP)/ext/misc/base64.c \
|
||||
$(TOP)/ext/misc/base85.c \
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/memtrace.c \
|
||||
$(TOP)/ext/misc/pcachetrace.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/misc/uint.c \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.h \
|
||||
$(TOP)/src/test_windirent.c \
|
||||
$(TOP)/src/test_windirent.h
|
||||
# Source files that go into making shell.c
|
||||
SHELL_SRC = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/misc/appendvfs.c \
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/base64.c \
|
||||
$(TOP)/ext/misc/base85.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/misc/uint.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/misc/memtrace.c \
|
||||
$(TOP)/ext/misc/pcachetrace.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.h \
|
||||
$(TOP)/src/test_windirent.c
|
||||
|
||||
shell.c: $(SHELL_DEP) $(TOP)/tool/mkshellc.tcl
|
||||
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
|
||||
tclsh $(TOP)/tool/mkshellc.tcl >shell.c
|
||||
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
c9c2ab54ba1f5f46360f1b4f35d849cd3f080e6fc2b6c60e91b16c63f69a1e33
|
||||
8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355
|
||||
|
||||
@ -1320,7 +1320,7 @@ static int renameResolveTrigger(Parse *pParse){
|
||||
/* ALWAYS() because if the table of the trigger does not exist, the
|
||||
** error would have been hit before this point */
|
||||
if( ALWAYS(pParse->pTriggerTab) ){
|
||||
rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab)!=0;
|
||||
rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab);
|
||||
}
|
||||
|
||||
/* Resolve symbols in WHEN clause */
|
||||
@ -2262,12 +2262,7 @@ void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){
|
||||
if( i==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regOut);
|
||||
}else{
|
||||
char aff = pTab->aCol[i].affinity;
|
||||
if( aff==SQLITE_AFF_REAL ){
|
||||
pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC;
|
||||
}
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
|
||||
pTab->aCol[i].affinity = aff;
|
||||
}
|
||||
nField++;
|
||||
}
|
||||
|
||||
@ -872,7 +872,7 @@ static void statGet(
|
||||
if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
|
||||
sqlite3_str_appendf(&sStat, " %llu", iVal);
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
assert( p->current.anEq[i] || p->nRow==0 );
|
||||
assert( p->current.anEq[i] );
|
||||
#endif
|
||||
}
|
||||
sqlite3ResultStrAccum(context, &sStat);
|
||||
@ -1057,7 +1057,7 @@ static void analyzeOneTable(
|
||||
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int nCol; /* Number of columns in pIdx. "N" */
|
||||
int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */
|
||||
int addrRewind; /* Address of "OP_Rewind iIdxCur" */
|
||||
int addrNextRow; /* Address of "next_row:" */
|
||||
const char *zIdxName; /* Name of the index */
|
||||
int nColTest; /* Number of columns to test for changes */
|
||||
@ -1081,14 +1081,9 @@ static void analyzeOneTable(
|
||||
/*
|
||||
** Pseudo-code for loop that calls stat_push():
|
||||
**
|
||||
** regChng = 0
|
||||
** Rewind csr
|
||||
** if eof(csr){
|
||||
** stat_init() with count = 0;
|
||||
** goto end_of_scan;
|
||||
** }
|
||||
** count()
|
||||
** stat_init()
|
||||
** if eof(csr) goto end_of_scan;
|
||||
** regChng = 0
|
||||
** goto chng_addr_0;
|
||||
**
|
||||
** next_row:
|
||||
@ -1127,36 +1122,41 @@ static void analyzeOneTable(
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
|
||||
/* Implementation of the following:
|
||||
**
|
||||
** regChng = 0
|
||||
** Rewind csr
|
||||
** if eof(csr){
|
||||
** stat_init() with count = 0;
|
||||
** goto end_of_scan;
|
||||
** }
|
||||
** count()
|
||||
** stat_init()
|
||||
** goto chng_addr_0;
|
||||
*/
|
||||
assert( regTemp2==regStat+4 );
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
|
||||
|
||||
/* Arguments to stat_init():
|
||||
/* Invoke the stat_init() function. The arguments are:
|
||||
**
|
||||
** (1) the number of columns in the index including the rowid
|
||||
** (or for a WITHOUT ROWID table, the number of PK columns),
|
||||
** (2) the number of columns in the key without the rowid/pk
|
||||
** (3) estimated number of rows in the index. */
|
||||
** (3) estimated number of rows in the index,
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
|
||||
assert( regRowid==regStat+2 );
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp,
|
||||
OptimizationDisabled(db, SQLITE_Stat4));
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( OptimizationEnabled(db, SQLITE_Stat4) ){
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
|
||||
addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
|
||||
VdbeCoverage(v);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
|
||||
}
|
||||
assert( regTemp2==regStat+4 );
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
|
||||
&statInitFuncdef, 0);
|
||||
addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
|
||||
VdbeCoverage(v);
|
||||
|
||||
/* Implementation of the following:
|
||||
**
|
||||
** Rewind csr
|
||||
** if eof(csr) goto end_of_scan;
|
||||
** regChng = 0
|
||||
** goto next_push_0;
|
||||
**
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
|
||||
addrNextRow = sqlite3VdbeCurrentAddr(v);
|
||||
|
||||
@ -1263,12 +1263,6 @@ static void analyzeOneTable(
|
||||
}
|
||||
|
||||
/* Add the entry to the stat1 table. */
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
/* Partial indexes might get a zero-entry in sqlite_stat1. But
|
||||
** an empty table is omitted from sqlite_stat1. */
|
||||
sqlite3VdbeJumpHere(v, addrGotoEnd);
|
||||
addrGotoEnd = 0;
|
||||
}
|
||||
callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
|
||||
assert( "BBB"[0]==SQLITE_AFF_TEXT );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
|
||||
@ -1292,13 +1286,6 @@ static void analyzeOneTable(
|
||||
int addrIsNull;
|
||||
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
||||
|
||||
/* No STAT4 data is generated if the number of rows is zero */
|
||||
if( addrGotoEnd==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER);
|
||||
addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
if( doOnce ){
|
||||
int mxCol = nCol;
|
||||
Index *pX;
|
||||
@ -1351,7 +1338,7 @@ static void analyzeOneTable(
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/* End of analysis */
|
||||
if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd);
|
||||
sqlite3VdbeJumpHere(v, addrRewind);
|
||||
}
|
||||
|
||||
|
||||
|
||||
172
src/btree.c
172
src/btree.c
@ -151,47 +151,8 @@ int corruptPageError(int lineno, MemPage *p){
|
||||
# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno)
|
||||
#endif
|
||||
|
||||
/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled
|
||||
** or if the lock tracking is disabled. This is always the value for
|
||||
** release builds.
|
||||
*/
|
||||
#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/
|
||||
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
|
||||
#if 0
|
||||
/* ^---- Change to 1 and recompile to enable shared-lock tracing
|
||||
** for debugging purposes.
|
||||
**
|
||||
** Print all shared-cache locks on a BtShared. Debugging use only.
|
||||
*/
|
||||
static void sharedLockTrace(
|
||||
BtShared *pBt,
|
||||
const char *zMsg,
|
||||
int iRoot,
|
||||
int eLockType
|
||||
){
|
||||
BtLock *pLock;
|
||||
if( iRoot>0 ){
|
||||
printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W");
|
||||
}else{
|
||||
printf("%s-%p:", zMsg, pBt);
|
||||
}
|
||||
for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){
|
||||
printf(" %p/%u%s", pLock->pBtree, pLock->iTable,
|
||||
pLock->eLock==READ_LOCK ? "R" : "W");
|
||||
while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){
|
||||
pLock = pLock->pNext;
|
||||
printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
#undef SHARED_LOCK_TRACE
|
||||
#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE)
|
||||
#endif /* Shared-lock tracing */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
**** This function is only used as part of an assert() statement. ***
|
||||
@ -268,8 +229,6 @@ static int hasSharedCacheTableLock(
|
||||
iTab = iRoot;
|
||||
}
|
||||
|
||||
SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType);
|
||||
|
||||
/* Search for the required lock. Either a write-lock on root-page iTab, a
|
||||
** write-lock on the schema table, or (if the client is reading) a
|
||||
** read-lock on iTab will suffice. Return 1 if any of these are found. */
|
||||
@ -403,8 +362,6 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
|
||||
BtLock *pLock = 0;
|
||||
BtLock *pIter;
|
||||
|
||||
SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock);
|
||||
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
|
||||
assert( p->db!=0 );
|
||||
@ -472,8 +429,6 @@ static void clearAllSharedCacheTableLocks(Btree *p){
|
||||
assert( p->sharable || 0==*ppIter );
|
||||
assert( p->inTrans>0 );
|
||||
|
||||
SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0);
|
||||
|
||||
while( *ppIter ){
|
||||
BtLock *pLock = *ppIter;
|
||||
assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );
|
||||
@ -512,9 +467,6 @@ static void clearAllSharedCacheTableLocks(Btree *p){
|
||||
*/
|
||||
static void downgradeAllSharedCacheTableLocks(Btree *p){
|
||||
BtShared *pBt = p->pBt;
|
||||
|
||||
SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0);
|
||||
|
||||
if( pBt->pWriter==p ){
|
||||
BtLock *pLock;
|
||||
pBt->pWriter = 0;
|
||||
@ -5128,12 +5080,9 @@ static int accessPayload(
|
||||
if( pCur->aOverflow==0
|
||||
|| nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
|
||||
){
|
||||
Pgno *aNew;
|
||||
if( sqlite3FaultSim(413) ){
|
||||
aNew = 0;
|
||||
}else{
|
||||
aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno));
|
||||
}
|
||||
Pgno *aNew = (Pgno*)sqlite3Realloc(
|
||||
pCur->aOverflow, nOvfl*2*sizeof(Pgno)
|
||||
);
|
||||
if( aNew==0 ){
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
@ -5143,12 +5092,6 @@ static int accessPayload(
|
||||
memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
|
||||
pCur->curFlags |= BTCF_ValidOvfl;
|
||||
}else{
|
||||
/* Sanity check the validity of the overflow page cache */
|
||||
assert( pCur->aOverflow[0]==nextPage
|
||||
|| pCur->aOverflow[0]==0
|
||||
|| CORRUPT_DB );
|
||||
assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 );
|
||||
|
||||
/* If the overflow page-list cache has been allocated and the
|
||||
** entry for the first required overflow page is valid, skip
|
||||
** directly to it.
|
||||
@ -5630,23 +5573,6 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that
|
||||
** this flags are true for a consistent database.
|
||||
**
|
||||
** This routine is is called from within assert() statements only.
|
||||
** It is an internal verification routine and does not appear in production
|
||||
** builds.
|
||||
*/
|
||||
static int cursorIsAtLastEntry(BtCursor *pCur){
|
||||
int ii;
|
||||
for(ii=0; ii<pCur->iPage; ii++){
|
||||
if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0;
|
||||
}
|
||||
return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Move the cursor to the last entry in the table. Return SQLITE_OK
|
||||
** on success. Set *pRes to 0 if the cursor actually points to something
|
||||
** or set *pRes to 1 if the table is empty.
|
||||
@ -5675,7 +5601,18 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
|
||||
|
||||
/* If the cursor already points to the last entry, this is a no-op. */
|
||||
if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
|
||||
assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB );
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* This block serves to assert() that the cursor really does point
|
||||
** to the last entry in the b-tree. */
|
||||
int ii;
|
||||
for(ii=0; ii<pCur->iPage; ii++){
|
||||
assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell );
|
||||
}
|
||||
assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB );
|
||||
testcase( pCur->ix!=pCur->pPage->nCell-1 );
|
||||
/* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */
|
||||
assert( pCur->pPage->leaf );
|
||||
#endif
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -5728,7 +5665,6 @@ int sqlite3BtreeTableMoveto(
|
||||
}
|
||||
if( pCur->info.nKey<intKey ){
|
||||
if( (pCur->curFlags & BTCF_AtLast)!=0 ){
|
||||
assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB );
|
||||
*pRes = -1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -6195,10 +6131,10 @@ i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
|
||||
/* Currently this interface is only called by the OP_IfSizeBetween
|
||||
** opcode and the OP_Count opcode with P3=1. In either case,
|
||||
** the cursor will always be valid unless the btree is empty. */
|
||||
if( pCur->eState!=CURSOR_VALID ) return 0;
|
||||
/* Currently this interface is only called by the OP_IfSmaller
|
||||
** opcode, and it that case the cursor will always be valid and
|
||||
** will always point to a leaf node. */
|
||||
if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1;
|
||||
if( NEVER(pCur->pPage->leaf==0) ) return -1;
|
||||
|
||||
n = pCur->pPage->nCell;
|
||||
@ -8329,7 +8265,7 @@ static int balance_nonroot(
|
||||
** table-interior, index-leaf, or index-interior).
|
||||
*/
|
||||
if( pOld->aData[0]!=apOld[0]->aData[0] ){
|
||||
rc = SQLITE_CORRUPT_PAGE(pOld);
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
|
||||
@ -8353,7 +8289,7 @@ static int balance_nonroot(
|
||||
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
|
||||
if( pOld->nOverflow>0 ){
|
||||
if( NEVER(limit<pOld->aiOvfl[0]) ){
|
||||
rc = SQLITE_CORRUPT_PAGE(pOld);
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
limit = pOld->aiOvfl[0];
|
||||
@ -8996,7 +8932,7 @@ static int anotherValidCursor(BtCursor *pCur){
|
||||
&& pOther->eState==CURSOR_VALID
|
||||
&& pOther->pPage==pCur->pPage
|
||||
){
|
||||
return SQLITE_CORRUPT_PAGE(pCur->pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@ -9056,7 +8992,7 @@ static int balance(BtCursor *pCur){
|
||||
/* The page being written is not a root page, and there is currently
|
||||
** more than one reference to it. This only happens if the page is one
|
||||
** of its own ancestor pages. Corruption. */
|
||||
rc = SQLITE_CORRUPT_PAGE(pPage);
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
MemPage * const pParent = pCur->apPage[iPage-1];
|
||||
int const iIdx = pCur->aiIdx[iPage-1];
|
||||
@ -9220,7 +9156,7 @@ static SQLITE_NOINLINE int btreeOverwriteOverflowCell(
|
||||
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
|
||||
if( rc ) return rc;
|
||||
if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){
|
||||
rc = SQLITE_CORRUPT_PAGE(pPage);
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
if( iOffset+ovflPageSize<(u32)nTotal ){
|
||||
ovflPgno = get4byte(pPage->aData);
|
||||
@ -9248,7 +9184,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
|
||||
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
|
||||
|| pCur->info.pPayload < pPage->aData + pPage->cellOffset
|
||||
){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( pCur->info.nLocal==nTotal ){
|
||||
/* The entire cell is local */
|
||||
@ -9329,7 +9265,7 @@ int sqlite3BtreeInsert(
|
||||
** Which can only happen if the SQLITE_NoSchemaError flag was set when
|
||||
** the schema was loaded. This cannot be asserted though, as a user might
|
||||
** set the flag, load the schema, and then unset the flag. */
|
||||
return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9452,7 +9388,7 @@ int sqlite3BtreeInsert(
|
||||
if( pPage->nFree<0 ){
|
||||
if( NEVER(pCur->eState>CURSOR_INVALID) ){
|
||||
/* ^^^^^--- due to the moveToRoot() call above */
|
||||
rc = SQLITE_CORRUPT_PAGE(pPage);
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
rc = btreeComputeFreeSpace(pPage);
|
||||
}
|
||||
@ -9494,7 +9430,7 @@ int sqlite3BtreeInsert(
|
||||
CellInfo info;
|
||||
assert( idx>=0 );
|
||||
if( idx>=pPage->nCell ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
if( rc ){
|
||||
@ -9521,10 +9457,10 @@ int sqlite3BtreeInsert(
|
||||
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
|
||||
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
|
||||
if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( oldCell+szNew > pPage->aDataEnd ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
memcpy(oldCell, newCell, szNew);
|
||||
return SQLITE_OK;
|
||||
@ -9626,7 +9562,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
|
||||
nIn = pSrc->info.nLocal;
|
||||
aIn = pSrc->info.pPayload;
|
||||
if( aIn+nIn>pSrc->pPage->aDataEnd ){
|
||||
return SQLITE_CORRUPT_PAGE(pSrc->pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
nRem = pSrc->info.nPayload;
|
||||
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
|
||||
@ -9651,7 +9587,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
|
||||
|
||||
if( nRem>nIn ){
|
||||
if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
|
||||
return SQLITE_CORRUPT_PAGE(pSrc->pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
|
||||
}
|
||||
@ -9747,7 +9683,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID );
|
||||
if( rc || pCur->eState!=CURSOR_VALID ) return rc;
|
||||
}else{
|
||||
return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
@ -9756,14 +9692,14 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
iCellIdx = pCur->ix;
|
||||
pPage = pCur->pPage;
|
||||
if( pPage->nCell<=iCellIdx ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pCell = findCell(pPage, iCellIdx);
|
||||
if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( pCell<&pPage->aCellIdx[pPage->nCell] ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
/* If the BTREE_SAVEPOSITION bit is on, then the cursor position must
|
||||
@ -9854,7 +9790,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
n = pCur->pPage->pgno;
|
||||
}
|
||||
pCell = findCell(pLeaf, pLeaf->nCell-1);
|
||||
if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf);
|
||||
if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
|
||||
nCell = pLeaf->xCellSize(pLeaf, pCell);
|
||||
assert( MX_CELL_SIZE(pBt) >= nCell );
|
||||
pTmp = pBt->pTmpSpace;
|
||||
@ -9970,7 +9906,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
|
||||
*/
|
||||
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
|
||||
if( pgnoRoot>btreePagecount(pBt) ){
|
||||
return SQLITE_CORRUPT_PGNO(pgnoRoot);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pgnoRoot++;
|
||||
|
||||
@ -10018,7 +9954,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
|
||||
}
|
||||
rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage);
|
||||
if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){
|
||||
rc = SQLITE_CORRUPT_PGNO(pgnoRoot);
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(pRoot);
|
||||
@ -10108,14 +10044,14 @@ static int clearDatabasePage(
|
||||
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
if( pgno>btreePagecount(pBt) ){
|
||||
return SQLITE_CORRUPT_PGNO(pgno);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
rc = getAndInitPage(pBt, pgno, &pPage, 0);
|
||||
if( rc ) return rc;
|
||||
if( (pBt->openFlags & BTREE_SINGLE)==0
|
||||
&& sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1))
|
||||
){
|
||||
rc = SQLITE_CORRUPT_PAGE(pPage);
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto cleardatabasepage_out;
|
||||
}
|
||||
hdr = pPage->hdrOffset;
|
||||
@ -10219,7 +10155,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
|
||||
assert( p->inTrans==TRANS_WRITE );
|
||||
assert( iTable>=2 );
|
||||
if( iTable>btreePagecount(pBt) ){
|
||||
return SQLITE_CORRUPT_PGNO(iTable);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
rc = sqlite3BtreeClearTable(p, iTable, 0);
|
||||
@ -10813,9 +10749,6 @@ static int checkTreePage(
|
||||
** number of cells on the page. */
|
||||
nCell = get2byte(&data[hdr+3]);
|
||||
assert( pPage->nCell==nCell );
|
||||
if( pPage->leaf || pPage->intKey==0 ){
|
||||
pCheck->nRow += nCell;
|
||||
}
|
||||
|
||||
/* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
|
||||
** immediately follows the b-tree page header. */
|
||||
@ -10927,7 +10860,6 @@ static int checkTreePage(
|
||||
btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
|
||||
}
|
||||
}
|
||||
assert( heap!=0 );
|
||||
/* Add the freeblocks to the min-heap
|
||||
**
|
||||
** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
|
||||
@ -11027,7 +10959,6 @@ int sqlite3BtreeIntegrityCheck(
|
||||
sqlite3 *db, /* Database connection that is running the check */
|
||||
Btree *p, /* The btree to be checked */
|
||||
Pgno *aRoot, /* An array of root pages numbers for individual trees */
|
||||
Mem *aCnt, /* Memory cells to write counts for each tree to */
|
||||
int nRoot, /* Number of entries in aRoot[] */
|
||||
int mxErr, /* Stop reporting errors after this many */
|
||||
int *pnErr, /* OUT: Write number of errors seen to this variable */
|
||||
@ -11041,9 +10972,7 @@ int sqlite3BtreeIntegrityCheck(
|
||||
int bPartial = 0; /* True if not checking all btrees */
|
||||
int bCkFreelist = 1; /* True to scan the freelist */
|
||||
VVA_ONLY( int nRef );
|
||||
|
||||
assert( nRoot>0 );
|
||||
assert( aCnt!=0 );
|
||||
|
||||
/* aRoot[0]==0 means this is a partial check */
|
||||
if( aRoot[0]==0 ){
|
||||
@ -11116,18 +11045,15 @@ int sqlite3BtreeIntegrityCheck(
|
||||
testcase( pBt->db->flags & SQLITE_CellSizeCk );
|
||||
pBt->db->flags &= ~(u64)SQLITE_CellSizeCk;
|
||||
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
|
||||
sCheck.nRow = 0;
|
||||
if( aRoot[i] ){
|
||||
i64 notUsed;
|
||||
i64 notUsed;
|
||||
if( aRoot[i]==0 ) continue;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
|
||||
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
|
||||
}
|
||||
#endif
|
||||
sCheck.v0 = aRoot[i];
|
||||
checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64);
|
||||
if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
|
||||
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
|
||||
}
|
||||
sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow);
|
||||
#endif
|
||||
sCheck.v0 = aRoot[i];
|
||||
checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64);
|
||||
}
|
||||
pBt->db->flags = savedDbFlags;
|
||||
|
||||
|
||||
@ -331,7 +331,6 @@ int sqlite3BtreeIntegrityCheck(
|
||||
sqlite3 *db, /* Database connection that is running the check */
|
||||
Btree *p, /* The btree to be checked */
|
||||
Pgno *aRoot, /* An array of root pages numbers for individual trees */
|
||||
sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */
|
||||
int nRoot, /* Number of entries in aRoot[] */
|
||||
int mxErr, /* Stop reporting errors after this many */
|
||||
int *pnErr, /* OUT: Write number of errors seen to this variable */
|
||||
|
||||
@ -707,7 +707,6 @@ struct IntegrityCk {
|
||||
StrAccum errMsg; /* Accumulate the error message text here */
|
||||
u32 *heap; /* Min-heap used for analyzing cell coverage */
|
||||
sqlite3 *db; /* Database connection running the check */
|
||||
i64 nRow; /* Number of rows visited in current tree */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
26
src/build.c
26
src/build.c
@ -189,7 +189,7 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
}
|
||||
sqlite3VdbeAddOp0(v, OP_Halt);
|
||||
|
||||
#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE)
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
if( pParse->nTableLock>0 && db->init.busy==0 ){
|
||||
sqlite3UserAuthInit(db);
|
||||
if( db->auth.authLevel<UAUTH_User ){
|
||||
@ -2828,20 +2828,20 @@ void sqlite3EndTable(
|
||||
int regRowid; /* Rowid of the next row to insert */
|
||||
int addrInsLoop; /* Top of the loop for inserting rows */
|
||||
Table *pSelTab; /* A table that describes the SELECT results */
|
||||
int iCsr; /* Write cursor on the new table */
|
||||
|
||||
if( IN_SPECIAL_PARSE ){
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
pParse->nErr++;
|
||||
return;
|
||||
}
|
||||
iCsr = pParse->nTab++;
|
||||
regYield = ++pParse->nMem;
|
||||
regRec = ++pParse->nMem;
|
||||
regRowid = ++pParse->nMem;
|
||||
assert(pParse->nTab==1);
|
||||
sqlite3MayAbort(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
|
||||
pParse->nTab = 2;
|
||||
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
|
||||
if( pParse->nErr ) return;
|
||||
@ -2862,11 +2862,11 @@ void sqlite3EndTable(
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);
|
||||
sqlite3TableAffinity(v, p, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid);
|
||||
sqlite3VdbeGoto(v, addrInsLoop);
|
||||
sqlite3VdbeJumpHere(v, addrInsLoop);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, 1);
|
||||
}
|
||||
|
||||
/* Compute the complete text of the CREATE statement */
|
||||
@ -2923,10 +2923,13 @@ void sqlite3EndTable(
|
||||
/* Test for cycles in generated columns and illegal expressions
|
||||
** in CHECK constraints and in DEFAULT clauses. */
|
||||
if( p->tabFlags & TF_HasGenerated ){
|
||||
sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0,
|
||||
sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
|
||||
sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"",
|
||||
db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
|
||||
sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)",
|
||||
db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
|
||||
}
|
||||
|
||||
/* Add the table to the in-memory representation of the database.
|
||||
@ -3064,9 +3067,8 @@ create_view_fail:
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
/*
|
||||
** The Table structure pTable is really a VIEW. Fill in the names of
|
||||
** the columns of the view in the pTable structure. Return non-zero if
|
||||
** there are errors. If an error is seen an error message is left
|
||||
** in pParse->zErrMsg.
|
||||
** the columns of the view in the pTable structure. Return the number
|
||||
** of errors. If an error is seen leave an error message in pParse->zErrMsg.
|
||||
*/
|
||||
static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
Table *pSelTab; /* A fake table from which we get the result set */
|
||||
@ -3189,7 +3191,7 @@ static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
sqlite3DeleteColumnNames(db, pTable);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIEW */
|
||||
return nErr + pParse->nErr;
|
||||
return nErr;
|
||||
}
|
||||
int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
assert( pTable!=0 );
|
||||
|
||||
1277
src/crypto.c
Normal file
1277
src/crypto.c
Normal file
File diff suppressed because it is too large
Load Diff
370
src/crypto.h
Normal file
370
src/crypto.h
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
** SQLCipher
|
||||
** crypto.h developed by Stephen Lombardo (Zetetic LLC)
|
||||
** sjlombardo at zetetic dot net
|
||||
** http://zetetic.net
|
||||
**
|
||||
** Copyright (c) 2008, ZETETIC LLC
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of the ZETETIC LLC nor the
|
||||
** names of its contributors may be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
|
||||
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
|
||||
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**
|
||||
*/
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
#ifndef CRYPTO_H
|
||||
#define CRYPTO_H
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
#include "pager.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
#if !defined(SQLCIPHER_OMIT_LOG_DEVICE)
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#include <os/log.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32) || defined(SQLITE_OS_WINRT)
|
||||
#include <windows.h> /* amalgamator: dontcache */
|
||||
#else
|
||||
#include <sys/time.h> /* amalgamator: dontcache */
|
||||
#endif
|
||||
|
||||
#ifndef OMIT_MEMLOCK
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(_AIX)
|
||||
#include <errno.h> /* amalgamator: dontcache */
|
||||
#include <unistd.h> /* amalgamator: dontcache */
|
||||
#include <sys/resource.h> /* amalgamator: dontcache */
|
||||
#include <sys/mman.h> /* amalgamator: dontcache */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "sqlcipher.h"
|
||||
|
||||
/* extensions defined in pager.c */
|
||||
void *sqlcipherPagerGetCodec(Pager*);
|
||||
void sqlcipherPagerSetCodec(Pager*, void *(*)(void*,void*,Pgno,int), void (*)(void*,int,int), void (*)(void*), void *);
|
||||
int sqlite3pager_is_sj_pgno(Pager*, Pgno);
|
||||
void sqlite3pager_error(Pager*, int);
|
||||
void sqlite3pager_reset(Pager *pPager);
|
||||
/* end extensions defined in pager.c */
|
||||
|
||||
#if !defined (SQLCIPHER_CRYPTO_CC) \
|
||||
&& !defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) \
|
||||
&& !defined (SQLCIPHER_CRYPTO_NSS) \
|
||||
&& !defined (SQLCIPHER_CRYPTO_OPENSSL)
|
||||
#define SQLCIPHER_CRYPTO_OPENSSL
|
||||
#endif
|
||||
|
||||
#define FILE_HEADER_SZ 16
|
||||
|
||||
#define CIPHER_XSTR(s) CIPHER_STR(s)
|
||||
#define CIPHER_STR(s) #s
|
||||
|
||||
#ifndef CIPHER_VERSION_NUMBER
|
||||
#define CIPHER_VERSION_NUMBER 4.6.0
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER_VERSION_BUILD
|
||||
#define CIPHER_VERSION_BUILD community
|
||||
#endif
|
||||
|
||||
#define CIPHER_DECRYPT 0
|
||||
#define CIPHER_ENCRYPT 1
|
||||
|
||||
#define CIPHER_READ_CTX 0
|
||||
#define CIPHER_WRITE_CTX 1
|
||||
#define CIPHER_READWRITE_CTX 2
|
||||
|
||||
#ifndef PBKDF2_ITER
|
||||
#define PBKDF2_ITER 256000
|
||||
#endif
|
||||
|
||||
#define SQLCIPHER_FLAG_GET(FLAG,BIT) ((FLAG & BIT) != 0)
|
||||
#define SQLCIPHER_FLAG_SET(FLAG,BIT) FLAG |= BIT
|
||||
#define SQLCIPHER_FLAG_UNSET(FLAG,BIT) FLAG &= ~BIT
|
||||
|
||||
/* possible flags for codec_ctx->flags */
|
||||
#define CIPHER_FLAG_HMAC (1 << 0)
|
||||
#define CIPHER_FLAG_LE_PGNO (1 << 1)
|
||||
#define CIPHER_FLAG_BE_PGNO (1 << 2)
|
||||
#define CIPHER_FLAG_KEY_USED (1 << 3)
|
||||
#define CIPHER_FLAG_HAS_KDF_SALT (1 << 4)
|
||||
|
||||
|
||||
#ifndef DEFAULT_CIPHER_FLAGS
|
||||
#define DEFAULT_CIPHER_FLAGS CIPHER_FLAG_HMAC | CIPHER_FLAG_LE_PGNO
|
||||
#endif
|
||||
|
||||
|
||||
/* by default, sqlcipher will use a reduced number of iterations to generate
|
||||
the HMAC key / or transform a raw cipher key
|
||||
*/
|
||||
#ifndef FAST_PBKDF2_ITER
|
||||
#define FAST_PBKDF2_ITER 2
|
||||
#endif
|
||||
|
||||
/* this if a fixed random array that will be xor'd with the database salt to ensure that the
|
||||
salt passed to the HMAC key derivation function is not the same as that used to derive
|
||||
the encryption key. This can be overridden at compile time but it will make the resulting
|
||||
binary incompatible with the default builds when using HMAC. A future version of SQLcipher
|
||||
will likely allow this to be defined at runtime via pragma */
|
||||
#ifndef HMAC_SALT_MASK
|
||||
#define HMAC_SALT_MASK 0x3a
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER_MAX_IV_SZ
|
||||
#define CIPHER_MAX_IV_SZ 16
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER_MAX_KEY_SZ
|
||||
#define CIPHER_MAX_KEY_SZ 64
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Simple shared routines for converting hex char strings to binary data
|
||||
*/
|
||||
static int cipher_hex2int(char c) {
|
||||
return (c>='0' && c<='9') ? (c)-'0' :
|
||||
(c>='A' && c<='F') ? (c)-'A'+10 :
|
||||
(c>='a' && c<='f') ? (c)-'a'+10 : 0;
|
||||
}
|
||||
|
||||
static void cipher_hex2bin(const unsigned char *hex, int sz, unsigned char *out){
|
||||
int i;
|
||||
for(i = 0; i < sz; i += 2){
|
||||
out[i/2] = (cipher_hex2int(hex[i])<<4) | cipher_hex2int(hex[i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void cipher_bin2hex(const unsigned char* in, int sz, char *out) {
|
||||
int i;
|
||||
for(i=0; i < sz; i++) {
|
||||
sqlite3_snprintf(3, out + (i*2), "%02x ", in[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int cipher_isHex(const unsigned char *hex, int sz){
|
||||
int i;
|
||||
for(i = 0; i < sz; i++) {
|
||||
unsigned char c = hex[i];
|
||||
if ((c < '0' || c > '9') &&
|
||||
(c < 'A' || c > 'F') &&
|
||||
(c < 'a' || c > 'f')) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* possible flags for simulating specific test conditions */
|
||||
#ifdef SQLCIPHER_TEST
|
||||
#define TEST_FAIL_ENCRYPT 0x01
|
||||
#define TEST_FAIL_DECRYPT 0x02
|
||||
#define TEST_FAIL_MIGRATE 0x04
|
||||
unsigned int sqlcipher_get_test_flags(void);
|
||||
void sqlcipher_set_test_flags(unsigned int);
|
||||
int sqlcipher_get_test_rand(void);
|
||||
void sqlcipher_set_test_rand(int);
|
||||
int sqlcipher_get_test_fail(void);
|
||||
#endif
|
||||
|
||||
/* extensions defined in crypto_impl.c */
|
||||
/* the default implementation of SQLCipher uses a cipher_ctx
|
||||
to keep track of read / write state separately. The following
|
||||
struct and associated functions are defined here */
|
||||
typedef struct {
|
||||
int derive_key;
|
||||
int pass_sz;
|
||||
unsigned char *key;
|
||||
unsigned char *hmac_key;
|
||||
unsigned char *pass;
|
||||
char *keyspec;
|
||||
} cipher_ctx;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int store_pass;
|
||||
int kdf_iter;
|
||||
int fast_kdf_iter;
|
||||
int kdf_salt_sz;
|
||||
int key_sz;
|
||||
int iv_sz;
|
||||
int block_sz;
|
||||
int page_sz;
|
||||
int keyspec_sz;
|
||||
int reserve_sz;
|
||||
int hmac_sz;
|
||||
int plaintext_header_sz;
|
||||
int hmac_algorithm;
|
||||
int kdf_algorithm;
|
||||
unsigned int flags;
|
||||
unsigned char *kdf_salt;
|
||||
unsigned char *hmac_kdf_salt;
|
||||
unsigned char *buffer;
|
||||
Btree *pBt;
|
||||
cipher_ctx *read_ctx;
|
||||
cipher_ctx *write_ctx;
|
||||
sqlcipher_provider *provider;
|
||||
void *provider_ctx;
|
||||
} codec_ctx ;
|
||||
|
||||
/* crypto.c functions */
|
||||
int sqlcipher_codec_pragma(sqlite3*, int, Parse*, const char *, const char*);
|
||||
int sqlcipherCodecAttach(sqlite3*, int, const void *, int);
|
||||
void sqlcipherCodecGetKey(sqlite3*, int, void**, int*);
|
||||
void sqlcipher_exportFunc(sqlite3_context *, int, sqlite3_value **);
|
||||
|
||||
/* crypto_impl.c functions */
|
||||
|
||||
void sqlcipher_init_memmethods(void);
|
||||
|
||||
/* activation and initialization */
|
||||
void sqlcipher_activate(void);
|
||||
void sqlcipher_deactivate(void);
|
||||
|
||||
int sqlcipher_codec_ctx_init(codec_ctx **, Db *, Pager *, const void *, int);
|
||||
void sqlcipher_codec_ctx_free(codec_ctx **);
|
||||
int sqlcipher_codec_key_derive(codec_ctx *);
|
||||
int sqlcipher_codec_key_copy(codec_ctx *, int);
|
||||
|
||||
/* page cipher implementation */
|
||||
int sqlcipher_page_cipher(codec_ctx *, int, Pgno, int, int, unsigned char *, unsigned char *);
|
||||
|
||||
/* context setters & getters */
|
||||
void sqlcipher_codec_ctx_set_error(codec_ctx *, int);
|
||||
|
||||
void sqlcipher_codec_get_pass(codec_ctx *, void **, int *);
|
||||
int sqlcipher_codec_ctx_set_pass(codec_ctx *, const void *, int, int);
|
||||
void sqlcipher_codec_get_keyspec(codec_ctx *, void **zKey, int *nKey);
|
||||
|
||||
int sqlcipher_codec_ctx_set_pagesize(codec_ctx *, int);
|
||||
int sqlcipher_codec_ctx_get_pagesize(codec_ctx *);
|
||||
int sqlcipher_codec_ctx_get_reservesize(codec_ctx *);
|
||||
|
||||
void sqlcipher_set_default_pagesize(int page_size);
|
||||
int sqlcipher_get_default_pagesize(void);
|
||||
|
||||
void sqlcipher_set_default_kdf_iter(int iter);
|
||||
int sqlcipher_get_default_kdf_iter(void);
|
||||
int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *, int);
|
||||
int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx);
|
||||
|
||||
int sqlcipher_codec_ctx_set_kdf_salt(codec_ctx *ctx, unsigned char *salt, int sz);
|
||||
int sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx, void **salt);
|
||||
|
||||
int sqlcipher_codec_ctx_set_fast_kdf_iter(codec_ctx *, int);
|
||||
int sqlcipher_codec_ctx_get_fast_kdf_iter(codec_ctx *);
|
||||
|
||||
const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx);
|
||||
|
||||
void* sqlcipher_codec_ctx_get_data(codec_ctx *);
|
||||
|
||||
void sqlcipher_set_default_use_hmac(int use);
|
||||
int sqlcipher_get_default_use_hmac(void);
|
||||
|
||||
void sqlcipher_set_hmac_salt_mask(unsigned char mask);
|
||||
unsigned char sqlcipher_get_hmac_salt_mask(void);
|
||||
|
||||
int sqlcipher_codec_ctx_set_use_hmac(codec_ctx *ctx, int use);
|
||||
int sqlcipher_codec_ctx_get_use_hmac(codec_ctx *ctx);
|
||||
|
||||
const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx);
|
||||
int sqlcipher_codec_ctx_migrate(codec_ctx *ctx);
|
||||
int sqlcipher_codec_add_random(codec_ctx *ctx, const char *data, int random_sz);
|
||||
int sqlcipher_cipher_profile(sqlite3 *db, const char *destination);
|
||||
int sqlcipher_codec_get_store_pass(codec_ctx *ctx);
|
||||
void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey);
|
||||
void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value);
|
||||
int sqlcipher_codec_fips_status(codec_ctx *ctx);
|
||||
const char* sqlcipher_codec_get_provider_version(codec_ctx *ctx);
|
||||
|
||||
int sqlcipher_set_default_plaintext_header_size(int size);
|
||||
int sqlcipher_get_default_plaintext_header_size(void);
|
||||
int sqlcipher_codec_ctx_set_plaintext_header_size(codec_ctx *ctx, int size);
|
||||
int sqlcipher_codec_ctx_get_plaintext_header_size(codec_ctx *ctx);
|
||||
|
||||
int sqlcipher_set_default_hmac_algorithm(int algorithm);
|
||||
int sqlcipher_get_default_hmac_algorithm(void);
|
||||
int sqlcipher_codec_ctx_set_hmac_algorithm(codec_ctx *ctx, int algorithm);
|
||||
int sqlcipher_codec_ctx_get_hmac_algorithm(codec_ctx *ctx);
|
||||
|
||||
int sqlcipher_set_default_kdf_algorithm(int algorithm);
|
||||
int sqlcipher_get_default_kdf_algorithm(void);
|
||||
int sqlcipher_codec_ctx_set_kdf_algorithm(codec_ctx *ctx, int algorithm);
|
||||
int sqlcipher_codec_ctx_get_kdf_algorithm(codec_ctx *ctx);
|
||||
|
||||
void sqlcipher_set_mem_security(int);
|
||||
int sqlcipher_get_mem_security(void);
|
||||
|
||||
int sqlcipher_find_db_index(sqlite3 *db, const char *zDb);
|
||||
|
||||
int sqlcipher_codec_ctx_integrity_check(codec_ctx *, Parse *, char *);
|
||||
|
||||
int sqlcipher_set_log(const char *destination);
|
||||
void sqlcipher_set_log_level(unsigned int level);
|
||||
|
||||
#define SQLCIPHER_LOG_NONE 0x00
|
||||
#define SQLCIPHER_LOG_ERROR 0x01
|
||||
#define SQLCIPHER_LOG_WARN 0x02
|
||||
#define SQLCIPHER_LOG_INFO 0x04
|
||||
#define SQLCIPHER_LOG_DEBUG 0x08
|
||||
#define SQLCIPHER_LOG_TRACE 0x10
|
||||
#define SQLCIPHER_LOG_ALL 0xffffffff
|
||||
|
||||
#ifdef SQLCIPHER_OMIT_LOG
|
||||
#define sqlcipher_log(tag, message, ...)
|
||||
#else
|
||||
void sqlcipher_log(unsigned int tag, const char *message, ...);
|
||||
#endif
|
||||
|
||||
void sqlcipher_vdbe_return_string(Parse*, const char*, const char*, int);
|
||||
|
||||
#ifdef CODEC_DEBUG_PAGEDATA
|
||||
#define CODEC_HEXDUMP(DESC,BUFFER,LEN) \
|
||||
{ \
|
||||
int __pctr; \
|
||||
printf(DESC); \
|
||||
for(__pctr=0; __pctr < LEN; __pctr++) { \
|
||||
if(__pctr % 16 == 0) printf("\n%05x: ",__pctr); \
|
||||
printf("%02x ",((unsigned char*) BUFFER)[__pctr]); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
#else
|
||||
#define CODEC_HEXDUMP(DESC,BUFFER,LEN)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
#ifdef SQLCIPHER_CRYPTO_CC
|
||||
#include "crypto.h"
|
||||
#include "sqlcipher.h"
|
||||
#include <CommonCrypto/CommonCrypto.h>
|
||||
#include <Security/SecRandom.h>
|
||||
|
||||
1834
src/crypto_impl.c
Normal file
1834
src/crypto_impl.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -48,9 +48,9 @@ static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) {
|
||||
int block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
|
||||
const unsigned char * data = (const unsigned char *)buffer;
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
|
||||
while(data_to_read > 0){
|
||||
rc = fortuna_add_entropy(data, block_sz, &prng);
|
||||
@ -64,9 +64,9 @@ static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) {
|
||||
}
|
||||
fortuna_ready(&prng);
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_add_random: left SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -74,9 +74,9 @@ static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) {
|
||||
static int sqlcipher_ltc_activate(void *ctx) {
|
||||
unsigned char random_buffer[FORTUNA_MAX_SZ];
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
|
||||
sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
|
||||
if(ltc_init == 0) {
|
||||
@ -102,17 +102,17 @@ static int sqlcipher_ltc_activate(void *ctx) {
|
||||
}
|
||||
sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_deactivate(void *ctx) {
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
|
||||
ltc_ref_count--;
|
||||
if(ltc_ref_count == 0){
|
||||
@ -120,9 +120,9 @@ static int sqlcipher_ltc_deactivate(void *ctx) {
|
||||
sqlcipher_memset((void *)&prng, 0, sizeof(prng));
|
||||
}
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_deactivate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -136,15 +136,15 @@ static const char* sqlcipher_ltc_get_provider_version(void *ctx) {
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_random(void *ctx, void *buffer, int length) {
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: entering SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: entered SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
|
||||
fortuna_read(buffer, length, &prng);
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: leaving SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_RAND));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_ltc_random: left SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_ltc_random: left SQLCIPHER_MUTEX_PROVIDER_RAND");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
#ifdef SQLCIPHER_CRYPTO_NSS
|
||||
#include "crypto.h"
|
||||
#include "sqlcipher.h"
|
||||
#include <nss/blapit.h>
|
||||
#include <nss/nss.h>
|
||||
@ -43,25 +44,25 @@ int sqlcipher_nss_setup(sqlcipher_provider *p);
|
||||
|
||||
static int sqlcipher_nss_activate(void *ctx) {
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
if (nss_init_context == NULL) {
|
||||
nss_init_context = NSS_InitContext("", "", "", "", NULL,
|
||||
NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
|
||||
NSS_INIT_FORCEOPEN | NSS_INIT_OPTIMIZESPACE | NSS_INIT_NOROOTINIT);
|
||||
}
|
||||
nss_init_count++;
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_nss_deactivate(void *ctx) {
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entering SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_enter(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: entered SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
|
||||
nss_init_count--;
|
||||
if (nss_init_count == 0 && nss_init_context != NULL) {
|
||||
@ -69,9 +70,9 @@ static int sqlcipher_nss_deactivate(void *ctx) {
|
||||
nss_init_context = NULL;
|
||||
}
|
||||
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: leaving SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlite3_mutex_leave(sqlcipher_mutex(SQLCIPHER_MUTEX_PROVIDER_ACTIVATE));
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, SQLCIPHER_LOG_MUTEX, "sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
sqlcipher_log(SQLCIPHER_LOG_TRACE, "sqlcipher_nss_activate: left SQLCIPHER_MUTEX_PROVIDER_ACTIVATE");
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user