Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b783ec950 |
@@ -1,125 +0,0 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guidance for AI coding agents working in this repository.
|
||||
|
||||
## Project nature
|
||||
|
||||
SQLite is a self-contained, serverless SQL database engine written in C. The
|
||||
source is **public domain** — no copyright or license header should ever be
|
||||
added to any file. The blessing comment that appears at the top of each source
|
||||
file is intentional and should be preserved unchanged.
|
||||
|
||||
SQLite does not accept pull requests without prior agreement and/or
|
||||
accompanying legal paperwork that places the pull request in the public domain.
|
||||
However, the human SQLite developers will review a concise and well-written
|
||||
pull request as a proof-of-concept prior to reimplementing the changes
|
||||
themselves.
|
||||
|
||||
SQLite does not accept agentic code. However the project
|
||||
will accept agentic bug reports that include a reproducible test case.
|
||||
Patches or pull requests demonstrating a possible fix, for documentation
|
||||
purposes, are welcomed.
|
||||
|
||||
SQLite uses the [Fossil](https://fossil-scm.org/) for version control,
|
||||
not Git. The canonical repository is at <https://sqlite.org/src>.
|
||||
|
||||
## Build
|
||||
|
||||
The configure script uses [autosetup](https://msteveb.github.io/autosetup/),
|
||||
not GNU Autoconf.
|
||||
|
||||
```bash
|
||||
apt install gcc make tcl-dev # prerequisites (Debian/Ubuntu)
|
||||
|
||||
./configure --dev # debug build
|
||||
make sqlite3 # CLI shell
|
||||
make sqlite3d # Debugging variant of the CLI shell
|
||||
make sqlite3.c # amalgamation (single-file distribution form)
|
||||
make testfixture # test runner binary (requires tcl-dev)
|
||||
make tclextension-install # install TCL extension before running tests
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Tests are TCL scripts run through the `testfixture` enhanced interpreter.
|
||||
|
||||
```bash
|
||||
# From the build directory:
|
||||
./testfixture test/main.test # single test file
|
||||
test/testrunner.tcl # quick suite
|
||||
test/testrunner.tcl full # full suite
|
||||
test/testrunner.tcl fts5% # pattern match
|
||||
|
||||
# Check for failures:
|
||||
grep '!' testrunner.log
|
||||
```
|
||||
|
||||
`make devtest` is the fastest way to run a representative subset. Always run
|
||||
at least `devtest` after any change to `src/`.
|
||||
|
||||
## Architecture
|
||||
|
||||
SQL text → **tokenizer** (`tokenize.c`) → **parser** (`parse.y` / Lemon) →
|
||||
**code generator** (`build.c`, `select.c`, `insert.c`, `update.c`, `delete.c`,
|
||||
`expr.c`) → **optimizer** (`where*.c`) → **VDBE** (`vdbe.c`) → **B-Tree**
|
||||
(`btree.c`) → **Pager** (`pager.c`) → **WAL** (`wal.c`) → **VFS**
|
||||
(`os_unix.c`, `os_win.c`).
|
||||
|
||||
The master internal header is `src/sqliteInt.h`. Major subsystems have private
|
||||
headers: `vdbeInt.h`, `btreeInt.h`, `whereInt.h`. The public API is defined in
|
||||
`src/sqlite.h.in` (template) which generates `sqlite3.h`.
|
||||
|
||||
## Do not edit generated files
|
||||
|
||||
These files are produced by scripts and must not be edited by hand:
|
||||
|
||||
| File | Regenerate with |
|
||||
|---|---|
|
||||
| `sqlite3.h` | `tool/mksqlite3h.tcl` |
|
||||
| `parse.c`, `parse.h` | build lemon (`tool/lemon.c`) then run on `src/parse.y` |
|
||||
| `opcodes.h` | `tool/mkopcodeh.tcl` (reads `src/vdbe.c`) |
|
||||
| `opcodes.c` | `tool/mkopcodec.tcl` (reads `opcodes.h`) |
|
||||
| `keywordhash.h` | `tool/mkkeywordhash.c` |
|
||||
| `pragma.h` | `tool/mkpragmatab.tcl` |
|
||||
| `sqlite3.c` | `tool/mksqlite3c.tcl` (the amalgamation) |
|
||||
|
||||
Editing rules:
|
||||
- To add a PRAGMA: edit `tool/mkpragmatab.tcl`, then regenerate `pragma.h`.
|
||||
- To add a VDBE opcode: add the `case OP_Xxx:` handler in `src/vdbe.c`; the
|
||||
opcode number and name are extracted automatically by `mkopcodeh.tcl`.
|
||||
- To change the SQL grammar: edit `src/parse.y`, not `parse.c`.
|
||||
|
||||
## Coding conventions
|
||||
|
||||
- C89/C99 compatible C only. No C++, no STL, no exceptions, no VLAs.
|
||||
- All memory allocation goes through `sqlite3Malloc` / `sqlite3_malloc64`
|
||||
(never raw `malloc`). The `sqlite3MallocZero` variant zero-initializes.
|
||||
- Integer widths: use `i64` (`sqlite3_int64`) for 64-bit values, `u32`/`u64`
|
||||
for unsigned. Avoid bare `long` or `int` for values that could exceed 2G.
|
||||
- Error propagation: functions return `SQLITE_OK` (0) on success and a
|
||||
`SQLITE_*` error code on failure. Many routines also set `db->mallocFailed`
|
||||
on OOM, allowing deferred error checking.
|
||||
- Assert liberally for invariants that must hold in correct code; use
|
||||
`ALWAYS(x)` / `NEVER(x)` for conditions that are logically always
|
||||
true/false but that the compiler cannot prove.
|
||||
|
||||
## Extensions (`ext/`)
|
||||
|
||||
Extensions are compiled separately from the core and are not part of the
|
||||
amalgamation by default (except where explicitly included). Major subsystems:
|
||||
|
||||
- `ext/fts5/` — Full-Text Search 5 (current)
|
||||
- `ext/rtree/` — R-Tree spatial indexing
|
||||
- `ext/session/` — Changesets and sessions
|
||||
- `ext/recover/` — Database file recovery
|
||||
- `ext/misc/` — Single-file utility extensions (many included in the CLI)
|
||||
- `ext/qrf/` — Query Result Formatter utility library
|
||||
|
||||
## Useful references
|
||||
|
||||
- Architecture: <https://sqlite.org/arch.html>
|
||||
- File format: <https://sqlite.org/fileformat2.html>
|
||||
- VDBE opcodes: <https://sqlite.org/opcode.html>
|
||||
- Query planner: <https://sqlite.org/optoverview.html>
|
||||
- Lemon parser generator: <https://sqlite.org/doc/trunk/doc/lemon.html>
|
||||
- Compile-time options: <https://sqlite.org/compile.html>
|
||||
+3
-11
@@ -131,15 +131,9 @@ libsqlite3.DLL.install-rules = @SQLITE_DLL_INSTALL_RULES@
|
||||
CFLAGS.fuzzcheck-asan.fsanitize = @CFLAGS_ASAN_FSANITIZE@
|
||||
|
||||
#
|
||||
# TARGET_DEBUG is intended to either be empty or be set to -g
|
||||
# -DSQLITE_DEBUG=1, plus any other flags relevant for debug-only
|
||||
# builds. These get added near the front of $(T.cc) so that they can
|
||||
# be overridden by CFLAGS passed directly to make. Historically (prior
|
||||
# to 2026-03-30) these were at/near the end of the flags but that made
|
||||
# it impossible to pass custom -O* flags to specific binaries without
|
||||
# reconfiguring.
|
||||
# Intended to either be empty or be set to -g -DSQLITE_DEBUG=1.
|
||||
#
|
||||
T.cc += @TARGET_DEBUG@
|
||||
T.cc.TARGET_DEBUG = @TARGET_DEBUG@
|
||||
|
||||
#
|
||||
# $(JIMSH) and $(CFLAGS.jimsh) are documented in main.mk. $(JIMSH)
|
||||
@@ -170,9 +164,7 @@ OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ $(OPTIONS)
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
|
||||
#
|
||||
# Filename extensions for binaries and libraries. B.* are for the
|
||||
# being-built-on platform and T.* are for the build's target platform.
|
||||
# i.e. B.* is for build-time tooling and T.* is for deliverables.
|
||||
# Filename extensions for binaries and libraries
|
||||
#
|
||||
B.exe = @BUILD_EXEEXT@
|
||||
T.exe = @TARGET_EXEEXT@
|
||||
|
||||
+163
-263
@@ -11,8 +11,6 @@
|
||||
TOP = .
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# Set this non-0 to create and use the SQLite amalgamation file.
|
||||
#
|
||||
!IFNDEF USE_AMALGAMATION
|
||||
@@ -103,6 +101,13 @@ NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use the library paths and other options necessary for
|
||||
# Windows Phone 8.1.
|
||||
#
|
||||
!IFNDEF USE_WP81_OPTS
|
||||
USE_WP81_OPTS = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to split the SQLite amalgamation file into chunks to
|
||||
# be used for debugging with Visual Studio.
|
||||
#
|
||||
@@ -111,8 +116,14 @@ SPLIT_AMALGAMATION = 0
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
# Set this non-0 to have this makefile assume the Tcl shell executable
|
||||
# (tclsh*.exe) is available in the PATH. By default, this is disabled
|
||||
# for compatibility with older build environments. This setting only
|
||||
# applies if TCLSH_CMD is not set manually.
|
||||
#
|
||||
!IFNDEF USE_TCLSH_IN_PATH
|
||||
USE_TCLSH_IN_PATH = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use zlib, possibly compiling it from source code.
|
||||
#
|
||||
@@ -175,6 +186,14 @@ USE_NATIVE_LIBPATHS = 0
|
||||
USE_RC = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to compile binaries suitable for the WinRT environment.
|
||||
# This setting does not apply to any binaries that require Tcl to operate
|
||||
# properly (i.e. the text fixture, etc).
|
||||
#
|
||||
!IFNDEF FOR_WINRT
|
||||
FOR_WINRT = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to compile binaries suitable for the UWP environment.
|
||||
# This setting does not apply to any binaries that require Tcl to operate
|
||||
# properly (i.e. the text fixture, etc).
|
||||
@@ -190,8 +209,6 @@ FOR_WIN10 = 0
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# Set this non-0 to skip attempting to look for and/or link with the Tcl
|
||||
# runtime library.
|
||||
#
|
||||
@@ -248,8 +265,6 @@ DEBUG = 0
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# By default, use --linemacros=1 argument to the mksqlite3c.tcl tool, which
|
||||
# is used to build the amalgamation. This can be turned off to ease debug
|
||||
# of the amalgamation away from the source tree.
|
||||
@@ -345,8 +360,6 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# These are the names of the customized Tcl header files used by various parts
|
||||
# of this makefile when the stdcall calling convention is in use. It is not
|
||||
# used for any other purpose.
|
||||
@@ -628,24 +641,16 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS)
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
|
||||
# <</mark>>
|
||||
|
||||
!ELSE
|
||||
!IFNDEF PLATFORM
|
||||
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
|
||||
# <</mark>>
|
||||
|
||||
!ELSE
|
||||
CORE_CCONV_OPTS =
|
||||
SHELL_CCONV_OPTS =
|
||||
@@ -772,6 +777,18 @@ SHELL_LINK_OPTS = $(SHELL_CORE_LIB)
|
||||
TCC = $(TCC) -FAcs
|
||||
!ENDIF
|
||||
|
||||
# When compiling the library for use in the WinRT environment,
|
||||
# the following compile-time options must be used as well to
|
||||
# disable use of Win32 APIs that are not available and to enable
|
||||
# use of Win32 APIs that are specific to Windows 8 and/or WinRT.
|
||||
#
|
||||
!IF $(FOR_WINRT)!=0
|
||||
TCC = $(TCC) -DSQLITE_OS_WINRT=1
|
||||
RCC = $(RCC) -DSQLITE_OS_WINRT=1
|
||||
TCC = $(TCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
|
||||
RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
|
||||
!ENDIF
|
||||
|
||||
# C compiler options for the Windows 10 platform (needs MSVC 2015).
|
||||
#
|
||||
!IF $(FOR_WIN10)!=0
|
||||
@@ -784,31 +801,25 @@ BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
|
||||
# USE_CRT_DLL option is set to force dynamically linking to the
|
||||
# MSVC runtime library.
|
||||
#
|
||||
!IF $(USE_CRT_DLL)!=0
|
||||
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
|
||||
!IF $(DEBUG)>1
|
||||
TCC = $(TCC) -MDd
|
||||
BCC = $(BCC) -MDd
|
||||
ZLIBCFLAGS = -nologo -MDd -W3 -O2 -Oy- -Zi
|
||||
!ELSE
|
||||
TCC = $(TCC) -MD
|
||||
BCC = $(BCC) -MD
|
||||
ZLIBCFLAGS = -nologo -MD -W3 -O2 -Oy- -Zi
|
||||
!ENDIF
|
||||
!ELSE
|
||||
!IF $(DEBUG)>1
|
||||
TCC = $(TCC) -MTd
|
||||
BCC = $(BCC) -MTd
|
||||
ZLIBCFLAGS = -nologo -MTd -W3 -O2 -Oy- -Zi
|
||||
!ELSE
|
||||
TCC = $(TCC) -MT
|
||||
BCC = $(BCC) -MT
|
||||
ZLIBCFLAGS = -nologo -MT -W3 -O2 -Oy- -Zi
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# The mksqlite3c.tcl and mksqlite3h.tcl scripts will pull in
|
||||
# any extension header files by default. For non-amalgamation
|
||||
# builds, we need to make sure the compiler can find these.
|
||||
@@ -859,6 +870,16 @@ MKSQLITE3H_ARGS =
|
||||
!ENDIF
|
||||
# <</mark>>
|
||||
|
||||
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
# includes extra comments when "EXPLAIN stmt" is used.
|
||||
#
|
||||
!IF $(DEBUG)==0
|
||||
TCC = $(TCC) -DNDEBUG
|
||||
BCC = $(BCC) -DNDEBUG
|
||||
RCC = $(RCC) -DNDEBUG
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>0 || $(API_ARMOR)!=0 || $(FOR_WIN10)!=0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1
|
||||
RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
|
||||
@@ -924,8 +945,6 @@ TCC = $(TCC) /fsanitize=address
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# The locations of the Tcl header and library files. Also, the library that
|
||||
# non-stubs enabled programs using Tcl must link against. These variables
|
||||
# (TCLINCDIR, TCLLIBDIR, and LIBTCL) may be overridden via the environment
|
||||
@@ -1165,8 +1184,6 @@ BCC = $(BCC) -Zi
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# If zlib support is enabled, add the compiler options for it.
|
||||
#
|
||||
!IF $(USE_ZLIB)!=0
|
||||
@@ -1219,6 +1236,56 @@ LTLINKOPTS = /NOLOGO
|
||||
LTLIBOPTS = /NOLOGO
|
||||
!ENDIF
|
||||
|
||||
# When compiling for use in the WinRT environment, the following
|
||||
# linker option must be used to mark the executable as runnable
|
||||
# only in the context of an application container.
|
||||
#
|
||||
!IF $(FOR_WINRT)!=0
|
||||
LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
|
||||
!IF "$(VISUALSTUDIOVERSION)"=="12.0" || "$(VISUALSTUDIOVERSION)"=="14.0"
|
||||
!IFNDEF STORELIBPATH
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store
|
||||
!ELSEIF "$(PLATFORM)"=="x64"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store\amd64
|
||||
!ELSEIF "$(PLATFORM)"=="ARM"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store\arm
|
||||
!ELSE
|
||||
STORELIBPATH = $(CRTLIBPATH)\store
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
STORELIBPATH = $(STORELIBPATH:\\=\)
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(STORELIBPATH)"
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# When compiling for Windows Phone 8.1, an extra library path is
|
||||
# required.
|
||||
#
|
||||
!IF $(USE_WP81_OPTS)!=0
|
||||
!IFNDEF WP81LIBPATH
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
|
||||
!ELSEIF "$(PLATFORM)"=="ARM"
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\ARM
|
||||
!ELSE
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# When compiling for Windows Phone 8.1, some extra linker options
|
||||
# are also required.
|
||||
#
|
||||
!IF $(USE_WP81_OPTS)!=0
|
||||
!IFDEF WP81LIBPATH
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(WP81LIBPATH)"
|
||||
!ENDIF
|
||||
LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE
|
||||
LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelHost.lib
|
||||
LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
|
||||
!ENDIF
|
||||
|
||||
# When compiling for UWP or the Windows 10 platform, some extra linker
|
||||
# options are also required.
|
||||
#
|
||||
@@ -1242,14 +1309,12 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
|
||||
# If either debugging or symbols are enabled, enable PDBs.
|
||||
#
|
||||
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
|
||||
LDFLAGS = /NODEFAULTLIB:msvcrt /DEBUG $(LDOPTS)
|
||||
LDFLAGS = /DEBUG $(LDOPTS)
|
||||
!ELSE
|
||||
LDFLAGS = /NODEFAULTLIB:msvcrt $(LDOPTS)
|
||||
LDFLAGS = $(LDOPTS)
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# Start with the Tcl related linker options.
|
||||
#
|
||||
!IF $(NO_TCL)==0
|
||||
@@ -1276,8 +1341,6 @@ LTLIBS = $(LTLIBS) $(LIBICU)
|
||||
###############################################################################
|
||||
|
||||
# <<mark>>
|
||||
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
|
||||
#
|
||||
# Object files for the SQLite library (non-amalgamation).
|
||||
#
|
||||
LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
|
||||
@@ -1396,7 +1459,7 @@ SRC01 = \
|
||||
$(TOP)\src\status.c \
|
||||
$(TOP)\src\table.c \
|
||||
$(TOP)\src\threads.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
$(TOP)\src\tokenize.c \
|
||||
$(TOP)\src\treeview.c \
|
||||
$(TOP)\src\trigger.c \
|
||||
@@ -1719,7 +1782,6 @@ FUZZERSHELL_COMPILE_OPTS =
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -I$(TOP)\test -I$(TOP)\ext\recover
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_MEMSYS5
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OSS_FUZZ
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_DECIMAL_MAX_DIGIT=1000
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_CARRAY
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
@@ -1766,18 +1828,7 @@ FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\base64.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\base85.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\completion.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\decimal.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\diskused.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\ieee754.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\randomjson.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\regexp.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\series.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\shathree.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\sha1.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\stmtrand.c
|
||||
|
||||
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
|
||||
DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
|
||||
@@ -1878,8 +1929,8 @@ $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLIT
|
||||
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
# <<mark>>
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS) $(TOP)\tool\winmain.c
|
||||
$(LTLINK) $(NO_WARN) -I$(TOP)\ext\misc $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.c $(TOP)\tool\winmain.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -I$(TOP)\ext\misc $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||
|
||||
dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -1897,6 +1948,9 @@ RSYNC_OPT = \
|
||||
sqlite3_rsync.exe: $(RSYNC_SRC) $(LIBRESOBJS)
|
||||
$(LTLINK) $(RSYNC_OPT) $(NO_WARN) $(RSYNC_SRC) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||
|
||||
scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) -DSCRUB_STANDALONE=1 $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
srcck1.exe: $(TOP)\tool\srcck1.c
|
||||
$(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c
|
||||
|
||||
@@ -1966,18 +2020,8 @@ sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-v
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\split-sqlite3c.tcl
|
||||
|
||||
TCLSQLITEEX = \
|
||||
$(TOP)\ext\qrf\qrf.h \
|
||||
$(TOP)\ext\qrf\qrf.c \
|
||||
$(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite-ex.c: $(TCLSQLITEEX) $(TOP)\tool\mkcombo.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\mkcombo.tcl $(TCLSQLITEEX) -o $@
|
||||
# <</mark>>
|
||||
|
||||
tclsqlite-ex.c:
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: $(SQLITE3C)
|
||||
@@ -2277,11 +2321,11 @@ whereexpr.lo: $(TOP)\src\whereexpr.c $(HDR)
|
||||
window.lo: $(TOP)\src\window.c $(HDR)
|
||||
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\window.c
|
||||
|
||||
tclsqlite.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c /OUT:tclsqlite.lo
|
||||
tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite-shell.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c
|
||||
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
@@ -2334,8 +2378,6 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\qrf\qrf.c \
|
||||
$(TOP)\ext\qrf\qrf.h \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\intck\sqlite3intck.c \
|
||||
@@ -2345,7 +2387,6 @@ SHELL_DEP = \
|
||||
$(TOP)\ext\misc\base85.c \
|
||||
$(TOP)\ext\misc\completion.c \
|
||||
$(TOP)\ext\misc\decimal.c \
|
||||
$(TOP)\ext\misc\diskused.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\memtrace.c \
|
||||
@@ -2376,7 +2417,7 @@ shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\mkshellc.tcl shell.c
|
||||
|
||||
zlib:
|
||||
pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) CFLAGS="$(ZLIBCFLAGS)" && popd
|
||||
pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd
|
||||
|
||||
# Rules to build the extension objects.
|
||||
#
|
||||
@@ -2500,9 +2541,9 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STRICT_SUBTYPE=1
|
||||
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
|
||||
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
|
||||
!IF $(USE_AMALGAMATION)==0
|
||||
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC0)
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
|
||||
!ELSE
|
||||
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC1)
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
|
||||
!ENDIF
|
||||
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
@@ -2550,19 +2591,43 @@ coretestprogs: testfixture.exe sqlite3.exe
|
||||
|
||||
testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe
|
||||
|
||||
fulltest: alltest fuzztest
|
||||
|
||||
alltest: $(TESTPROGS)
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\all.test $(TESTOPTS)
|
||||
|
||||
soaktest: $(TESTPROGS)
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\all.test -soak=1 $(TESTOPTS)
|
||||
|
||||
fulltestonly: $(TESTPROGS) fuzztest
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\full.test
|
||||
|
||||
queryplantest: testfixture.exe shell
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\permutations.test queryplanner $(TESTOPTS)
|
||||
|
||||
fuzztest: fuzzcheck.exe
|
||||
.\fuzzcheck.exe $(FUZZDATA)
|
||||
|
||||
# Legacy testing target for third-party integrators. The SQLite
|
||||
# developers seldom use this target themselves. Instead
|
||||
# they use "nmake /f Makefile.msc devtest" which runs tests on
|
||||
# a standard set of options
|
||||
#
|
||||
# Legacy testing targets, no longer used by the developers and
|
||||
# now aliased to one of the commonly used testing targets.
|
||||
test: $(TESTPROGS) sourcetest fuzztest tcltest
|
||||
|
||||
# Minimal testing that runs in less than 3 minutes (on a fast machine)
|
||||
#
|
||||
quicktest: testfixture.exe sourcetest
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\extraquick.test $(TESTOPTS)
|
||||
|
||||
# This is the common case. Run many tests that do not take too long,
|
||||
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
|
||||
#
|
||||
quicktest: devtest
|
||||
test: devtest
|
||||
fulltest: releasetest
|
||||
alltest: releasetest
|
||||
soaktest: releasetest
|
||||
fulltestonly: releasetest
|
||||
|
||||
# The veryquick.test TCL tests.
|
||||
#
|
||||
@@ -2587,22 +2652,6 @@ devtest: srctree-check sourcetest
|
||||
mdevtest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
|
||||
|
||||
# Rerun test cases that failed on the previous testrunner invocation
|
||||
#
|
||||
retest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl retest
|
||||
|
||||
# Show all errors from the most reason testrunner invocation
|
||||
#
|
||||
errors:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl errors
|
||||
|
||||
# Show the status of the current testrunner invocation,
|
||||
# updated every couple of seconds
|
||||
#
|
||||
status:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl status -d 2
|
||||
|
||||
# Validate that various generated files in the source tree
|
||||
# are up-to-date.
|
||||
#
|
||||
@@ -2626,23 +2675,17 @@ smoketest: $(TESTPROGS)
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)
|
||||
|
||||
# Measure the performance of floating-point conversions.
|
||||
#
|
||||
fp-speed-test: fp-speed-1.exe fp-speed-2.exe
|
||||
fp-speed-1 1000000
|
||||
fp-speed-2 1000000
|
||||
shelltest: $(TESTPROGS)
|
||||
.\testfixture.exe $(TOP)\test\permutations.test shell
|
||||
|
||||
shelltest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release shell
|
||||
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) tclsqlite-ex.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl -DINCLUDE_SQLITE3_C $(TOP)\tool\sqlite3_analyzer.c.in > $@
|
||||
|
||||
sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
sqltclsh.c: sqlite3.c tclsqlite-ex.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
|
||||
sqltclsh.c: sqlite3.c $(TOP)\src\tclsqlite.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in >sqltclsh.c
|
||||
|
||||
sqltclsh.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
|
||||
@@ -2652,6 +2695,23 @@ sqltclsh.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
|
||||
sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
|
||||
|
||||
CHECKER_DEPS =\
|
||||
$(TOP)\tool\mkccode.tcl \
|
||||
sqlite3.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
$(TOP)\ext\repair\sqlite3_checker.tcl \
|
||||
$(TOP)\ext\repair\checkindex.c \
|
||||
$(TOP)\ext\repair\checkfreelist.c \
|
||||
$(TOP)\ext\misc\btreeinfo.c \
|
||||
$(TOP)\ext\repair\sqlite3_checker.c.in
|
||||
|
||||
sqlite3_checker.c: $(CHECKER_DEPS)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\ext\repair\sqlite3_checker.c.in > $@
|
||||
|
||||
sqlite3_checker.exe: sqlite3_checker.c $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_checker.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
dbdump.exe: $(TOP)\ext\misc\dbdump.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DDBDUMP_STANDALONE $(TOP)\ext\misc\dbdump.c $(SQLITE3C) \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS)
|
||||
@@ -2684,9 +2744,6 @@ showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C) $(SQLITE3H)
|
||||
showshm.exe: $(TOP)\tool\showshm.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\showshm.c /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
showtmlog.exe: $(TOP)\tool\showtmlog.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\showtmlog.c /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
index_usage.exe: $(TOP)\tool\index_usage.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
$(TOP)\tool\index_usage.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -2724,14 +2781,6 @@ speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
$(TOP)\test\speedtest1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
fp-speed-1.exe: $(TOP)\test\fp-speed-1.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
$(TOP)\test\fp-speed-1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
fp-speed-2.exe: $(TOP)\test\fp-speed-2.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
$(TOP)\test\fp-speed-2.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
kvtest.exe: $(TOP)\test\kvtest.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(KV_COMPILE_OPTS) \
|
||||
$(TOP)\test\kvtest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -2740,9 +2789,6 @@ rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H
|
||||
$(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \
|
||||
$(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
$(AUXTEST).exe: $(TOP)\test\c\$(AUXTEST).c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\test\c\$(AUXTEST).c sqlite3.lo /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
THREADTEST3_SRC = \
|
||||
$(TOP)\test\threadtest3.c \
|
||||
$(TOP)\test\tt3_checkpoint.c \
|
||||
@@ -2754,21 +2800,6 @@ THREADTEST3_SRC = \
|
||||
threadtest3.exe: $(THREADTEST3_SRC) $(TOP)\src\test_multiplex.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\test\threadtest3.c $(TOP)\src\test_multiplex.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
# Convenienced targets for common executables that omit
|
||||
# the .exe suffix
|
||||
#
|
||||
sqldiff: sqldiff.exe
|
||||
sqlite3_rsync: sqlite3_rsync.exe
|
||||
fuzzcheck: fuzzcheck.exe
|
||||
testfixture: testfixture.exe
|
||||
sqlite3: $(SQLITE3EXE)
|
||||
showdb: showdb.exe
|
||||
LogEst: LogEst.exe
|
||||
wordcount: wordcount.exe
|
||||
speedtest1: speedtest1.exe
|
||||
kvtest: kvtest.exe
|
||||
jimsh0: jimsh0.exe
|
||||
|
||||
# Display key variables that control which version of TCL is to be used.
|
||||
#
|
||||
tcl-env:
|
||||
@@ -2781,135 +2812,6 @@ tcl-env:
|
||||
@echo JIM_TCLSH = $(JIM_TCLSH)
|
||||
@echo VISUALSTUDIOVERSION = $(VISUALSTUDIOVERSION)
|
||||
|
||||
env:
|
||||
@echo ALL_TCL_TARGETS = $(ALL_TCL_TARGETS)
|
||||
@echo API_ARMOR = $(API_ARMOR)
|
||||
@echo ASAN = $(ASAN)
|
||||
@echo BCC = $(BCC)
|
||||
@echo BUILD_ZLIB = $(BUILD_ZLIB)
|
||||
@echo CC = $(CC)
|
||||
@echo CCOPTS = $(CCOPTS)
|
||||
@echo CHECKER_DEPS = $(CHECKER_DEPS)
|
||||
@echo CORE_CCONV_OPTS = $(CORE_CCONV_OPTS)
|
||||
@echo CORE_COMPILE_OPTS = $(CORE_COMPILE_OPTS)
|
||||
@echo CORE_LINK_DEP = $(CORE_LINK_DEP)
|
||||
@echo CORE_LINK_OPTS = $(CORE_LINK_OPTS)
|
||||
@echo CRTLIBPATH = $(CRTLIBPATH)
|
||||
@echo CSC = $(CSC)
|
||||
@echo DBFUZZ_COMPILE_OPTS = $(DBFUZZ_COMPILE_OPTS)
|
||||
@echo DEBUG = $(DEBUG)
|
||||
@echo DYNAMIC_SHELL = $(DYNAMIC_SHELL)
|
||||
@echo EXT_FEATURE_FLAGS = $(EXT_FEATURE_FLAGS)
|
||||
@echo EXTHDR = $(EXTHDR)
|
||||
@echo EXTRA_SRC = $(EXTRA_SRC)
|
||||
@echo FOR_UWP = $(FOR_UWP)
|
||||
@echo FUZZCHECK_OPTS = $(FUZZCHECK_OPTS)
|
||||
@echo FUZZCHECK_SRC = $(FUZZCHECK_SRC)
|
||||
@echo FUZZDATA = $(FUZZDATA)
|
||||
@echo FUZZERSHELL_COMPILE_OPTS = $(FUZZERSHELL_COMPILE_OPTS)
|
||||
@echo HDR = $(HDR)
|
||||
@echo ICUDIR = $(ICUDIR)
|
||||
@echo ICUINCDIR = $(ICUINCDIR)
|
||||
@echo ICULIBDIR = $(ICULIBDIR)
|
||||
@echo JIM_TCLSH = $(JIM_TCLSH)
|
||||
@echo KV_COMPILE_OPTS = $(KV_COMPILE_OPTS)
|
||||
@echo LDFLAGS = $(LDFLAGS)
|
||||
@echo LD = $(LD)
|
||||
@echo LIBICU = $(LIBICU)
|
||||
@echo LIBOBJ = $(LIBOBJ)
|
||||
@echo LIBREADLINE = $(LIBREADLINE)
|
||||
@echo LIBRESOBJS = $(LIBRESOBJS)
|
||||
@echo LIBTCLPATH = $(LIBTCLPATH)
|
||||
@echo LIBTCLSTUB = $(LIBTCLSTUB)
|
||||
@echo LIBTCL = $(LIBTCL)
|
||||
@echo LTCOMPILE = $(LTCOMPILE)
|
||||
@echo LTLIB = $(LTLIB)
|
||||
@echo LTLIBOPTS = $(LTLIBOPTS)
|
||||
@echo LTLIBPATHS = $(LTLIBPATHS)
|
||||
@echo LTLIBS = $(LTLIBS)
|
||||
@echo LTLINK = $(LTLINK)
|
||||
@echo LTLINKOPTS = $(LTLINKOPTS)
|
||||
@echo LTRCOMPILE = $(LTRCOMPILE)
|
||||
@echo MEMDEBUG = $(MEMDEBUG)
|
||||
@echo MINIMAL_AMALGAMATION = $(MINIMAL_AMALGAMATION)
|
||||
@echo MPTESTER_COMPILE_OPTS = $(MPTESTER_COMPILE_OPTS)
|
||||
@echo NCC = $(NCC)
|
||||
@echo NCRTLIBPATH = $(NCRTLIBPATH)
|
||||
@echo NLTLIBPATHS = $(NLTLIBPATHS)
|
||||
@echo NO_LINEMACROS = $(NO_LINEMACROS)
|
||||
@echo NO_TCL = $(NO_TCL)
|
||||
@echo NO_WARN = $(NO_WARN)
|
||||
@echo NSDKLIBPATH = $(NSDKLIBPATH)
|
||||
@echo NUCRTLIBPATH = $(NUCRTLIBPATH)
|
||||
@echo OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS)
|
||||
@echo OPTIMIZATIONS = $(OPTIMIZATIONS)
|
||||
@echo OSSSHELL_SRC = $(OSSSHELL_SRC)
|
||||
@echo OSTRACE = $(OSTRACE)
|
||||
@echo RBU = $(RBU)
|
||||
@echo RCC = $(RCC)
|
||||
@echo RC = $(RC)
|
||||
@echo READLINE_FLAGS = $(READLINE_FLAGS)
|
||||
@echo REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS)
|
||||
@echo RSYNC_OPT = $(RSYNC_OPT)
|
||||
@echo RSYNC_SRC = $(RSYNC_SRC)
|
||||
@echo SESSION = $(SESSION)
|
||||
@echo SETLK_TIMEOUT = $(SETLK_TIMEOUT)
|
||||
@echo SHELL_CCONV_OPTS = $(SHELL_CCONV_OPTS)
|
||||
@echo SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS)
|
||||
@echo SHELL_CORE_DEP = $(SHELL_CORE_DEP)
|
||||
@echo SHELL_CORE_LIB = $(SHELL_CORE_LIB)
|
||||
@echo SHELL_CORE_SRC = $(SHELL_CORE_SRC)
|
||||
@echo SHELL_DEP = $(SHELL_DEP)
|
||||
@echo SHELL_LINK_OPTS = $(SHELL_LINK_OPTS)
|
||||
@echo SPLIT_AMALGAMATION = $(SPLIT_AMALGAMATION)
|
||||
@echo SQLITETCLDECLSH = $(SQLITETCLDECLSH)
|
||||
@echo SQLITE_TCL_DEP = $(SQLITE_TCL_DEP)
|
||||
@echo SQLITETCLH = $(SQLITETCLH)
|
||||
@echo SRC = $(SRC)
|
||||
@echo STATICALLY_LINK_TCL = $(STATICALLY_LINK_TCL)
|
||||
@echo ST_COMPILE_OPTS = $(ST_COMPILE_OPTS)
|
||||
@echo STORELIBPATH = $(STORELIBPATH)
|
||||
@echo SYMBOLS = $(SYMBOLS)
|
||||
@echo TCC = $(TCC)
|
||||
@echo TCLDIR = $(TCLDIR)
|
||||
@echo TCLINCDIR = $(TCLINCDIR)
|
||||
@echo TCLLIBDIR = $(TCLLIBDIR)
|
||||
@echo TCLLIBPATHS = $(TCLLIBPATHS)
|
||||
@echo TCLLIBS = $(TCLLIBS)
|
||||
@echo TCLSH_CMD = $(TCLSH_CMD)
|
||||
@echo TCLSQLITEEX = $(TCLSQLITEEX)
|
||||
@echo TCLSUFFIX = $(TCLSUFFIX)
|
||||
@echo TCLVERSION = $(TCLVERSION)
|
||||
@echo TEST_CCONV_OPTS = $(TEST_CCONV_OPTS)
|
||||
@echo TESTEXT = $(TESTEXT)
|
||||
@echo TESTFIXTURE_DEP = $(TESTFIXTURE_DEP)
|
||||
@echo TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS)
|
||||
@echo TESTFIXTURE_SRC = $(TESTFIXTURE_SRC)
|
||||
@echo TESTOPTS = $(TESTOPTS)
|
||||
@echo TESTPROGS = $(TESTPROGS)
|
||||
@echo TESTSRC = $(TESTSRC)
|
||||
@echo TLIBS = $(TLIBS)
|
||||
@echo TOP = $(TOP)
|
||||
@echo UCRTLIBPATH = $(UCRTLIBPATH)
|
||||
@echo USE_AMALGAMATION = $(USE_AMALGAMATION)
|
||||
@echo USE_CRT_DLL = $(USE_CRT_DLL)
|
||||
@echo USE_FATAL_WARN = $(USE_FATAL_WARN)
|
||||
@echo USE_FULLWARN = $(USE_FULLWARN)
|
||||
@echo USE_ICU = $(USE_ICU)
|
||||
@echo USE_LISTINGS = $(USE_LISTINGS)
|
||||
@echo USE_NATIVE_LIBPATHS = $(USE_NATIVE_LIBPATHS)
|
||||
@echo USE_RC = $(USE_RC)
|
||||
@echo USE_RUNTIME_CHECKS = $(USE_RUNTIME_CHECKS)
|
||||
@echo USE_SEH = $(USE_SEH)
|
||||
@echo USE_STDCALL = $(USE_STDCALL)
|
||||
@echo USE_ZLIB = $(USE_ZLIB)
|
||||
@echo XCOMPILE = $(XCOMPILE)
|
||||
@echo ZLIBCFLAGS = $(ZLIBCFLAGS)
|
||||
@echo ZLIBDIR = $(ZLIBDIR)
|
||||
@echo ZLIBINCDIR = $(ZLIBINCDIR)
|
||||
@echo ZLIBLIBDIR = $(ZLIBLIBDIR)
|
||||
@echo ZLIBLIB = $(ZLIBLIB)
|
||||
|
||||
moreclean: clean
|
||||
del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL
|
||||
# <</mark>>
|
||||
@@ -2939,7 +2841,6 @@ clean:
|
||||
del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL
|
||||
del /Q changeset.exe 2>NUL
|
||||
del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL
|
||||
del /Q fp-speed-1.exe fp-speed-2.exe 2>NUL
|
||||
del /Q mptester.exe wordcount.exe rbu.exe srcck1.exe 2>NUL
|
||||
del /Q sqlite3.c sqlite3-*.c sqlite3.h 2>NUL
|
||||
del /Q sqlite3rc.h 2>NUL
|
||||
@@ -2954,5 +2855,4 @@ clean:
|
||||
del /Q fts5.* fts5parse.* 2>NUL
|
||||
del /q src-verify.exe 2>NUL
|
||||
del /q jimsh.exe jimsh0.exe 2>NUL
|
||||
-$(TCLSH_CMD) test/testrunner.tcl clean
|
||||
# <</mark>>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<h1 align="center">SQLite Source Repository</h1>
|
||||
|
||||
This repository contains the complete source code for the
|
||||
[SQLite database engine](https://sqlite.org/) going back
|
||||
to 2000-05-29. The tree includes many tests and some
|
||||
documentation, though additional tests and most documentation
|
||||
[SQLite database engine](https://sqlite.org/), including
|
||||
many tests. Additional tests and most documentation
|
||||
are managed separately.
|
||||
|
||||
See the [on-line documentation](https://sqlite.org/) for more information
|
||||
@@ -33,11 +32,10 @@ verify its integrity, there are hints on how to do that in the
|
||||
|
||||
## Contacting The SQLite Developers
|
||||
|
||||
The preferred way to ask questions or make comments about SQLite is to
|
||||
visit the [SQLite Forum](https://sqlite.org/forum) at
|
||||
<https://sqlite.org/forum/>. Anonymous postings are permitted. For
|
||||
bug reports, please use the [SQLite bugs
|
||||
forum](https://sqlite.org/bugs) at <https://sqlite.org/bugs>.
|
||||
The preferred way to ask questions or make comments about SQLite or to
|
||||
report bugs against SQLite is to visit the
|
||||
[SQLite Forum](https://sqlite.org/forum) at <https://sqlite.org/forum/>.
|
||||
Anonymous postings are permitted.
|
||||
|
||||
If you think you have found a bug that has security implications and
|
||||
you do not want to report it on the public forum, you can send a private
|
||||
@@ -81,8 +79,8 @@ Then run commands like this:
|
||||
fossil open https://sqlite.org/src
|
||||
|
||||
The initial "fossil open" command will take two or three minutes. Afterwards,
|
||||
you can do fast, bandwidth-efficient updates to whatever versions of SQLite you
|
||||
like. Some examples:
|
||||
you can do fast, bandwidth-efficient updates to the whatever versions
|
||||
of SQLite you like. Some examples:
|
||||
|
||||
fossil update trunk ;# latest trunk check-in
|
||||
fossil update release ;# latest official release
|
||||
@@ -98,21 +96,19 @@ the build products. It is recommended, but not required, that the
|
||||
build directory be separate from the source directory. Cd into the
|
||||
build directory and then from the build directory run the configure
|
||||
script found at the root of the source tree. Then run "make".
|
||||
See the [compile-for-unix.md](doc/compile-for-unix.md) document for
|
||||
more detail.
|
||||
|
||||
For example:
|
||||
|
||||
apt install gcc make tcl-dev ;# Install the necessary build tools
|
||||
apt install gcc make tcl-dev ;# Make sure you have all the necessary build tools
|
||||
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
|
||||
mkdir bld ;# Build happens in a sibling directory
|
||||
mkdir bld ;# Build will occur in a sibling directory
|
||||
cd bld ;# Change to the build directory
|
||||
../sqlite/configure ;# Run the configure script
|
||||
make sqlite3 ;# The "sqlite3" command-line tool
|
||||
make sqlite3.c ;# The "amalgamation" source file
|
||||
make sqldiff ;# The "sqldiff" command-line tool
|
||||
#### Targets below require tcl-dev ####
|
||||
make tclextension-install ;# Install the SQLite TCL extension
|
||||
make sqlite3 ;# Builds the "sqlite3" command-line tool
|
||||
make sqlite3.c ;# Build the "amalgamation" source file
|
||||
make sqldiff ;# Builds the "sqldiff" command-line tool
|
||||
# Makefile targets below this point require tcl-dev
|
||||
make tclextension-install ;# Build and install the SQLite TCL extension
|
||||
make devtest ;# Run development tests
|
||||
make releasetest ;# Run full release tests
|
||||
make sqlite3_analyzer ;# Builds the "sqlite3_analyzer" tool
|
||||
@@ -120,15 +116,14 @@ For example:
|
||||
See the makefile for additional targets. For debugging builds, the
|
||||
core developers typically run "configure" with options like this:
|
||||
|
||||
../sqlite/configure --all --debug CFLAGS='-O0 -g'
|
||||
../sqlite/configure --enable-all --enable-debug CFLAGS='-O0 -g'
|
||||
|
||||
For release builds, the core developers usually do:
|
||||
|
||||
../sqlite/configure --all
|
||||
../sqlite/configure --enable-all
|
||||
|
||||
Core deliverables (sqlite3.c, sqlite3) can be built without a TCL, but
|
||||
many makefile targets require a "tclsh" TCL interpreter version 8.6
|
||||
or later. The "tclextension-install" target and the test targets that follow
|
||||
Almost all makefile targets require a "tclsh" TCL interpreter version 8.6 or
|
||||
later. The "tclextension-install" target and the test targets that follow
|
||||
all require TCL development libraries too. ("apt install tcl-dev"). It is
|
||||
helpful, but is not required, to install the SQLite TCL extension (the
|
||||
"tclextension-install" target) prior to running tests. The "releasetest"
|
||||
@@ -138,20 +133,20 @@ On "make" command-lines, one can add "OPTIONS=..." to specify additional
|
||||
compile-time options over and above those set by ./configure. For example,
|
||||
to compile with the SQLITE_OMIT_DEPRECATED compile-time option, one could say:
|
||||
|
||||
./configure --all
|
||||
./configure --enable-all
|
||||
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3
|
||||
|
||||
The configure script uses [autosetup](https://msteveb.github.io/autosetup/).
|
||||
If the configure script does not work out for you, there is a generic
|
||||
makefile named "Makefile.linux-gcc" in the top directory of the source tree
|
||||
that you can copy and edit to suit your needs. Comments on the generic
|
||||
makefile show what changes are needed.
|
||||
The configure script uses autoconf 2.61 and libtool. If the configure
|
||||
script does not work out for you, there is a generic makefile named
|
||||
"Makefile.linux-gcc" in the top directory of the source tree that you
|
||||
can copy and edit to suit your needs. Comments on the generic makefile
|
||||
show what changes are needed.
|
||||
|
||||
## Compiling for Windows Using MSVC
|
||||
|
||||
On Windows, everything can be compiled with MSVC.
|
||||
You will also need a working installation of TCL if you want to run tests,
|
||||
though TCL is not required if you just want to build SQLite itself.
|
||||
You will also need a working installation of TCL if you want to run tests.
|
||||
TCL is not required if you just want to build SQLite itself.
|
||||
See the [compile-for-windows.md](doc/compile-for-windows.md) document for
|
||||
additional information about how to install MSVC and TCL and configure your
|
||||
build environment.
|
||||
@@ -161,25 +156,24 @@ TCL library, using a command like this:
|
||||
|
||||
set TCLDIR=c:\Tcl
|
||||
|
||||
SQLite itself does not contain any TCL code, but it does use TCL to run
|
||||
tests. You may need to install TCL development libraries in order to
|
||||
successfully complete some makefile targets. It is helpful, but is not
|
||||
required, to install the SQLite TCL extension (the "tclextension-install"
|
||||
target) prior to running tests.
|
||||
SQLite uses "tclsh.exe" as part of the build process, and so that
|
||||
program will need to be somewhere on your %PATH%. SQLite itself
|
||||
does not contain any TCL code, but it does use TCL to run tests.
|
||||
You may need to install TCL development
|
||||
libraries in order to successfully complete some makefile targets.
|
||||
It is helpful, but is not required, to install the SQLite TCL extension
|
||||
(the "tclextension-install" target) prior to running tests.
|
||||
|
||||
The source tree contains a "make.bat" file that allows the same "make"
|
||||
commands of Unix to work on Windows. In the following, you can substitute
|
||||
"nmake /f Makefile.msc" in place of "make", if you prefer to avoid this BAT
|
||||
file:
|
||||
Build using Makefile.msc. Example:
|
||||
|
||||
make sqlite3.exe
|
||||
make sqlite3.c
|
||||
make sqldiff.exe
|
||||
#### Targets below require TCL development libraries ####
|
||||
make tclextension-install
|
||||
make devtest
|
||||
make releasetest
|
||||
make sqlite3_analyzer.exe
|
||||
nmake /f Makefile.msc sqlite3.exe
|
||||
nmake /f Makefile.msc sqlite3.c
|
||||
nmake /f Makefile.msc sqldiff.exe
|
||||
# Makefile targets below this point require TCL development libraries
|
||||
nmake /f Makefile.msc tclextension-install
|
||||
nmake /f Makefile.msc devtest
|
||||
nmake /f Makefile.msc releasetest
|
||||
nmake /f Makefile.msc sqlite3_analyzer.exe
|
||||
|
||||
There are many other makefile targets. See comments in Makefile.msc for
|
||||
details.
|
||||
@@ -187,7 +181,7 @@ details.
|
||||
As with the unix Makefile, the OPTIONS=... argument can be passed on the nmake
|
||||
command-line to enable new compile-time options. For example:
|
||||
|
||||
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
|
||||
nmake /f Makefile.msc OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
|
||||
|
||||
## Source Tree Map
|
||||
|
||||
@@ -200,7 +194,8 @@ command-line to enable new compile-time options. For example:
|
||||
* **test/** - This directory and its subdirectories contains code used
|
||||
for testing. Files that end in "`.test`" are TCL scripts that run
|
||||
tests using an augmented TCL interpreter named "testfixture". Use
|
||||
a command like "`make testfixture`" to build that
|
||||
a command like "`make testfixture`" (unix) or
|
||||
"`nmake /f Makefile.msc testfixture.exe`" (windows) to build that
|
||||
augmented TCL interpreter, then run individual tests using commands like
|
||||
"`testfixture test/main.test`". This test/ subdirectory also contains
|
||||
additional C code modules and scripts for other kinds of testing.
|
||||
@@ -382,11 +377,10 @@ implementation. It will not be the easiest library in the world to hack.
|
||||
(and some other test programs too) is built and run when you type
|
||||
"make test".
|
||||
|
||||
* **VERSION**, **manifest**, **manifest.tags**, and **manifest.uuid** -
|
||||
These files define the current SQLite version number. The "VERSION" file
|
||||
is human generated, but the "manifest", "manifest.tags", and
|
||||
"manifest.uuid" files are automatically generated by the
|
||||
[Fossil version control system](https://fossil-scm.org/).
|
||||
* **VERSION**, **manifest**, and **manifest.uuid** - These files define
|
||||
the current SQLite version number. The "VERSION" file is human generated,
|
||||
but the "manifest" and "manifest.uuid" files are automatically generated
|
||||
by the [Fossil version control system](https://fossil-scm.org/).
|
||||
|
||||
There are many other source files. Each has a succinct header comment that
|
||||
describes its purpose and role within the larger system.
|
||||
@@ -412,6 +406,10 @@ makefile:
|
||||
|
||||
> make verify-source
|
||||
|
||||
Or on windows:
|
||||
|
||||
> nmake /f Makefile.msc verify-source
|
||||
|
||||
Using the makefile to verify source integrity is good for detecting
|
||||
accidental changes to the source tree, but malicious changes could be
|
||||
hidden by also modifying the makefiles.
|
||||
|
||||
@@ -282,7 +282,7 @@ DIST_FILES := \
|
||||
README.txt VERSION \
|
||||
auto.def autosetup configure tea \
|
||||
sqlite3.h sqlite3.c shell.c sqlite3ext.h \
|
||||
Makefile.in Makefile.msc Makefile.fallback make.bat \
|
||||
Makefile.in Makefile.msc Makefile.fallback \
|
||||
sqlite3.rc sqlite3rc.h Replace.cs \
|
||||
sqlite3.pc.in sqlite3.1
|
||||
|
||||
|
||||
+90
-14
@@ -101,6 +101,13 @@ NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use the library paths and other options necessary for
|
||||
# Windows Phone 8.1.
|
||||
#
|
||||
!IFNDEF USE_WP81_OPTS
|
||||
USE_WP81_OPTS = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to split the SQLite amalgamation file into chunks to
|
||||
# be used for debugging with Visual Studio.
|
||||
#
|
||||
@@ -149,6 +156,14 @@ USE_NATIVE_LIBPATHS = 0
|
||||
USE_RC = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to compile binaries suitable for the WinRT environment.
|
||||
# This setting does not apply to any binaries that require Tcl to operate
|
||||
# properly (i.e. the text fixture, etc).
|
||||
#
|
||||
!IFNDEF FOR_WINRT
|
||||
FOR_WINRT = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to compile binaries suitable for the UWP environment.
|
||||
# This setting does not apply to any binaries that require Tcl to operate
|
||||
# properly (i.e. the text fixture, etc).
|
||||
@@ -548,14 +563,10 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
|
||||
|
||||
!ELSE
|
||||
!IFNDEF PLATFORM
|
||||
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
|
||||
|
||||
|
||||
!ELSE
|
||||
CORE_CCONV_OPTS =
|
||||
SHELL_CCONV_OPTS =
|
||||
@@ -656,6 +667,18 @@ SHELL_LINK_OPTS = $(SHELL_CORE_LIB)
|
||||
TCC = $(TCC) -FAcs
|
||||
!ENDIF
|
||||
|
||||
# When compiling the library for use in the WinRT environment,
|
||||
# the following compile-time options must be used as well to
|
||||
# disable use of Win32 APIs that are not available and to enable
|
||||
# use of Win32 APIs that are specific to Windows 8 and/or WinRT.
|
||||
#
|
||||
!IF $(FOR_WINRT)!=0
|
||||
TCC = $(TCC) -DSQLITE_OS_WINRT=1
|
||||
RCC = $(RCC) -DSQLITE_OS_WINRT=1
|
||||
TCC = $(TCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
|
||||
RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
|
||||
!ENDIF
|
||||
|
||||
# C compiler options for the Windows 10 platform (needs MSVC 2015).
|
||||
#
|
||||
!IF $(FOR_WIN10)!=0
|
||||
@@ -668,29 +691,35 @@ BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
|
||||
# USE_CRT_DLL option is set to force dynamically linking to the
|
||||
# MSVC runtime library.
|
||||
#
|
||||
!IF $(USE_CRT_DLL)!=0
|
||||
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
|
||||
!IF $(DEBUG)>1
|
||||
TCC = $(TCC) -MDd
|
||||
BCC = $(BCC) -MDd
|
||||
ZLIBCFLAGS = -nologo -MDd -W3 -O2 -Oy- -Zi
|
||||
!ELSE
|
||||
TCC = $(TCC) -MD
|
||||
BCC = $(BCC) -MD
|
||||
ZLIBCFLAGS = -nologo -MD -W3 -O2 -Oy- -Zi
|
||||
!ENDIF
|
||||
!ELSE
|
||||
!IF $(DEBUG)>1
|
||||
TCC = $(TCC) -MTd
|
||||
BCC = $(BCC) -MTd
|
||||
ZLIBCFLAGS = -nologo -MTd -W3 -O2 -Oy- -Zi
|
||||
!ELSE
|
||||
TCC = $(TCC) -MT
|
||||
BCC = $(BCC) -MT
|
||||
ZLIBCFLAGS = -nologo -MT -W3 -O2 -Oy- -Zi
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
|
||||
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
# includes extra comments when "EXPLAIN stmt" is used.
|
||||
#
|
||||
!IF $(DEBUG)==0
|
||||
TCC = $(TCC) -DNDEBUG
|
||||
BCC = $(BCC) -DNDEBUG
|
||||
RCC = $(RCC) -DNDEBUG
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>0 || $(API_ARMOR)!=0 || $(FOR_WIN10)!=0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1
|
||||
RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
|
||||
@@ -880,6 +909,56 @@ LTLINKOPTS = /NOLOGO
|
||||
LTLIBOPTS = /NOLOGO
|
||||
!ENDIF
|
||||
|
||||
# When compiling for use in the WinRT environment, the following
|
||||
# linker option must be used to mark the executable as runnable
|
||||
# only in the context of an application container.
|
||||
#
|
||||
!IF $(FOR_WINRT)!=0
|
||||
LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
|
||||
!IF "$(VISUALSTUDIOVERSION)"=="12.0" || "$(VISUALSTUDIOVERSION)"=="14.0"
|
||||
!IFNDEF STORELIBPATH
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store
|
||||
!ELSEIF "$(PLATFORM)"=="x64"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store\amd64
|
||||
!ELSEIF "$(PLATFORM)"=="ARM"
|
||||
STORELIBPATH = $(CRTLIBPATH)\store\arm
|
||||
!ELSE
|
||||
STORELIBPATH = $(CRTLIBPATH)\store
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
STORELIBPATH = $(STORELIBPATH:\\=\)
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(STORELIBPATH)"
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# When compiling for Windows Phone 8.1, an extra library path is
|
||||
# required.
|
||||
#
|
||||
!IF $(USE_WP81_OPTS)!=0
|
||||
!IFNDEF WP81LIBPATH
|
||||
!IF "$(PLATFORM)"=="x86"
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
|
||||
!ELSEIF "$(PLATFORM)"=="ARM"
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\ARM
|
||||
!ELSE
|
||||
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# When compiling for Windows Phone 8.1, some extra linker options
|
||||
# are also required.
|
||||
#
|
||||
!IF $(USE_WP81_OPTS)!=0
|
||||
!IFDEF WP81LIBPATH
|
||||
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(WP81LIBPATH)"
|
||||
!ENDIF
|
||||
LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE
|
||||
LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelHost.lib
|
||||
LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
|
||||
!ENDIF
|
||||
|
||||
# When compiling for UWP or the Windows 10 platform, some extra linker
|
||||
# options are also required.
|
||||
#
|
||||
@@ -903,9 +982,9 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
|
||||
# If either debugging or symbols are enabled, enable PDBs.
|
||||
#
|
||||
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
|
||||
LDFLAGS = /NODEFAULTLIB:msvcrt /DEBUG $(LDOPTS)
|
||||
LDFLAGS = /DEBUG $(LDOPTS)
|
||||
!ELSE
|
||||
LDFLAGS = /NODEFAULTLIB:msvcrt $(LDOPTS)
|
||||
LDFLAGS = $(LDOPTS)
|
||||
!ENDIF
|
||||
|
||||
|
||||
@@ -941,7 +1020,6 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_PERCENTILE=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1
|
||||
!ENDIF
|
||||
|
||||
@@ -990,8 +1068,6 @@ $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLIT
|
||||
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
|
||||
tclsqlite-ex.c:
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: $(SQLITE3C)
|
||||
|
||||
+1
-11
@@ -375,11 +375,6 @@ configure process, and check it in.
|
||||
Patching Autosetup for Project-local Changes
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The autosetup files require the following patches after updating
|
||||
from their upstream sources:
|
||||
|
||||
### `--debug` flag
|
||||
|
||||
Autosetup reserves the flag name **`--debug`** for its own purposes,
|
||||
and its own special handling of `--enable-...` flags makes `--debug`
|
||||
an alias for `--enable-debug`. As this project has a long history of
|
||||
@@ -393,11 +388,6 @@ If autosetup is upgraded and this patch is _not_ applied the invoking
|
||||
`./configure` will fail loudly because of the declaration of the
|
||||
`debug` flag in `auto.def` - duplicated flags are not permitted.
|
||||
|
||||
### Fail on `malloc()` error
|
||||
|
||||
See [check-in 72c8a5b94cdf5d](/info/72c8a5b94cdf5d).
|
||||
|
||||
|
||||
<a name="branch-customization"></a>
|
||||
Branch-specific Customization
|
||||
========================================================================
|
||||
@@ -461,4 +451,4 @@ all other significant processing.
|
||||
[sqlite-config.tcl]: /file/autosetup/sqlite-config.tcl
|
||||
[Makefile.in]: /file/Makefile.in
|
||||
[main.mk]: /file/main.mk
|
||||
[JimTCL]: https://msteveb.github.io/jimtcl/
|
||||
[JimTCL]: https://jim.tcl.tk
|
||||
|
||||
+960
-1647
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -1842,7 +1842,7 @@ proc proj-setup-autoreconfig {defName} {
|
||||
}
|
||||
|
||||
#
|
||||
# @prop-define-append defineName args...
|
||||
# @prop-append-to defineName args...
|
||||
#
|
||||
# A proxy for Autosetup's [define-append]. Appends all non-empty $args
|
||||
# to [define-append $defineName].
|
||||
@@ -1873,7 +1873,7 @@ proc proj-define-append {defineName args} {
|
||||
# but it is technically correct and still relevant on some
|
||||
# environments.
|
||||
#
|
||||
# See: proj-define-append
|
||||
# See: proj-append-to
|
||||
#
|
||||
proc proj-define-amend {args} {
|
||||
set defName ""
|
||||
@@ -2028,7 +2028,7 @@ array set proj__Cache {}
|
||||
# Returns a cache key for the given argument:
|
||||
#
|
||||
# integer: relative call stack levels to get the scope name of for
|
||||
# use as a key. [proj-scope [expr {1 + $arg + $addLevel}]] is
|
||||
# use as a key. [proj-scope [expr {1 + $arg + addLevel}]] is
|
||||
# then used to generate the key. i.e. the default of 0 uses the
|
||||
# calling scope's name as the key.
|
||||
#
|
||||
@@ -2226,7 +2226,7 @@ proc proj-coalesce {args} {
|
||||
#
|
||||
# Pragmas:
|
||||
#
|
||||
# The prototype list may contain :PRAGMAS to modify how it works. The
|
||||
# Passing :PRAGMAS to this function may modify how it works. The
|
||||
# following pragmas are supported (note the leading ":"):
|
||||
#
|
||||
# :all-flags indicates that the whole input list should be scanned,
|
||||
|
||||
+23
-69
@@ -466,7 +466,7 @@ proc sqlite-configure {buildMode configScript} {
|
||||
proc sqlite-configure-phase1 {buildMode} {
|
||||
define PACKAGE_NAME sqlite
|
||||
define PACKAGE_URL {https://sqlite.org}
|
||||
define PACKAGE_BUGREPORT [get-define PACKAGE_URL]/bugs
|
||||
define PACKAGE_BUGREPORT [get-define PACKAGE_URL]/forum
|
||||
define PACKAGE_STRING "[get-define PACKAGE_NAME] [get-define PACKAGE_VERSION]"
|
||||
proj-xfer-options-aliases {
|
||||
# Carry values from hidden --flag aliases over to their canonical
|
||||
@@ -679,7 +679,6 @@ proc sqlite-check-common-system-deps {} {
|
||||
define HAVE_ZLIB 1
|
||||
define LDFLAGS_ZLIB -lz
|
||||
sqlite-add-shell-opt -DSQLITE_HAVE_ZLIB=1
|
||||
sqlite-add-feature-flag -DSQLITE_HAVE_ZLIB=1
|
||||
} else {
|
||||
define HAVE_ZLIB 0
|
||||
define LDFLAGS_ZLIB ""
|
||||
@@ -743,11 +742,12 @@ proc sqlite-setup-default-cflags {} {
|
||||
# compiling binaries for the target system (CC a.k.a. $(T.cc)).
|
||||
# Normally they're the same, but they will differ when
|
||||
# cross-compiling.
|
||||
#
|
||||
# When cross-compiling we default to not using the -g flag, based on a
|
||||
# /chat discussion prompted by
|
||||
# https://sqlite.org/forum/forumpost/9a67df63eda9925c
|
||||
set defaultCFlags {-O2}
|
||||
if {!$::sqliteConfig(is-cross-compiling)} {
|
||||
# When cross-compiling we default to not using the -g flag, based
|
||||
# on a /chat discussion prompted by
|
||||
# https://sqlite.org/forum/forumpost/9a67df63eda9925c
|
||||
lappend defaultCFlags -g
|
||||
}
|
||||
define CFLAGS [proj-get-env CFLAGS $defaultCFlags]
|
||||
@@ -805,8 +805,7 @@ proc sqlite-handle-common-feature-flags {} {
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3
|
||||
}
|
||||
}
|
||||
bytecode-vtab -DSQLITE_ENABLE_BYTECODE_VTAB {}
|
||||
scanstatus {-DSQLITE_ENABLE_STMT_SCANSTATUS -DSQLITE_ENABLE_BYTECODE_VTAB} {}
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
column-metadata -DSQLITE_ENABLE_COLUMN_METADATA {}
|
||||
dbpage -DSQLITE_ENABLE_DBPAGE_VTAB {}
|
||||
dbstat -DSQLITE_ENABLE_DBSTAT_VTAB {}
|
||||
@@ -1067,7 +1066,7 @@ proc sqlite-get-readline-dir-list {} {
|
||||
}
|
||||
*-haiku {
|
||||
lappend dirs /boot/system/develop/headers
|
||||
if {![opt-bool editline] && [opt-val with-readline-ldflags] in {auto ""}} {
|
||||
if {[opt-val with-readline-ldflags] in {auto ""}} {
|
||||
# If the user did not supply their own --with-readline-ldflags
|
||||
# value, hijack that flag to inject options which are known to
|
||||
# work on Haiku OS installations.
|
||||
@@ -1094,30 +1093,16 @@ proc sqlite-get-readline-dir-list {} {
|
||||
# - HAVE_LINENOISE to 0, 1, or 2
|
||||
# - HAVE_EDITLINE to 0 or 1
|
||||
#
|
||||
# Those will be set as follows:
|
||||
#
|
||||
# HAVE_LINENOISE=0, HAVE_EDITLINE=0, HAVE_READLINE=0: no line editing
|
||||
#
|
||||
# HAVE_LINENOISE>0: 1=antirez flavor, 2=msteveb flavor
|
||||
#
|
||||
# HAVE_EDITLINE=0, HAVE_READLINE=1: use GNU readline
|
||||
#
|
||||
# HAVE_EDITLINE=1, HAVE_READLINE=1: use BSD editline header
|
||||
# <readline/readline.h>.
|
||||
#
|
||||
# HAVE_EDITLINE=1, HAVE_READLINE=0: use BSD editline header
|
||||
# <editline/readline.h>.
|
||||
#
|
||||
# Other defines it sets:
|
||||
# Only one of ^^^ those will be set to non-0.
|
||||
#
|
||||
# - LDFLAGS_READLINE = linker flags or empty string
|
||||
#
|
||||
# - CFLAGS_READLINE = compilation flags for clients or empty string.
|
||||
#
|
||||
# Both LDFLAGS_READLINE and CFLAGS_READLINE may refer to linenoise or
|
||||
# editline, not necessarily libreadline. In some cases it will set
|
||||
# HAVE_READLINE=1 when it's really using editline, for reasons
|
||||
# described in this function's comments.
|
||||
# Note that LDFLAGS_READLINE and CFLAGS_READLINE may refer to
|
||||
# linenoise or editline, not necessarily libreadline. In some cases
|
||||
# it will set HAVE_READLINE=1 when it's really using editline, for
|
||||
# reasons described in this function's comments.
|
||||
#
|
||||
# Returns a string describing which line-editing approach to use, or
|
||||
# "none" if no option is available.
|
||||
@@ -1151,27 +1136,6 @@ proc sqlite-check-line-editing {} {
|
||||
set editLibName "readline" ; # "readline" or "editline"
|
||||
set editLibDef "HAVE_READLINE" ; # "HAVE_READLINE" or "HAVE_EDITLINE"
|
||||
set dirLn [opt-val with-linenoise]
|
||||
|
||||
# If none of --with-linenoise, --enable-readline, or --enable-editline
|
||||
# are provided, but there exists a directory "linenoise" at $HOME or
|
||||
# a sibling of the build or source directory, then try to use that linenoise
|
||||
# directory.
|
||||
#
|
||||
if {"" eq $dirLn
|
||||
&& ![proj-opt-was-provided readline]
|
||||
&& ![proj-opt-was-provided editline]
|
||||
} {
|
||||
set dirlist ../linenoise
|
||||
catch {lappend dirlist [file-normalize $::autosetup(srcdir)/../linenoise]}
|
||||
catch {lappend dirlist $::env(HOME)/linenoise}
|
||||
foreach d $dirlist {
|
||||
if {[file exists $d/linenoise.c] && [file exists $d/linenoise.h]} {
|
||||
set dirLn $d
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if {"" ne $dirLn} {
|
||||
# Use linenoise from a copy of its sources (not a library)...
|
||||
if {![file isdir $dirLn]} {
|
||||
@@ -1207,8 +1171,6 @@ proc sqlite-check-line-editing {} {
|
||||
if {$::sqliteConfig(use-jim-for-codegen) && 2 == $lnVal} {
|
||||
define-append CFLAGS_JIMSH -DUSE_LINENOISE [get-define CFLAGS_READLINE]
|
||||
user-notice "Adding linenoise support to jimsh."
|
||||
} else {
|
||||
msg-result "Using linenoise at [file-normalize $dirLn]"
|
||||
}
|
||||
return "linenoise ($flavor)"
|
||||
} elseif {[opt-bool editline]} {
|
||||
@@ -1319,18 +1281,14 @@ proc sqlite-check-line-editing {} {
|
||||
|
||||
# If we found a library, configure the build to use it...
|
||||
if {"" ne $rlLib} {
|
||||
if {"editline" eq $editLibName} {
|
||||
# sqlite-add-shell-opt -DEL_WIDECHAR=1;
|
||||
if {"HAVE_READLINE" eq $editLibDef} {
|
||||
sqlite-add-shell-opt -DHAVE_EDITLINE=1
|
||||
# Alert the user that, despite outward appearances, we won't be
|
||||
# linking to the GPL'd libreadline. Presumably that distinction is
|
||||
# significant for those using --editline.
|
||||
proj-indented-notice {
|
||||
NOTE: the local libedit uses <readline/readline.h> so we
|
||||
will compile with -DHAVE_READLINE=1 but will link with
|
||||
libedit.
|
||||
}
|
||||
if {"editline" eq $editLibName && "HAVE_READLINE" eq $editLibDef} {
|
||||
# Alert the user that, despite outward appearances, we won't be
|
||||
# linking to the GPL'd libreadline. Presumably that distinction is
|
||||
# significant for those using --editline.
|
||||
proj-indented-notice {
|
||||
NOTE: the local libedit uses <readline/readline.h> so we
|
||||
will compile with -DHAVE_READLINE=1 but will link with
|
||||
libedit.
|
||||
}
|
||||
}
|
||||
set rlLib [join $rlLib]
|
||||
@@ -1763,12 +1721,8 @@ proc sqlite-handle-env-quirks {} {
|
||||
set autoDll 0; # true if --out-implib/--dll-basename should be implied
|
||||
set host [get-define host]
|
||||
switch -glob -- $host {
|
||||
*-*-darwin* {
|
||||
set instName darwin
|
||||
# We don't look for *apple* because:
|
||||
# https://sqlite.org/forum/forumpost/7b218c3c9f207646
|
||||
# There's at least one Linux out there which matches *apple*.
|
||||
}
|
||||
*apple* -
|
||||
*darwin* { set instName darwin }
|
||||
default {
|
||||
set x [sqlite-env-is-unix-on-windows $host]
|
||||
if {"" ne $x} {
|
||||
@@ -2064,7 +2018,7 @@ proc sqlite-check-tcl {} {
|
||||
}
|
||||
}
|
||||
if {![file-isexec $with_tclsh]} {
|
||||
proj-warn "Cannot find a usable tclsh (tried: $tryThese)"
|
||||
proj-warn "Cannot find a usable tclsh (tried: $tryThese)
|
||||
}
|
||||
}
|
||||
define TCLSH_CMD $with_tclsh
|
||||
|
||||
+7
-33
@@ -22,10 +22,9 @@ guidance on building for Windows.
|
||||
or <https://sqlite.org/tmp/tcl9.0.0.tar.gz>.
|
||||
<li>Untar the source archive. CD into the "unix/" subfolder
|
||||
of the source tree.
|
||||
<li>Run:   `mkdir $HOME/local`
|
||||
<li>Run:   `./configure --prefix=$HOME/local`<br>
|
||||
SQLite deliverable builds add:   `--static CFLAGS=-Os`
|
||||
<li>Run:   `make install`
|
||||
<li>Run: `mkdir $HOME/local`
|
||||
<li>Run: `./configure --prefix=$HOME/local`
|
||||
<li>Run: `make install`
|
||||
</ol>
|
||||
<p>
|
||||
As of 2024-10-25, TCL is not longer required for many
|
||||
@@ -37,35 +36,12 @@ guidance on building for Windows.
|
||||
4. Download the SQLite source tree and unpack it. CD into the
|
||||
toplevel directory of the source tree.
|
||||
|
||||
5. *(Optional):* Download Antirez's "linenoise" command-line editing
|
||||
library to provide command-line editing in the CLI. You can find
|
||||
a suitable copy of the linenoise sources at
|
||||
<https://sqlite.org/linenoise.tar.gz> or at various other locations
|
||||
on the internet. If you put the linenoise source tree in
|
||||
a directory named $HOME/linenoise or a directory "linenoise" which
|
||||
is a sibling of the SQLite source tree, then the SQLite ./configure
|
||||
script will automatically find and use that source code to provide
|
||||
command-line editing in the CLI. If you would rather use the readline
|
||||
or editline libraries or a precompiled linenoise library, there are
|
||||
./configure options to accommodate that choice. The SQLite developers
|
||||
typically use $HOME/linenoise since linenoise is small, has no
|
||||
external dependencies, "just works", and the ./configure script
|
||||
will pick it up and use it automatically. But you do what works
|
||||
best for you.
|
||||
<p>
|
||||
You are not required to have any command-line editing support in
|
||||
order to use SQLite. But command-line editing does make the
|
||||
interactive experience more enjoyable.
|
||||
|
||||
6. Run: `./configure --enable-all --with-tclsh=$HOME/local/bin/tclsh9.0`
|
||||
5. Run: `./configure --enable-all --with-tclsh=$HOME/local/bin/tclsh9.0`
|
||||
|
||||
You do not need to use --with-tclsh if the tclsh you want to use is the
|
||||
first one on your PATH or if you are building without TCL.
|
||||
|
||||
Lots of other options to ./configure are available.
|
||||
Run `./configure --help` for further guidance.
|
||||
|
||||
7. Run the "`Makefile`" makefile with an appropriate target.
|
||||
6. Run the "`Makefile`" makefile with an appropriate target.
|
||||
Examples:
|
||||
<ul>
|
||||
<li> `make sqlite3.c`
|
||||
@@ -90,7 +66,5 @@ guidance on building for Windows.
|
||||
of SQLite.
|
||||
|
||||
|
||||
8. For a debugging build of the CLI, use `./configure --dev`. A debugging
|
||||
build contains lots of extra debugging code, so it is slow and a lot
|
||||
bigger. You probably do not want to deploy a debugging build. But if
|
||||
you are working on the code, a debugging build works much better.
|
||||
7. For a debugging build of the CLI, where the ".treetrace" and ".wheretrace"
|
||||
commands work, add the the --with-debug argument to configure.
|
||||
|
||||
@@ -180,39 +180,3 @@ statically linked so that it does not depend on separate DLL:
|
||||
6. After your executable is built, you can verify that it does not
|
||||
depend on the TCL DLL by running:
|
||||
<blockquote><pre>dumpbin /dependents sqlite3_analyzer.exe</pre></blockquote>
|
||||
|
||||
## Linking Against ZLIB
|
||||
|
||||
Some feature (such as zip-file support in the CLI) require the ZLIB
|
||||
compression library. That library is more or less universally available
|
||||
on unix platforms, but is seldom provided on Windows. You will probably
|
||||
need to provide it yourself. Here the the steps needed:
|
||||
|
||||
1. Download the zlib-1.3.1.tar.gz tarball (or a similar version).
|
||||
Unpack the tarball sources. You can put them wherever you like.
|
||||
For the purposes of this document, let's assume you put the source
|
||||
tree in c:\\zlib-64. Note: If you are building for both x64 and
|
||||
x86, you will need separate builds of ZLIB for each, thus separate
|
||||
build directories.
|
||||
|
||||
2. Before building SQLite (as described above) first make these
|
||||
environment changes. The lead-programmer for SQLite (who writes
|
||||
these words) has BAT files named "env-x64.bat" and "env-x32.bat"
|
||||
and "env-arm64.bat" that make these changes, and he runs those
|
||||
BAT file whenever he starts a new shell. These are the settings
|
||||
needed:
|
||||
<blockquote>
|
||||
set USE_ZLIB=1<br>
|
||||
set BUILD_ZLIB=0<br>
|
||||
set ZLIBDIR=c:\\zlib-64
|
||||
</blockquote>
|
||||
|
||||
3. Because the settings in step 2 specify "BUILD_ZLIB=0", you will need
|
||||
to build the library at least once. I recommand:
|
||||
<blockquote>
|
||||
make clean sqlite3.exe BUILD_ZLIB=1
|
||||
</blockquote>
|
||||
|
||||
4. After making the environment changes specified in steps 1 through 3
|
||||
above, you then build and test SQLite as you normally would. The
|
||||
environment variable changes will cause ZLIB to be linked automatically.
|
||||
|
||||
+24
-23
@@ -695,8 +695,8 @@ other than that, the order of directives in Lemon is arbitrary.</p>
|
||||
<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='#reallc'>%reallocx</a></tt>
|
||||
<li><tt><a href='#stack_overflow'>%stack_overflow</a></tt>
|
||||
<li><tt><a href='#reallc'>%stack_size_limit</a></tt>
|
||||
<li><tt><a href='#stack_size'>%stack_size</a></tt>
|
||||
<li><tt><a href='#start_symbol'>%start_symbol</a></tt>
|
||||
<li><tt><a href='#syntax_error'>%syntax_error</a></tt>
|
||||
@@ -1204,33 +1204,34 @@ 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>, <tt>%free</tt>, and
|
||||
<tt>%stack_size_limit</tt> directives</h4>
|
||||
<h4>4.4.26 The <tt>%realloc</tt>, <tt>%reallocx</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 and semantics of
|
||||
these functions are similar to the realloc() and free() functions from
|
||||
the standard C library, except that these functions take an extra
|
||||
parameter at the end that is determined by %extra_context. If
|
||||
%extra_context is not defined, then the extra argument is 0. The
|
||||
extra parameter provides the capability to do better error reporting
|
||||
in the event of a memory allocation error, and/or to use an alternative
|
||||
private application heap.
|
||||
<p>The <tt>%realloc</tt>, <tt>%reallocx</tt>, and <tt>%free</tt>
|
||||
directives defines function that allocate and free heap memory.
|
||||
The signatures of th3 %realloc and %free are the same as
|
||||
realloc() and free() functions from the standard C library.
|
||||
The %reallocx function has the same semantics as %realloc but
|
||||
takes four parameters instead of two:
|
||||
<ol>
|
||||
<li> A pointer to the space to be reallocated, or NULL for a new allocation,
|
||||
<li> The number of elements to allocate,
|
||||
<li> The size of each element,
|
||||
<li> The %extra parameter.
|
||||
</ol>
|
||||
The extra parameters of %reallocx can be used, for example, to monitor
|
||||
the parser stack size and raise an error if it exceeds application-defined
|
||||
limits.
|
||||
If both %reallocx and %realloc are defined, then %reallocx is used
|
||||
and %realloc is ignored.
|
||||
|
||||
<p>If both of these functions are defined then they 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
|
||||
<p>If %free is defined and one of %realloc and %reallocx is
|
||||
defined then the 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.
|
||||
|
||||
<p>The <tt>%stack_size_limit</tt> directive defines a function that returns
|
||||
the maximum allowed parser stack size. If this diretive does not exist,
|
||||
no size limit is enforced. This function takes a single argument which
|
||||
is the %extra_context value or "0" if %extra_context is not defined.
|
||||
The function should return an integer that is the maximum
|
||||
number of parser stack entries. If more stack space
|
||||
than this is needed, the %stack_overflow code is invoked.
|
||||
|
||||
<a id='errors'></a>
|
||||
<h2>5.0 Error Processing</h2>
|
||||
|
||||
|
||||
+57
-107
@@ -4,12 +4,6 @@
|
||||
|
||||
<ul type=none>
|
||||
<li> 1. <a href=#overview>Overview</a>
|
||||
<ul type=none>
|
||||
<li> 1.1. <a href=#runtr>Running testrunner.tcl</a>
|
||||
<li> 1.2. <a href=#runviamake>Run using "make"</a>
|
||||
<li> 1.3. <a href=#outputs>Outputs from testrunner.tcl</a>
|
||||
<li> 1.4. <a href=#help>Built-in help</a>
|
||||
</ul>
|
||||
<li> 2. <a href=#binary_tests>Binary Tests</a>
|
||||
<ul type=none>
|
||||
<li> 2.1. <a href=#organization_tests>Organization of Tcl Tests</a>
|
||||
@@ -32,45 +26,17 @@
|
||||
|
||||
The testrunner.tcl program is a Tcl script used to run multiple SQLite
|
||||
tests in parallel, thus reducing testing time on multi-core machines.
|
||||
The testrunner.tcl supports running tests that based on `testfixture`,
|
||||
`sqlite3`, `fuzzcheck`, and C programs found in test/c.
|
||||
It supports the following types of tests:
|
||||
|
||||
<a name="runtr"></a>
|
||||
## 1.1 Running testrunner.tcl
|
||||
* Tcl test scripts.
|
||||
|
||||
The testrunner.tcl script is located in the "test" subdirectory of the
|
||||
SQLite source tree. So if your shell is current positioned at the top
|
||||
of the source tree, you would normally run the script using the command:
|
||||
"<tt>test/testrunner.tcl</tt>". On Windows, you have to specify the
|
||||
`tclsh` interpreter command first, like this:
|
||||
"<tt>tclsh test/testrunner.tcl</tt>".
|
||||
* Fuzzcheck tests, including using an external fuzzcheck database.
|
||||
|
||||
In this document, we will assume that you are on a unix-like OS
|
||||
(not on Windows) and that your current directory is the root
|
||||
of the SQLite source tree, and so all invocations of the testrunner.tcl
|
||||
script will be of the form "<tt>test/testrunner.tcl</tt>". If you
|
||||
are in a different directory, then make appropriate adjustments to
|
||||
the path. On Windows, add the "<tt>tclsh</tt>" interpreter command
|
||||
up front.
|
||||
|
||||
<a name="runviamake"></a>
|
||||
## 1.2 Run using make
|
||||
|
||||
The standard Makefiles for SQLite include targets that invoke
|
||||
testrunner.tcl. So the following commands also run testrunner.tcl:
|
||||
|
||||
* `make test`
|
||||
* `make devtest`
|
||||
* `make releasetest`
|
||||
* `make sdevtest`
|
||||
* `make testrunner`
|
||||
|
||||
Since SQLite 3.53.0, there is a "<tt>make.bat</tt>" script in the root
|
||||
of the source tree that causes the "make" command to work on Windows.
|
||||
There is no need to do something different using "nmake" on Windows.
|
||||
|
||||
<a name="outputs"></a>
|
||||
## 1.3 Outputs from testrunner.tcl
|
||||
* Tests run with `make` commands. Examples:
|
||||
- `make devtest`
|
||||
- `make releasetest`
|
||||
- `make sdevtest`
|
||||
- `make testrunner`
|
||||
|
||||
The testrunner.tcl program stores output of all tests and builds run in
|
||||
log file **testrunner.log**, created in the current working directory.
|
||||
@@ -88,19 +54,17 @@ A useful query might be:
|
||||
```
|
||||
|
||||
You can get a summary of errors in a prior run by invoking commands like
|
||||
those shown below. Note that the testrunner.tcl script can be run directly
|
||||
on unix systems (including Macs) but you will need to add <tt>tclsh</tt>
|
||||
to the front on Windows.
|
||||
these:
|
||||
|
||||
```
|
||||
test/testrunner.tcl errors
|
||||
test/testrunner.tcl errors -v
|
||||
tclsh $(TESTDIR)/testrunner.tcl errors
|
||||
tclsh $(TESTDIR)/testrunner.tcl errors -v
|
||||
```
|
||||
|
||||
Running the command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl status
|
||||
tclsh $(TESTDIR)/testrunner.tcl status
|
||||
```
|
||||
|
||||
in the directory containing the testrunner.db database runs various queries
|
||||
@@ -109,43 +73,32 @@ A good way to keep and eye on test progress is to run either of the two
|
||||
following commands:
|
||||
|
||||
```
|
||||
watch test/testrunner.tcl status
|
||||
test/testrunner.tcl status -d 2
|
||||
watch tclsh $(TESTDIR)/testrunner.tcl status
|
||||
tclsh $(TESTDIR)/testrunner.tcl status -d 2
|
||||
```
|
||||
|
||||
Both of the commands above accomplish about the same thing, but the second
|
||||
one has the advantage of not requiring "watch" to be installed on your
|
||||
system.
|
||||
|
||||
Sometimes testrunner.tcl uses the `testfixture` and `sqlite3` binaries that
|
||||
are in the directory from which testrunner.tcl is run.
|
||||
(see "Binary Tests" below). Sometimes it builds testfixture and
|
||||
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").
|
||||
|
||||
<a name=help></a>
|
||||
## 1.4 Built-in help
|
||||
|
||||
Run this command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl help
|
||||
```
|
||||
|
||||
To get a summary of all of the various command-line options available
|
||||
with testrunner.tcl
|
||||
|
||||
|
||||
<a name=binary_tests></a>
|
||||
# 2. Binary Tests
|
||||
|
||||
The commands described in this section all run various combinations of the Tcl
|
||||
test scripts using whatever `testfixture` binary (and maybe also the `sqlite3`
|
||||
binary, depending on the test) that is found in the directory from which
|
||||
testrunner.tcl is launched. So typically, one must first run something
|
||||
like "`make testfixture sqlite3`" before launching binary tests. In other
|
||||
words, testrunner.tcl does not automatically build the binaries under test
|
||||
for binary tests. The testrunner.tcl expects the binaries to be available
|
||||
already.
|
||||
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
|
||||
these tests is therefore:
|
||||
|
||||
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
|
||||
whatever method seems convenient.
|
||||
|
||||
2. Test the binary built in step 1 by running testrunner.tcl with it,
|
||||
perhaps with various options.
|
||||
|
||||
The following sub-sections describe the various options that can be
|
||||
passed to testrunner.tcl to test binary testfixture builds.
|
||||
@@ -187,22 +140,22 @@ are defined in file *testrunner_data.tcl*.
|
||||
To run the "veryquick" test set, use either of the following:
|
||||
|
||||
```
|
||||
test/testrunner.tcl
|
||||
test/testrunner.tcl veryquick
|
||||
./testfixture $TESTDIR/testrunner.tcl
|
||||
./testfixture $TESTDIR/testrunner.tcl veryquick
|
||||
```
|
||||
|
||||
To run the "full" test suite:
|
||||
|
||||
```
|
||||
test/testrunner.tcl full
|
||||
./testfixture $TESTDIR/testrunner.tcl full
|
||||
```
|
||||
|
||||
To run the subset of the "full" test suite for which the test file name matches
|
||||
a specified pattern (e.g. all tests that start with "fts5"), either of:
|
||||
|
||||
```
|
||||
test/testrunner.tcl fts5%
|
||||
test/testrunner.tcl 'fts5*'
|
||||
./testfixture $TESTDIR/testrunner.tcl fts5%
|
||||
./testfixture $TESTDIR/testrunner.tcl 'fts5*'
|
||||
```
|
||||
|
||||
Strictly speaking, for a test to be run the pattern must match the script
|
||||
@@ -214,7 +167,7 @@ characters specified as part of the pattern are transformed to "\*".
|
||||
To run "all" tests (full + permutations):
|
||||
|
||||
```
|
||||
test/testrunner.tcl all
|
||||
./testfixture $TESTDIR/testrunner.tcl all
|
||||
```
|
||||
|
||||
<a name=binary_test_failures></a>
|
||||
@@ -233,15 +186,10 @@ If there is no permutation, the individual test script may be run with:
|
||||
Or, if the failure occured as part of a permutation:
|
||||
|
||||
```
|
||||
test/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
|
||||
./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
|
||||
```
|
||||
|
||||
One can also rerun all tests that failed or did not complete
|
||||
in the previous invocation by typing:
|
||||
|
||||
```
|
||||
test/testrunner.tcl retest
|
||||
```
|
||||
TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT?
|
||||
|
||||
<a name=source_code_tests></a>
|
||||
# 3. Source Code Tests
|
||||
@@ -256,9 +204,11 @@ other tests. The advantages of this are that:
|
||||
* it ensures that tests are always run using binaries created with the
|
||||
same set of compiler options.
|
||||
|
||||
The testrunner.tcl commands described in this section do not require that
|
||||
the testfixture and/or sqlite3 binaries be built ahead of time. Those
|
||||
binaries will be constructed automatically.
|
||||
The testrunner.tcl commands described in this section may be run using
|
||||
either a *testfixture* (or testfixture.exe) build, or with any other Tcl
|
||||
shell that supports SQLite 3.31.1 or newer via "package require sqlite3".
|
||||
|
||||
TODO: ./configure + Makefile.msc build systems.
|
||||
|
||||
<a name=commands_to_run_tests></a>
|
||||
## 3.1. Commands to Run SQLite Tests
|
||||
@@ -268,7 +218,7 @@ the `make fuzztest` target once for each of two --enable-all builds - one
|
||||
with debugging enabled and one without:
|
||||
|
||||
```
|
||||
test/testrunner.tcl mdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl mdevtest
|
||||
```
|
||||
|
||||
In other words, it is equivalent to running:
|
||||
@@ -277,13 +227,13 @@ In other words, it is equivalent to running:
|
||||
$TOP/configure --enable-all --enable-debug
|
||||
make fuzztest
|
||||
make testfixture
|
||||
$TOP/test/testrunner.tcl veryquick
|
||||
./testfixture $TOP/test/testrunner.tcl veryquick
|
||||
|
||||
# Then, after removing files created by the tests above:
|
||||
$TOP/configure --enable-all OPTS="-O0"
|
||||
make fuzztest
|
||||
make testfixture
|
||||
$TOP/test/testrunner.tcl veryquick
|
||||
./testfixture $TOP/test/testrunner.tcl veryquick
|
||||
```
|
||||
|
||||
The **sdevtest** command is identical to the mdevtest command, except that the
|
||||
@@ -291,7 +241,7 @@ second of the two builds is a sanitizer build. Specifically, this means that
|
||||
OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0":
|
||||
|
||||
```
|
||||
test/testrunner.tcl sdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl sdevtest
|
||||
```
|
||||
|
||||
The **release** command runs lots of tests under lots of builds. It runs
|
||||
@@ -300,7 +250,7 @@ on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details
|
||||
of the specific tests run.
|
||||
|
||||
```
|
||||
test/testrunner.tcl release
|
||||
tclsh $TESTDIR/testrunner.tcl release
|
||||
```
|
||||
|
||||
As with <a href=#source code tests>source code tests</a>, one or more patterns
|
||||
@@ -308,7 +258,7 @@ may be appended to any of the above commands (mdevtest, sdevtest or release).
|
||||
Pattern matching is used for both Tcl tests and fuzz tests.
|
||||
|
||||
```
|
||||
test/testrunner.tcl release rtree%
|
||||
tclsh $TESTDIR/testrunner.tcl release rtree%
|
||||
```
|
||||
|
||||
<a name=zipvfs_tests></a>
|
||||
@@ -318,14 +268,14 @@ testrunner.tcl can build a zipvfs-enabled testfixture and use it to run
|
||||
tests from the Zipvfs project with the following command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
|
||||
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
|
||||
```
|
||||
|
||||
This can be combined with any of "mdevtest", "sdevtest" or "release" to
|
||||
test both SQLite and Zipvfs with a single command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
|
||||
```
|
||||
|
||||
<a name=source_code_test_failures></a>
|
||||
@@ -345,10 +295,10 @@ a dos \*.bat file on windows. For example:
|
||||
```
|
||||
# Create a script that recreates build configuration "Device-One" on
|
||||
# Linux or OSX:
|
||||
test/testrunner.tcl script Device-One > make.sh
|
||||
tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh
|
||||
|
||||
# Create a script that recreates build configuration "Have-Not" on Windows:
|
||||
test/testrunner.tcl script Have-Not > make.bat
|
||||
tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
|
||||
```
|
||||
|
||||
The generated bash or \*.bat file script accepts a single argument - a makefile
|
||||
@@ -371,7 +321,7 @@ Thus, for example, to run a full releasetest including an external
|
||||
dbsqlfuzz database, run a command like one of these:
|
||||
|
||||
```
|
||||
test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
|
||||
tclsh test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
|
||||
FUZZDB=../fuzz/20250415.db make releasetest
|
||||
nmake /f Makefile.msc FUZZDB=../fuzz/20250415.db releasetest
|
||||
```
|
||||
@@ -381,7 +331,7 @@ databases. So if you want to run *only* tests involving the external
|
||||
database, you can use a command something like this:
|
||||
|
||||
```
|
||||
test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
|
||||
tclsh test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
|
||||
```
|
||||
|
||||
<a name=testrunner_options></a>
|
||||
@@ -395,7 +345,7 @@ required by a test, not to run any actual tests. For example:
|
||||
|
||||
```
|
||||
# Build binaries required by release test.
|
||||
test/testrunner.tcl --buildonly release"
|
||||
tclsh $TESTDIR/testrunner.tcl --buildonly release"
|
||||
```
|
||||
|
||||
The **--dryrun** option prevents testrunner.tcl from building any binaries
|
||||
@@ -404,7 +354,7 @@ would normally execute into the testrunner.log file. Example:
|
||||
|
||||
```
|
||||
# Log the shell commmands that make up the mdevtest test.
|
||||
test/testrunner.tcl --dryrun mdevtest"
|
||||
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
|
||||
```
|
||||
|
||||
The **--explain** option is similar to --dryrun in that it prevents
|
||||
@@ -414,7 +364,7 @@ summary of all the builds and tests that would have been run.
|
||||
|
||||
```
|
||||
# Show what builds and tests would have been run
|
||||
test/testrunner.tcl --explain mdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl --explain mdevtest
|
||||
```
|
||||
|
||||
The **--status** option uses VT100 escape sequences to display the test
|
||||
@@ -430,7 +380,7 @@ When running either binary or source code tests, testrunner.tcl reports the
|
||||
number of jobs it intends to use to stdout. e.g.
|
||||
|
||||
```
|
||||
$ test/testrunner.tcl
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl
|
||||
splitting work across 16 jobs
|
||||
... more output ...
|
||||
```
|
||||
@@ -440,7 +390,7 @@ of real cores on the machine. This can be overridden using the "--jobs" (or -j)
|
||||
switch:
|
||||
|
||||
```
|
||||
$ test/testrunner.tcl --jobs 8
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl --jobs 8
|
||||
splitting work across 8 jobs
|
||||
... more output ...
|
||||
```
|
||||
@@ -450,5 +400,5 @@ running by exucuting the following command from the directory containing the
|
||||
testrunner.log and testrunner.db files:
|
||||
|
||||
```
|
||||
$ test/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
|
||||
```
|
||||
|
||||
+1
-37
@@ -90,7 +90,7 @@ foreach {tn setup} {
|
||||
proc do_rec_test {tn sql res} {
|
||||
set res [squish [string trim $res]]
|
||||
set tst [subst -nocommands {
|
||||
squish [string trim [exec $::CLI -noinit test.db ".expert" {$sql;}]]
|
||||
squish [string trim [exec $::CLI test.db ".expert" {$sql;}]]
|
||||
}]
|
||||
uplevel [list do_test $tn $tst $res]
|
||||
}
|
||||
@@ -606,40 +606,4 @@ ifcapable fts5 {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
set ci {CREATE INDEX i1 ON t1(a COLLATE "binary,sqlite_expert_rem(999,0)");}
|
||||
|
||||
do_execsql_test 8.0 {
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE t1(a TEXT, b TEXT);
|
||||
INSERT INTO t1 VALUES('v0','d0');
|
||||
INSERT INTO t1 VALUES('v1','d1');
|
||||
INSERT INTO t1 VALUES('v2','d2');
|
||||
INSERT INTO t1 VALUES('v3','d3');
|
||||
INSERT INTO t1 VALUES('v4','d4');
|
||||
INSERT INTO t1 VALUES('v5','d5');
|
||||
INSERT INTO t1 VALUES('v6','d6');
|
||||
INSERT INTO t1 VALUES('v7','d7');
|
||||
INSERT INTO t1 VALUES('v8','d8');
|
||||
INSERT INTO t1 VALUES('v9','d9');
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
COMMIT;
|
||||
PRAGMA writable_schema = ON;
|
||||
UPDATE sqlite_schema SET sql = $ci WHERE name = 'i1';
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_test 8.1 {
|
||||
set expert [sqlite3_expert_new db]
|
||||
$expert sql { SELECT 1234 }
|
||||
list [catch { $expert analyze } msg] $msg
|
||||
} {1 {no such collation sequence: binary,sqlite_expert_rem(999,0)}}
|
||||
|
||||
$expert destroy
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1501,7 +1501,7 @@ static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
|
||||
/* The statement the vtab will pass to sqlite3_declare_vtab() */
|
||||
zInner = idxAppendText(&rc, 0, "CREATE TABLE x(");
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %Q",
|
||||
zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %s",
|
||||
(i==0 ? "" : ", "), pTab->aCol[i].zName, pTab->aCol[i].zColl
|
||||
);
|
||||
}
|
||||
@@ -1701,7 +1701,7 @@ static int idxPopulateOneStat1(
|
||||
return sqlite3_reset(pIndexXInfo);
|
||||
}
|
||||
zCols = idxAppendText(&rc, zCols,
|
||||
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %Q",
|
||||
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
|
||||
zComma, zName, nCol, zName, zColl
|
||||
);
|
||||
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
|
||||
|
||||
+9
-21
@@ -302,12 +302,6 @@
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Assume any b-tree layer with more levels than this is corrupt.
|
||||
*/
|
||||
#define FTS3_MAX_BTREE_HEIGHT 48
|
||||
|
||||
typedef struct Fts3HashWrapper Fts3HashWrapper;
|
||||
struct Fts3HashWrapper {
|
||||
Fts3Hash hash; /* Hash table */
|
||||
@@ -1822,7 +1816,9 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
|
||||
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
|
||||
if( !zSql ) return SQLITE_NOMEM;
|
||||
p->bLock++;
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
|
||||
rc = sqlite3_prepare_v3(
|
||||
p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
|
||||
);
|
||||
p->bLock--;
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
@@ -2024,11 +2020,7 @@ static int fts3SelectLeaf(
|
||||
assert( piLeaf || piLeaf2 );
|
||||
|
||||
fts3GetVarint32(zNode, &iHeight);
|
||||
if( iHeight>FTS3_MAX_BTREE_HEIGHT ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
}else{
|
||||
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
|
||||
}
|
||||
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
|
||||
assert_fts3_nc( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
|
||||
|
||||
if( rc==SQLITE_OK && iHeight>1 ){
|
||||
@@ -2073,13 +2065,8 @@ static void fts3PutDeltaVarint(
|
||||
sqlite3_int64 iVal /* Write this value to the list */
|
||||
){
|
||||
assert_fts3_nc( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
|
||||
if( iVal-(*piPrev)>=0 ){
|
||||
/* Refuse to write a negative delta integer. This only happens with a
|
||||
** corrupt db (see the assert above) and can cause buffer overwrites
|
||||
** in some cases. */
|
||||
*pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
|
||||
*piPrev = iVal;
|
||||
}
|
||||
*pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
|
||||
*piPrev = iVal;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3406,7 +3393,9 @@ static int fts3FilterMethod(
|
||||
}
|
||||
if( zSql ){
|
||||
p->bLock++;
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
|
||||
rc = sqlite3_prepare_v3(
|
||||
p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
|
||||
);
|
||||
p->bLock--;
|
||||
sqlite3_free(zSql);
|
||||
}else{
|
||||
@@ -4029,7 +4018,6 @@ static int fts3IntegrityMethod(
|
||||
|
||||
UNUSED_PARAMETER(isQuick);
|
||||
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
|
||||
assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK );
|
||||
assert( rc!=SQLITE_CORRUPT_VTAB );
|
||||
if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
|
||||
+1
-19
@@ -203,16 +203,7 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
|
||||
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
|
||||
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
|
||||
|
||||
#if !defined(deliberate_fall_through)
|
||||
# if defined(__has_attribute)
|
||||
# if __has_attribute(fallthrough)
|
||||
# define deliberate_fall_through __attribute__((fallthrough));
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(deliberate_fall_through)
|
||||
# define deliberate_fall_through
|
||||
#endif
|
||||
#define deliberate_fall_through
|
||||
|
||||
/*
|
||||
** Macros needed to provide flexible arrays in a portable way
|
||||
@@ -610,15 +601,6 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
|
||||
(*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
|
||||
)
|
||||
|
||||
int sqlite3Fts3PrepareStmt(
|
||||
Fts3Table *p, /* Prepare for this connection */
|
||||
const char *zSql, /* SQL to prepare */
|
||||
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
|
||||
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
|
||||
sqlite3_stmt **pp /* OUT: Prepared statement */
|
||||
);
|
||||
|
||||
|
||||
/* fts3.c */
|
||||
void sqlite3Fts3ErrMsg(char**,const char*,...);
|
||||
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
|
||||
|
||||
+2
-2
@@ -325,7 +325,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
pCsr->aStat[1].nDoc++;
|
||||
}
|
||||
eState = 2;
|
||||
/* no break */ deliberate_fall_through
|
||||
/* fall through */
|
||||
|
||||
case 2:
|
||||
if( v==0 ){ /* 0x00. Next integer will be a docid. */
|
||||
@@ -341,7 +341,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
/* State 3. The integer just read is a column number. */
|
||||
default: assert( eState==3 );
|
||||
iCol = (int)v;
|
||||
if( iCol<1 || iCol>(pFts3->nColumn+1) ){
|
||||
if( iCol<1 ){
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -471,7 +471,6 @@ static int getNextNode(
|
||||
assert( nKey==4 );
|
||||
if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
|
||||
nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
|
||||
if( nNear>=1000000000 ) nNear = 1000000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-10
@@ -132,9 +132,10 @@ struct StrBuffer {
|
||||
/*
|
||||
** Allocate a two-slot MatchinfoBuffer object.
|
||||
*/
|
||||
static MatchinfoBuffer *fts3MIBufferNew(i64 nElem, const char *zMatchinfo){
|
||||
static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
|
||||
MatchinfoBuffer *pRet;
|
||||
sqlite3_int64 nByte = sizeof(u32) * (2*(i64)nElem+1) + SZ_MATCHINFOBUFFER(1);
|
||||
sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
|
||||
+ SZ_MATCHINFOBUFFER(1);
|
||||
sqlite3_int64 nStr = strlen(zMatchinfo);
|
||||
|
||||
pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
|
||||
@@ -886,7 +887,7 @@ static int fts3ExprLHits(
|
||||
if( p->flag==FTS3_MATCHINFO_LHITS ){
|
||||
p->aMatchinfo[iStart + iCol] = (u32)nHit;
|
||||
}else if( nHit ){
|
||||
p->aMatchinfo[iStart + iCol/32] |= (1U << (iCol&0x1F));
|
||||
p->aMatchinfo[iStart + (iCol+1)/32] |= (1 << (iCol&0x1F));
|
||||
}
|
||||
}
|
||||
assert( *pIter==0x00 || *pIter==0x01 );
|
||||
@@ -1005,8 +1006,8 @@ static int fts3MatchinfoCheck(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static i64 fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
|
||||
i64 nVal; /* Number of integers output by cArg */
|
||||
static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
|
||||
size_t nVal; /* Number of integers output by cArg */
|
||||
|
||||
switch( cArg ){
|
||||
case FTS3_MATCHINFO_NDOC:
|
||||
@@ -1022,16 +1023,16 @@ static i64 fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
|
||||
break;
|
||||
|
||||
case FTS3_MATCHINFO_LHITS:
|
||||
nVal = (i64)pInfo->nCol * pInfo->nPhrase;
|
||||
nVal = (size_t)pInfo->nCol * pInfo->nPhrase;
|
||||
break;
|
||||
|
||||
case FTS3_MATCHINFO_LHITS_BM:
|
||||
nVal = (i64)pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
|
||||
nVal = (size_t)pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( cArg==FTS3_MATCHINFO_HITS );
|
||||
nVal = (i64)pInfo->nCol * pInfo->nPhrase * 3;
|
||||
nVal = (size_t)pInfo->nCol * pInfo->nPhrase * 3;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1313,7 +1314,7 @@ static int fts3MatchinfoValues(
|
||||
|
||||
case FTS3_MATCHINFO_LHITS_BM:
|
||||
case FTS3_MATCHINFO_LHITS: {
|
||||
i64 nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
|
||||
size_t nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
|
||||
memset(pInfo->aMatchinfo, 0, nZero);
|
||||
rc = fts3ExprLHitGather(pCsr->pExpr, pInfo);
|
||||
break;
|
||||
@@ -1382,7 +1383,7 @@ static void fts3GetMatchinfo(
|
||||
** initialize those elements that are constant for every row.
|
||||
*/
|
||||
if( pCsr->pMIBuffer==0 ){
|
||||
i64 nMatchinfo = 0; /* Number of u32 elements in match-info */
|
||||
size_t nMatchinfo = 0; /* Number of u32 elements in match-info */
|
||||
int i; /* Used to iterate through zArg */
|
||||
|
||||
/* Determine the number of phrases in the query */
|
||||
|
||||
+11
-37
@@ -98,9 +98,9 @@ typedef struct SegmentWriter SegmentWriter;
|
||||
** incrementally. See function fts3PendingListAppend() for details.
|
||||
*/
|
||||
struct PendingList {
|
||||
sqlite3_int64 nData;
|
||||
int nData;
|
||||
char *aData;
|
||||
sqlite3_int64 nSpace;
|
||||
int nSpace;
|
||||
sqlite3_int64 iLastDocid;
|
||||
sqlite3_int64 iLastCol;
|
||||
sqlite3_int64 iLastPos;
|
||||
@@ -273,24 +273,6 @@ struct SegmentNode {
|
||||
#define SQL_UPDATE_LEVEL_IDX 38
|
||||
#define SQL_UPDATE_LEVEL 39
|
||||
|
||||
/*
|
||||
** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL
|
||||
** is always set.
|
||||
*/
|
||||
int sqlite3Fts3PrepareStmt(
|
||||
Fts3Table *p, /* Prepare for this connection */
|
||||
const char *zSql, /* SQL to prepare */
|
||||
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
|
||||
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
|
||||
sqlite3_stmt **pp /* OUT: Prepared statement */
|
||||
){
|
||||
int f = SQLITE_PREPARE_FROM_DDL
|
||||
|((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0)
|
||||
|(bPersist ? SQLITE_PREPARE_PERSISTENT : 0);
|
||||
|
||||
return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to obtain an SQLite prepared statement handle
|
||||
** for the statement identified by the second argument. If successful,
|
||||
@@ -416,12 +398,12 @@ static int fts3SqlStmt(
|
||||
|
||||
pStmt = p->aStmt[eStmt];
|
||||
if( !pStmt ){
|
||||
int bAllowVtab = 0;
|
||||
int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
|
||||
char *zSql;
|
||||
if( eStmt==SQL_CONTENT_INSERT ){
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
|
||||
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
|
||||
bAllowVtab = 1;
|
||||
f &= ~SQLITE_PREPARE_NO_VTAB;
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
|
||||
@@ -429,7 +411,7 @@ static int fts3SqlStmt(
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt);
|
||||
rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL);
|
||||
sqlite3_free(zSql);
|
||||
assert( rc==SQLITE_OK || pStmt==0 );
|
||||
p->aStmt[eStmt] = pStmt;
|
||||
@@ -778,9 +760,7 @@ static int fts3PendingTermsAddOne(
|
||||
|
||||
pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
|
||||
if( pList ){
|
||||
assert( (i64)pList->nData+(i64)nToken+(i64)sizeof(Fts3HashElem)
|
||||
<= (i64)p->nPendingData );
|
||||
p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
}
|
||||
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
|
||||
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
|
||||
@@ -793,9 +773,7 @@ static int fts3PendingTermsAddOne(
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( (i64)p->nPendingData + pList->nData + nToken
|
||||
+ sizeof(Fts3HashElem) <= 0x3fffffff );
|
||||
p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -3129,10 +3107,6 @@ static void fts3ReadEndBlockField(
|
||||
for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
|
||||
iVal = iVal*10 + (zText[i] - '0');
|
||||
}
|
||||
|
||||
/* This if() clause is just to avoid an integer overflow. The record is
|
||||
** corrupt in this case. */
|
||||
if( (i64)iVal==SMALLEST_INT64 ) iMul = 1;
|
||||
*pnByte = ((i64)iVal * (i64)iMul);
|
||||
}
|
||||
}
|
||||
@@ -3600,7 +3574,7 @@ static int fts3DoRebuild(Fts3Table *p){
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
@@ -4359,7 +4333,7 @@ static int fts3IncrmergeLoad(
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
|
||||
pWriter->nLeafEst = (int)(((iEnd - iStart)+1)/FTS_MAX_APPENDABLE_HEIGHT);
|
||||
pWriter->nLeafEst = (int)((iEnd - iStart) + 1)/FTS_MAX_APPENDABLE_HEIGHT;
|
||||
pWriter->iStart = iStart;
|
||||
pWriter->iEnd = iEnd;
|
||||
pWriter->iAbsLevel = iAbsLevel;
|
||||
@@ -5353,7 +5327,7 @@ int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
@@ -5483,7 +5457,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
v = atoi(&zVal[9]);
|
||||
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
|
||||
v = atoi(&zVal[11]);
|
||||
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
|
||||
rc = SQLITE_OK;
|
||||
|
||||
+1
-7
@@ -81,13 +81,7 @@ typedef sqlite3_uint64 u64;
|
||||
# define FLEXARRAY 1
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
/*
|
||||
** Constants for the largest and smallest possible 32-bit signed integers.
|
||||
*/
|
||||
# define LARGEST_INT32 ((int)(0x7fffffff))
|
||||
# define SMALLEST_INT32 ((int)((-1) - LARGEST_INT32))
|
||||
#endif
|
||||
|
||||
/* Truncate very long tokens to this many bytes. Hard limit is
|
||||
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
|
||||
|
||||
+3
-3
@@ -426,7 +426,7 @@ static void fts5SnippetFunction(
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int iCol; /* 1st argument to snippet() */
|
||||
const char *zEllips; /* 4th argument to snippet() */
|
||||
i64 nToken; /* 5th argument to snippet() */
|
||||
int nToken; /* 5th argument to snippet() */
|
||||
int nInst = 0; /* Number of instance matches this row */
|
||||
int i; /* Used to iterate through instances */
|
||||
int nPhrase; /* Number of phrases in query */
|
||||
@@ -451,11 +451,11 @@ static void fts5SnippetFunction(
|
||||
ctx.zClose = fts5ValueToText(apVal[2]);
|
||||
ctx.iRangeEnd = -1;
|
||||
zEllips = fts5ValueToText(apVal[3]);
|
||||
nToken = (int)(MIN( MAX(sqlite3_value_int64(apVal[4]), 0), 64));
|
||||
nToken = sqlite3_value_int(apVal[4]);
|
||||
|
||||
iBestCol = (iCol>=0 ? iCol : 0);
|
||||
nPhrase = pApi->xPhraseCount(pFts);
|
||||
aSeen = sqlite3_malloc64(nPhrase);
|
||||
aSeen = sqlite3_malloc(nPhrase);
|
||||
if( aSeen==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
|
||||
if( nIn<0 ){
|
||||
nIn = (int)strlen(pIn);
|
||||
}
|
||||
zRet = (char*)sqlite3_malloc64((i64)nIn+1);
|
||||
zRet = (char*)sqlite3_malloc(nIn+1);
|
||||
if( zRet ){
|
||||
memcpy(zRet, pIn, nIn);
|
||||
zRet[nIn] = '\0';
|
||||
|
||||
@@ -576,7 +576,7 @@ int sqlite3Fts5ConfigParse(
|
||||
sqlite3_int64 nByte;
|
||||
int bUnindexed = 0; /* True if there are one or more UNINDEXED */
|
||||
|
||||
*ppOut = pRet = (Fts5Config*)sqlite3_malloc64(sizeof(Fts5Config));
|
||||
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
|
||||
if( pRet==0 ) return SQLITE_NOMEM;
|
||||
memset(pRet, 0, sizeof(Fts5Config));
|
||||
pRet->pGlobal = pGlobal;
|
||||
@@ -1123,3 +1123,5 @@ void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ int sqlite3Fts5ExprNew(
|
||||
|
||||
assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 );
|
||||
if( sParse.rc==SQLITE_OK ){
|
||||
*ppNew = pNew = sqlite3_malloc64(sizeof(Fts5Expr));
|
||||
*ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
|
||||
if( pNew==0 ){
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
sqlite3Fts5ParseNodeFree(sParse.pExpr);
|
||||
@@ -466,7 +466,7 @@ int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
|
||||
p2->pRoot = 0;
|
||||
|
||||
if( sParse.rc==SQLITE_OK ){
|
||||
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc64(
|
||||
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
|
||||
p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
|
||||
);
|
||||
if( ap==0 ){
|
||||
@@ -807,7 +807,7 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){
|
||||
i64 iPos = a[i].reader.iPos;
|
||||
Fts5PoslistWriter *pWriter = &a[i].writer;
|
||||
if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){
|
||||
sqlite3Fts5PoslistSafeAppend(a[i].pOut, &pWriter->iPrev, iPos);
|
||||
sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1758,10 +1758,10 @@ static int fts5ParseTokenize(
|
||||
memset(pSyn, 0, (size_t)nByte);
|
||||
pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
|
||||
pSyn->nFullTerm = pSyn->nQueryTerm = nToken;
|
||||
memcpy(pSyn->pTerm, pToken, nToken);
|
||||
if( pCtx->pConfig->bTokendata ){
|
||||
pSyn->nQueryTerm = (int)strlen(pSyn->pTerm);
|
||||
}
|
||||
memcpy(pSyn->pTerm, pToken, nToken);
|
||||
pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
|
||||
pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){
|
||||
int rc = SQLITE_OK;
|
||||
Fts5Hash *pNew;
|
||||
|
||||
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc64(sizeof(Fts5Hash));
|
||||
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash));
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -323,7 +323,7 @@ int sqlite3Fts5HashWrite(
|
||||
** + 5 bytes for the new position offset (32-bit max).
|
||||
*/
|
||||
if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){
|
||||
sqlite3_int64 nNew = (i64)p->nAlloc * 2;
|
||||
sqlite3_int64 nNew = p->nAlloc * 2;
|
||||
Fts5HashEntry *pNew;
|
||||
Fts5HashEntry **pp;
|
||||
pNew = (Fts5HashEntry*)sqlite3_realloc64(p, nNew);
|
||||
|
||||
+54
-76
@@ -922,7 +922,7 @@ static void fts5DataRelease(Fts5Data *pData){
|
||||
static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
|
||||
Fts5Data *pRet = fts5DataRead(p, iRowid);
|
||||
if( pRet ){
|
||||
if( pRet->szLeaf<4 || pRet->szLeaf>pRet->nn ){
|
||||
if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
fts5DataRelease(pRet);
|
||||
pRet = 0;
|
||||
@@ -1166,7 +1166,7 @@ static int fts5StructureDecode(
|
||||
i += fts5GetVarint32(&pData[i], nTotal);
|
||||
if( nTotal<pLvl->nMerge ) rc = FTS5_CORRUPT;
|
||||
pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc,
|
||||
(i64)nTotal * sizeof(Fts5StructureSegment)
|
||||
nTotal * sizeof(Fts5StructureSegment)
|
||||
);
|
||||
nSegment -= nTotal;
|
||||
}
|
||||
@@ -1695,7 +1695,6 @@ static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){
|
||||
pLvl->bEof = 1;
|
||||
}else{
|
||||
u8 *a = pLvl->pData->p;
|
||||
int nn = pLvl->pData->nn;
|
||||
|
||||
pLvl->iOff = 0;
|
||||
fts5DlidxLvlNext(pLvl);
|
||||
@@ -1704,7 +1703,7 @@ static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){
|
||||
int ii = pLvl->iOff;
|
||||
u64 delta = 0;
|
||||
|
||||
while( ii<nn && a[ii]==0 ){
|
||||
while( a[ii]==0 ){
|
||||
nZero++;
|
||||
ii++;
|
||||
}
|
||||
@@ -2075,7 +2074,6 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
while( 1 ){
|
||||
u64 iDelta = 0;
|
||||
|
||||
if( i>=n ) break;
|
||||
if( eDetail==FTS5_DETAIL_NONE ){
|
||||
/* todo */
|
||||
if( i<n && a[i]==0 ){
|
||||
@@ -2094,7 +2092,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
|
||||
/* If necessary, grow the pIter->aRowidOffset[] array. */
|
||||
if( iRowidOffset>=pIter->nRowidOffset ){
|
||||
i64 nNew = pIter->nRowidOffset + 8;
|
||||
int nNew = pIter->nRowidOffset + 8;
|
||||
int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int));
|
||||
if( aNew==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
@@ -2123,7 +2121,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
while( p->rc==SQLITE_OK && pIter->iLeafPgno>pIter->iTermLeafPgno ){
|
||||
Fts5Data *pNew;
|
||||
pIter->iLeafPgno--;
|
||||
pNew = fts5LeafRead(p, FTS5_SEGMENT_ROWID(
|
||||
pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID(
|
||||
pIter->pSeg->iSegid, pIter->iLeafPgno
|
||||
));
|
||||
if( pNew ){
|
||||
@@ -2577,10 +2575,6 @@ static void fts5LeafSeek(
|
||||
if( nKeep<nMatch ){
|
||||
goto search_failed;
|
||||
}
|
||||
if( (iOff+nNew)>n ){
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
|
||||
assert( nKeep>=nMatch );
|
||||
if( nKeep==nMatch ){
|
||||
@@ -3557,7 +3551,8 @@ static void fts5PoslistFilterCallback(
|
||||
|
||||
do {
|
||||
while( i<nChunk && pChunk[i]!=0x01 ){
|
||||
fts5IndexSkipVarint(pChunk, i);
|
||||
while( pChunk[i] & 0x80 ) i++;
|
||||
i++;
|
||||
}
|
||||
if( pCtx->eState ){
|
||||
fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
|
||||
@@ -3706,7 +3701,7 @@ static void fts5IndexExtractColset(
|
||||
/* Advance pointer p until it points to pEnd or an 0x01 byte that is
|
||||
** not part of a varint */
|
||||
while( p<pEnd && *p!=0x01 ){
|
||||
while( p<pEnd && (*p++ & 0x80) );
|
||||
while( *p++ & 0x80 );
|
||||
}
|
||||
|
||||
if( pColset->aiCol[i]==iCurrent ){
|
||||
@@ -3803,11 +3798,8 @@ static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){
|
||||
|
||||
assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
|
||||
assert( pIter->pColset );
|
||||
assert( pIter->poslist.nSpace>=pIter->pIndex->pConfig->nCol );
|
||||
|
||||
if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf
|
||||
|| pSeg->nPos>pIter->pIndex->pConfig->nCol
|
||||
){
|
||||
if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf ){
|
||||
fts5IterSetOutputs_Col(pIter, pSeg);
|
||||
}else{
|
||||
u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset];
|
||||
@@ -5247,7 +5239,7 @@ static void fts5DoSecureDelete(
|
||||
int iSegid = pSeg->pSeg->iSegid;
|
||||
u8 *aPg = pSeg->pLeaf->p;
|
||||
int nPg = pSeg->pLeaf->nn;
|
||||
int iPgIdx = pSeg->pLeaf->szLeaf; /* Offset of page footer */
|
||||
int iPgIdx = pSeg->pLeaf->szLeaf;
|
||||
|
||||
u64 iDelta = 0;
|
||||
int iNextOff = 0;
|
||||
@@ -5301,11 +5293,6 @@ static void fts5DoSecureDelete(
|
||||
}else{
|
||||
iStart = fts5GetU16(&aPg[0]);
|
||||
}
|
||||
if( iStart>nPg ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
sqlite3_free(aIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
|
||||
assert_nc( iSOP<=pSeg->iLeafOffset );
|
||||
@@ -5331,7 +5318,7 @@ static void fts5DoSecureDelete(
|
||||
iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
|
||||
}
|
||||
assert_nc( iSOP==pSeg->iLeafOffset );
|
||||
iNextOff = iSOP + pSeg->nPos;
|
||||
iNextOff = pSeg->iLeafOffset + pSeg->nPos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5411,31 +5398,31 @@ static void fts5DoSecureDelete(
|
||||
** is another term following it on this page. So the subsequent term
|
||||
** needs to be moved to replace the term associated with the entry
|
||||
** being removed. */
|
||||
u64 nPrefix = 0;
|
||||
u64 nSuffix = 0;
|
||||
u64 nPrefix2 = 0;
|
||||
u64 nSuffix2 = 0;
|
||||
int nPrefix = 0;
|
||||
int nSuffix = 0;
|
||||
int nPrefix2 = 0;
|
||||
int nSuffix2 = 0;
|
||||
|
||||
iDelKeyOff = iNextOff;
|
||||
iNextOff += fts5GetVarint(&aPg[iNextOff], &nPrefix2);
|
||||
iNextOff += fts5GetVarint(&aPg[iNextOff], &nSuffix2);
|
||||
iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2);
|
||||
iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2);
|
||||
|
||||
if( iKey!=1 ){
|
||||
iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nPrefix);
|
||||
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix);
|
||||
}
|
||||
iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nSuffix);
|
||||
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix);
|
||||
|
||||
nPrefix = MIN(nPrefix, nPrefix2);
|
||||
nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
|
||||
|
||||
if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){
|
||||
if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
if( iKey!=1 ){
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
|
||||
}
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
|
||||
if( nPrefix2>(u64)pSeg->term.n ){
|
||||
if( nPrefix2>pSeg->term.n ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else if( nPrefix2>nPrefix ){
|
||||
memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
|
||||
@@ -5466,7 +5453,7 @@ static void fts5DoSecureDelete(
|
||||
u8 *aTermIdx = &pTerm->p[pTerm->szLeaf];
|
||||
int nTermIdx = pTerm->nn - pTerm->szLeaf;
|
||||
int iTermIdx = 0;
|
||||
i64 iTermOff = 0;
|
||||
int iTermOff = 0;
|
||||
|
||||
while( 1 ){
|
||||
u32 iVal = 0;
|
||||
@@ -5477,15 +5464,12 @@ static void fts5DoSecureDelete(
|
||||
}
|
||||
nTermIdx = iTermIdx;
|
||||
|
||||
if( iTermOff>pTerm->szLeaf ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
|
||||
fts5PutU16(&pTerm->p[2], iTermOff);
|
||||
fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
|
||||
if( nTermIdx==0 ){
|
||||
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
|
||||
}
|
||||
memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
|
||||
fts5PutU16(&pTerm->p[2], iTermOff);
|
||||
|
||||
fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
|
||||
if( nTermIdx==0 ){
|
||||
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
|
||||
}
|
||||
}
|
||||
fts5DataRelease(pTerm);
|
||||
@@ -5508,9 +5492,7 @@ static void fts5DoSecureDelete(
|
||||
int iPrevKeyOut = 0;
|
||||
int iKeyIn = 0;
|
||||
|
||||
if( nMove>0 ){
|
||||
memmove(&aPg[iOff], &aPg[iNextOff], nMove);
|
||||
}
|
||||
memmove(&aPg[iOff], &aPg[iNextOff], nMove);
|
||||
iPgIdx -= nShift;
|
||||
nPg = iPgIdx;
|
||||
fts5PutU16(&aPg[2], iPgIdx);
|
||||
@@ -5948,7 +5930,7 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
|
||||
fts5StructureRelease(pStruct);
|
||||
pStruct = pNew;
|
||||
nMin = 1;
|
||||
nMerge = (nMerge==SMALLEST_INT32 ? LARGEST_INT32 : (nMerge*-1));
|
||||
nMerge = nMerge*-1;
|
||||
}
|
||||
if( pStruct && pStruct->nLevel ){
|
||||
if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){
|
||||
@@ -6430,16 +6412,16 @@ struct Fts5TokenDataMap {
|
||||
** aMap[] variables.
|
||||
*/
|
||||
struct Fts5TokenDataIter {
|
||||
i64 nMapAlloc; /* Allocated size of aMap[] in entries */
|
||||
i64 nMap; /* Number of valid entries in aMap[] */
|
||||
int nMapAlloc; /* Allocated size of aMap[] in entries */
|
||||
int nMap; /* Number of valid entries in aMap[] */
|
||||
Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
|
||||
|
||||
/* The following are used for prefix-queries only. */
|
||||
Fts5Buffer terms;
|
||||
|
||||
/* The following are used for other full-token tokendata queries only. */
|
||||
i64 nIter;
|
||||
i64 nIterAlloc;
|
||||
int nIter;
|
||||
int nIterAlloc;
|
||||
Fts5PoslistReader *aPoslistReader;
|
||||
int *aPoslistToIter;
|
||||
Fts5Iter *apIter[FLEXARRAY];
|
||||
@@ -6495,11 +6477,11 @@ static void fts5TokendataIterAppendMap(
|
||||
){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( pT->nMap==pT->nMapAlloc ){
|
||||
i64 nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
|
||||
i64 nAlloc = nNew * sizeof(Fts5TokenDataMap);
|
||||
int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
|
||||
int nAlloc = nNew * sizeof(Fts5TokenDataMap);
|
||||
Fts5TokenDataMap *aNew;
|
||||
|
||||
aNew = (Fts5TokenDataMap*)sqlite3_realloc64(pT->aMap, nAlloc);
|
||||
aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
|
||||
if( aNew==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
return;
|
||||
@@ -6525,7 +6507,7 @@ static void fts5TokendataIterAppendMap(
|
||||
*/
|
||||
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
|
||||
Fts5TokenDataMap *aTmp = 0;
|
||||
i64 nByte = pT->nMap * sizeof(Fts5TokenDataMap);
|
||||
int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
|
||||
|
||||
aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
|
||||
if( aTmp ){
|
||||
@@ -7059,10 +7041,9 @@ static Fts5TokenDataIter *fts5AppendTokendataIter(
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
|
||||
i64 nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
|
||||
i64 nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
|
||||
Fts5TokenDataIter *pNew;
|
||||
pNew = (Fts5TokenDataIter*)sqlite3_realloc64(pIn, nByte);
|
||||
int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
|
||||
int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
|
||||
Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
|
||||
|
||||
if( pNew==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
@@ -7159,8 +7140,8 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
|
||||
|
||||
/* Ensure the token-mapping is large enough */
|
||||
if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
|
||||
i64 nNew = (pT->nMapAlloc + nByte) * 2;
|
||||
Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc64(
|
||||
int nNew = (pT->nMapAlloc + nByte) * 2;
|
||||
Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
|
||||
pT->aMap, nNew*sizeof(Fts5TokenDataMap)
|
||||
);
|
||||
if( aNew==0 ){
|
||||
@@ -7991,8 +7972,8 @@ static void fts5IndexTombstoneRebuild(
|
||||
){
|
||||
const int MINSLOT = 32;
|
||||
int nSlotPerPage = MAX(MINSLOT, (p->pConfig->pgsz - 8) / szKey);
|
||||
i64 nSlot = 0; /* Number of slots in each output page */
|
||||
i64 nOut = 0;
|
||||
int nSlot = 0; /* Number of slots in each output page */
|
||||
int nOut = 0;
|
||||
|
||||
/* Figure out how many output pages (nOut) and how many slots per
|
||||
** page (nSlot). There are three possibilities:
|
||||
@@ -8017,26 +7998,23 @@ static void fts5IndexTombstoneRebuild(
|
||||
nSlot = MINSLOT;
|
||||
}else if( pSeg->nPgTombstone==1 ){
|
||||
/* Case 2. */
|
||||
u32 nElem = fts5GetU32(&pData1->p[4]);
|
||||
int nElem = (int)fts5GetU32(&pData1->p[4]);
|
||||
assert( pData1 && iPg1==0 );
|
||||
if( nElem>((u32)nSlotPerPage/4) ){
|
||||
nOut = 0;
|
||||
}else{
|
||||
nOut = 1;
|
||||
nSlot = MAX((i64)nElem*4, MINSLOT);
|
||||
}
|
||||
nOut = 1;
|
||||
nSlot = MAX(nElem*4, MINSLOT);
|
||||
if( nSlot>nSlotPerPage ) nOut = 0;
|
||||
}
|
||||
if( nOut==0 ){
|
||||
/* Case 3. */
|
||||
nOut = ((i64)pSeg->nPgTombstone * 2 + 1);
|
||||
nOut = (pSeg->nPgTombstone * 2 + 1);
|
||||
nSlot = nSlotPerPage;
|
||||
}
|
||||
|
||||
/* Allocate the required array and output pages */
|
||||
while( 1 ){
|
||||
int res = 0;
|
||||
i64 ii = 0;
|
||||
i64 szPage = 0;
|
||||
int ii = 0;
|
||||
int szPage = 0;
|
||||
Fts5Data **apOut = 0;
|
||||
|
||||
/* Allocate space for the new hash table */
|
||||
@@ -8574,7 +8552,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
/* Check any rowid-less pages that occur before the current leaf. */
|
||||
for(iPg=iPrevLeaf+1; iPg<fts5DlidxIterPgno(pDlidx); iPg++){
|
||||
iKey = FTS5_SEGMENT_ROWID(iSegid, iPg);
|
||||
pLeaf = fts5LeafRead(p, iKey);
|
||||
pLeaf = fts5DataRead(p, iKey);
|
||||
if( pLeaf ){
|
||||
if( fts5LeafFirstRowidOff(pLeaf)!=0 ) FTS5_CORRUPT_ROWID(p, iKey);
|
||||
fts5DataRelease(pLeaf);
|
||||
@@ -8585,7 +8563,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
/* Check that the leaf page indicated by the iterator really does
|
||||
** contain the rowid suggested by the same. */
|
||||
iKey = FTS5_SEGMENT_ROWID(iSegid, iPrevLeaf);
|
||||
pLeaf = fts5LeafRead(p, iKey);
|
||||
pLeaf = fts5DataRead(p, iKey);
|
||||
if( pLeaf ){
|
||||
i64 iRowid;
|
||||
int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
|
||||
|
||||
+22
-48
@@ -517,7 +517,7 @@ static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
|
||||
if( sqlite3_libversion_number()>=3008002 )
|
||||
#endif
|
||||
{
|
||||
pIdxInfo->estimatedRows = MAX(1, nRow);
|
||||
pIdxInfo->estimatedRows = nRow;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -586,30 +586,19 @@ static int fts5UsePatternMatch(
|
||||
** a) If a MATCH operator is present, the cost depends on the other
|
||||
** constraints also present. As follows:
|
||||
**
|
||||
** * No other constraints: cost=50000.0
|
||||
** * One rowid range constraint: cost=37500.0
|
||||
** * Both rowid range constraints: cost=30000.0
|
||||
** * An == rowid constraint: cost=25000.0
|
||||
** * No other constraints: cost=1000.0
|
||||
** * One rowid range constraint: cost=750.0
|
||||
** * Both rowid range constraints: cost=500.0
|
||||
** * An == rowid constraint: cost=100.0
|
||||
**
|
||||
** b) Otherwise, if there is no MATCH:
|
||||
**
|
||||
** * No other constraints: cost=3000000.0
|
||||
** * One rowid range constraints: cost=2250000.0
|
||||
** * Both rowid range constraint: cost=750000.0
|
||||
** * An == rowid constraint: cost=25.0
|
||||
** * No other constraints: cost=1000000.0
|
||||
** * One rowid range constraint: cost=750000.0
|
||||
** * Both rowid range constraints: cost=250000.0
|
||||
** * An == rowid constraint: cost=10.0
|
||||
**
|
||||
** Costs are not modified by the ORDER BY clause.
|
||||
**
|
||||
** The ratios used in case (a) are based on informal results obtained from
|
||||
** the tool/fts5cost.tcl script. The "MATCH and ==" combination has the
|
||||
** cost set quite high because the query may be a prefix query. Unless
|
||||
** there is a prefix index, prefix queries with rowid constraints are much
|
||||
** more expensive than non-prefix queries with rowid constraints.
|
||||
**
|
||||
** The estimated rows returned is set to the cost/40. For simple queries,
|
||||
** experimental results show that cost/4 might be about right. But for
|
||||
** more complex queries that use multiple terms the number of rows might
|
||||
** be far fewer than this. So we compromise and use cost/40.
|
||||
*/
|
||||
static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
Fts5Table *pTab = (Fts5Table*)pVTab;
|
||||
@@ -642,7 +631,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
idxStr = (char*)sqlite3_malloc64((i64)pInfo->nConstraint * 8 + 1);
|
||||
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
|
||||
if( idxStr==0 ) return SQLITE_NOMEM;
|
||||
pInfo->idxStr = idxStr;
|
||||
pInfo->needToFreeIdxStr = 1;
|
||||
@@ -735,35 +724,21 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
|
||||
/* Calculate the estimated cost based on the flags set in idxFlags. */
|
||||
if( bSeenEq ){
|
||||
pInfo->estimatedCost = nSeenMatch ? 25000.0 : 25.0;
|
||||
fts5SetEstimatedRows(pInfo, 1);
|
||||
pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0;
|
||||
fts5SetUniqueFlag(pInfo);
|
||||
fts5SetEstimatedRows(pInfo, 1);
|
||||
}else{
|
||||
i64 nEstRows;
|
||||
if( nSeenMatch ){
|
||||
if( bSeenLt && bSeenGt ){
|
||||
pInfo->estimatedCost = 50000.0;
|
||||
}else if( bSeenLt || bSeenGt ){
|
||||
pInfo->estimatedCost = 37500.0;
|
||||
}else{
|
||||
pInfo->estimatedCost = 50000.0;
|
||||
}
|
||||
nEstRows = (i64)(pInfo->estimatedCost / 40.0);
|
||||
for(i=1; i<nSeenMatch; i++){
|
||||
pInfo->estimatedCost *= 2.5;
|
||||
nEstRows = nEstRows / 2;
|
||||
}
|
||||
if( bSeenLt && bSeenGt ){
|
||||
pInfo->estimatedCost = nSeenMatch ? 5000.0 : 750000.0;
|
||||
}else if( bSeenLt || bSeenGt ){
|
||||
pInfo->estimatedCost = nSeenMatch ? 7500.0 : 2250000.0;
|
||||
}else{
|
||||
if( bSeenLt && bSeenGt ){
|
||||
pInfo->estimatedCost = 750000.0;
|
||||
}else if( bSeenLt || bSeenGt ){
|
||||
pInfo->estimatedCost = 2250000.0;
|
||||
}else{
|
||||
pInfo->estimatedCost = 3000000.0;
|
||||
}
|
||||
nEstRows = (i64)(pInfo->estimatedCost / 4.0);
|
||||
pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0;
|
||||
}
|
||||
fts5SetEstimatedRows(pInfo, nEstRows);
|
||||
for(i=1; i<nSeenMatch; i++){
|
||||
pInfo->estimatedCost *= 0.4;
|
||||
}
|
||||
fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0));
|
||||
}
|
||||
|
||||
pInfo->idxNum = idxFlags;
|
||||
@@ -2106,7 +2081,6 @@ static int fts5UpdateMethod(
|
||||
}
|
||||
|
||||
update_out:
|
||||
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
|
||||
pTab->p.pConfig->pzErrmsg = 0;
|
||||
return rc;
|
||||
}
|
||||
@@ -3788,7 +3762,7 @@ static int fts5Init(sqlite3 *db){
|
||||
int rc;
|
||||
Fts5Global *pGlobal = 0;
|
||||
|
||||
pGlobal = (Fts5Global*)sqlite3_malloc64(sizeof(Fts5Global));
|
||||
pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
|
||||
if( pGlobal==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
+23
-20
@@ -365,31 +365,34 @@ int sqlite3Fts5StorageOpen(
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL
|
||||
|| pConfig->eContent==FTS5_CONTENT_UNINDEXED
|
||||
){
|
||||
int i = 0;
|
||||
char *zDefn = 0;
|
||||
sqlite3_str *pDefn = sqlite3_str_new(pConfig->db);
|
||||
|
||||
sqlite3_str_appendf(pDefn, "id INTEGER PRIMARY KEY");
|
||||
for(i=0; i<pConfig->nCol; i++){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->abUnindexed[i] ){
|
||||
sqlite3_str_appendf(pDefn, ", c%d", i);
|
||||
}
|
||||
}
|
||||
if( pConfig->bLocale ){
|
||||
int nDefn = 32 + pConfig->nCol*10;
|
||||
char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20);
|
||||
if( zDefn==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
int i;
|
||||
int iOff;
|
||||
sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY");
|
||||
iOff = (int)strlen(zDefn);
|
||||
for(i=0; i<pConfig->nCol; i++){
|
||||
if( pConfig->abUnindexed[i]==0 ){
|
||||
sqlite3_str_appendf(pDefn, ", l%d", i);
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL
|
||||
|| pConfig->abUnindexed[i]
|
||||
){
|
||||
sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
|
||||
iOff += (int)strlen(&zDefn[iOff]);
|
||||
}
|
||||
}
|
||||
if( pConfig->bLocale ){
|
||||
for(i=0; i<pConfig->nCol; i++){
|
||||
if( pConfig->abUnindexed[i]==0 ){
|
||||
sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i);
|
||||
iOff += (int)strlen(&zDefn[iOff]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
zDefn = sqlite3_str_finish(pDefn);
|
||||
|
||||
if( zDefn ){
|
||||
rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
|
||||
sqlite3_free(zDefn);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3_free(zDefn);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize ){
|
||||
|
||||
+4
-4
@@ -391,7 +391,7 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
break;
|
||||
}
|
||||
CASE(12, "xSetAuxdata") {
|
||||
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc64(sizeof(F5tAuxData));
|
||||
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
|
||||
if( pData==0 ){
|
||||
Tcl_AppendResult(interp, "out of memory", (char*)0);
|
||||
return TCL_ERROR;
|
||||
@@ -780,7 +780,7 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
}
|
||||
|
||||
if( nText>0 ){
|
||||
pCopy = sqlite3_malloc64(nText);
|
||||
pCopy = sqlite3_malloc(nText);
|
||||
if( pCopy==0 ){
|
||||
tokenizer.xDelete(pTok);
|
||||
Tcl_AppendResult(interp, "error in sqlite3_malloc()", (char*)0);
|
||||
@@ -1420,7 +1420,7 @@ static int f5tOrigintextCreate(
|
||||
void *pTokCtx = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
pTok = (OriginTextTokenizer*)sqlite3_malloc64(sizeof(OriginTextTokenizer));
|
||||
pTok = (OriginTextTokenizer*)sqlite3_malloc(sizeof(OriginTextTokenizer));
|
||||
if( pTok==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else if( nArg<1 ){
|
||||
@@ -1480,7 +1480,7 @@ static int xOriginToken(
|
||||
int nReq = nToken + 1 + (iEnd-iStart);
|
||||
if( nReq>p->nBuf ){
|
||||
sqlite3_free(p->aBuf);
|
||||
p->aBuf = sqlite3_malloc64(nReq*2);
|
||||
p->aBuf = sqlite3_malloc(nReq*2);
|
||||
if( p->aBuf==0 ) return SQLITE_NOMEM;
|
||||
p->nBuf = nReq*2;
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ static int fts5tokConnectMethod(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (Fts5tokTable*)sqlite3_malloc64(sizeof(Fts5tokTable));
|
||||
pTab = (Fts5tokTable*)sqlite3_malloc(sizeof(Fts5tokTable));
|
||||
if( pTab==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -275,7 +275,7 @@ static int fts5tokBestIndexMethod(
|
||||
static int fts5tokOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
|
||||
Fts5tokCursor *pCsr;
|
||||
|
||||
pCsr = (Fts5tokCursor *)sqlite3_malloc64(sizeof(Fts5tokCursor));
|
||||
pCsr = (Fts5tokCursor *)sqlite3_malloc(sizeof(Fts5tokCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@@ -347,7 +347,7 @@ static int fts5tokCb(
|
||||
if( pCsr->nRow ){
|
||||
pRow->iPos = pRow[-1].iPos + ((tflags & FTS5_TOKEN_COLOCATED) ? 0 : 1);
|
||||
}
|
||||
pRow->zToken = sqlite3_malloc64((sqlite3_int64)nToken+1);
|
||||
pRow->zToken = sqlite3_malloc(nToken+1);
|
||||
if( pRow->zToken==0 ) return SQLITE_NOMEM;
|
||||
memcpy(pRow->zToken, pToken, nToken);
|
||||
pRow->zToken[nToken] = 0;
|
||||
@@ -373,8 +373,8 @@ static int fts5tokFilterMethod(
|
||||
fts5tokResetCursor(pCsr);
|
||||
if( idxNum==1 ){
|
||||
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
|
||||
sqlite3_int64 nByte = sqlite3_value_bytes(apVal[0]);
|
||||
pCsr->zInput = sqlite3_malloc64(nByte+1);
|
||||
int nByte = sqlite3_value_bytes(apVal[0]);
|
||||
pCsr->zInput = sqlite3_malloc(nByte+1);
|
||||
if( pCsr->zInput==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
@@ -72,7 +72,7 @@ static int fts5AsciiCreate(
|
||||
if( nArg%2 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p = sqlite3_malloc64(sizeof(AsciiTokenizer));
|
||||
p = sqlite3_malloc(sizeof(AsciiTokenizer));
|
||||
if( p==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -367,7 +367,7 @@ static int fts5UnicodeCreate(
|
||||
if( nArg%2 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p = (Unicode61Tokenizer*)sqlite3_malloc64(sizeof(Unicode61Tokenizer));
|
||||
p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
|
||||
if( p ){
|
||||
const char *zCat = "L* N* Co";
|
||||
int i;
|
||||
@@ -586,17 +586,11 @@ static int fts5PorterCreate(
|
||||
const char *zBase = "unicode61";
|
||||
fts5_tokenizer_v2 *pV2 = 0;
|
||||
|
||||
while( nArg>0 ){
|
||||
if( sqlite3_stricmp(azArg[0],"porter")==0 ){
|
||||
nArg--;
|
||||
azArg++;
|
||||
}else{
|
||||
zBase = azArg[0];
|
||||
break;
|
||||
}
|
||||
if( nArg>0 ){
|
||||
zBase = azArg[0];
|
||||
}
|
||||
|
||||
pRet = (PorterTokenizer*)sqlite3_malloc64(sizeof(PorterTokenizer));
|
||||
pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
|
||||
if( pRet ){
|
||||
memset(pRet, 0, sizeof(PorterTokenizer));
|
||||
rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2);
|
||||
@@ -718,9 +712,9 @@ static int fts5Porter_Ostar(char *zStem, int nStem){
|
||||
for(i=0; i<nStem; i++){
|
||||
bCons = !fts5PorterIsVowel(zStem[i], bCons);
|
||||
assert( bCons==0 || bCons==1 );
|
||||
mask = ((mask << 1) + bCons) & 0x0007;
|
||||
mask = (mask << 1) + bCons;
|
||||
}
|
||||
return (mask==0x0005);
|
||||
return ((mask & 0x0007)==0x0005);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1303,7 +1297,7 @@ static int fts5TriCreate(
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
int i;
|
||||
pNew = (TrigramTokenizer*)sqlite3_malloc64(sizeof(*pNew));
|
||||
pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
+1
-12
@@ -396,12 +396,7 @@ static int fts5VocabOpenMethod(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Restore cursor pCsr to the state it was in immediately after being
|
||||
** created by the xOpen() method.
|
||||
*/
|
||||
static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
|
||||
int nCol = pCsr->pFts5->pConfig->nCol;
|
||||
pCsr->rowid = 0;
|
||||
sqlite3Fts5IterClose(pCsr->pIter);
|
||||
sqlite3Fts5StructureRelease(pCsr->pStruct);
|
||||
@@ -411,12 +406,6 @@ static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
|
||||
pCsr->nLeTerm = -1;
|
||||
pCsr->zLeTerm = 0;
|
||||
pCsr->bEof = 0;
|
||||
pCsr->iCol = 0;
|
||||
pCsr->iInstPos = 0;
|
||||
pCsr->iInstOff = 0;
|
||||
pCsr->colUsed = 0;
|
||||
memset(pCsr->aCnt, 0, sizeof(i64)*nCol);
|
||||
memset(pCsr->aDoc, 0, sizeof(i64)*nCol);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -666,7 +655,7 @@ static int fts5VocabFilterMethod(
|
||||
const char *zCopy = (const char *)sqlite3_value_text(pLe);
|
||||
if( zCopy==0 ) zCopy = "";
|
||||
pCsr->nLeTerm = sqlite3_value_bytes(pLe);
|
||||
pCsr->zLeTerm = sqlite3_malloc64((i64)pCsr->nLeTerm+1);
|
||||
pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
|
||||
if( pCsr->zLeTerm==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
@@ -191,29 +191,6 @@ do_execsql_test 5.6 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', 6) FROM p1('x OR ' || x'DB');
|
||||
} {{[x a a a a a...}}
|
||||
|
||||
do_execsql_test 5.7.1 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', -2147483647) FROM p1('x OR ' || x'DB')
|
||||
} {{[x...}}
|
||||
do_execsql_test 5.7.2 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', -2147483648) FROM p1('x OR ' || x'DB')
|
||||
} {{[x...}}
|
||||
do_execsql_test 5.7.3 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', -2147483649) FROM p1('x OR ' || x'DB')
|
||||
} {{[x...}}
|
||||
do_execsql_test 5.7.4 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', 0) FROM p1('x OR ' || x'DB')
|
||||
} {{[x...}}
|
||||
|
||||
do_execsql_test 5.8.1 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', 2147483647) FROM p1('x OR ' || x'DB')
|
||||
} {{[x a a a a a a a a a a}}
|
||||
do_execsql_test 5.8.2 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', 2147483648) FROM p1('x OR ' || x'DB')
|
||||
} {{[x a a a a a a a a a a}}
|
||||
do_execsql_test 5.8.3 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', 2147483649) FROM p1('x OR ' || x'DB')
|
||||
} {{[x a a a a a a a a a a}}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
reset_db
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# 2026 June 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5bigtok2
|
||||
return_if_no_fts5
|
||||
|
||||
set big [string repeat a 1080000000]
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t USING fts5(x);
|
||||
}
|
||||
|
||||
do_catchsql_test 1.1 { INSERT INTO t VALUES($big); } {1 {out of memory}}
|
||||
|
||||
set big {}
|
||||
|
||||
finish_test
|
||||
@@ -15153,9 +15153,9 @@ do_execsql_test 78.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5vocab('t1','col');
|
||||
}
|
||||
|
||||
do_catchsql_test 78.2 {
|
||||
do_execsql_test 78.2 {
|
||||
SELECT count(rowid) FROM t3 WHERE term>='nsocse';
|
||||
} {1 {database disk image is malformed}}
|
||||
} 2
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -16124,367 +16124,5 @@ do_catchsql_test 83.1 {
|
||||
SELECT * FROM t1('R*R*R*R*R*R*R*R*') WHERE (a,b)<=(current_date,0 BETWEEN 'a'<>11 AND '') ORDER BY rowid DESC;
|
||||
} {/.*fts5: corruption found/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 84.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 53248 pagesize 4096 filename c1a.txt.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 0d .....@ ........
|
||||
| 32: 00 00 00 02 00 00 00 01 00 00 00 09 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 0f c7 00 07 0d 92 00 0f 8d 0f 36 ...............6
|
||||
| 112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 0d 92 00 00 00 00 ...k............
|
||||
| 3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
|
||||
| 3488: 32 74 32 0d 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
|
||||
| 3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
|
||||
| 3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
|
||||
| 3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
|
||||
| 3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
|
||||
| 3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
|
||||
| 3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06 WITHOUT ROWID[.
|
||||
| 3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
|
||||
| 3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
|
||||
| 3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
|
||||
| 3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
|
||||
| 3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
|
||||
| 3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21 Y, sz BLOB)^...!
|
||||
| 3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74 !...tablet1_cont
|
||||
| 3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52 entt1_content.CR
|
||||
| 3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 EATE TABLE 't1_c
|
||||
| 3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 ontent'(id INTEG
|
||||
| 3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
|
||||
| 3776: 63 30 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19 c0, c1, c2)i....
|
||||
| 3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 ...-tablet1_idxt
|
||||
| 3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42 1_idx.CREATE TAB
|
||||
| 3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69 LE 't1_idx'(segi
|
||||
| 3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 d, term, pgno, P
|
||||
| 3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 RIMARY KEY(segid
|
||||
| 3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 , term)) WITHOUT
|
||||
| 3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74 ROWIDU........t
|
||||
| 3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61 ablet1_datat1_da
|
||||
| 3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 ta.CREATE TABLE
|
||||
| 3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54 't1_data'(id INT
|
||||
| 3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
|
||||
| 3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06 , block BLOB)8..
|
||||
| 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR
|
||||
| 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB
|
||||
| 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 LE t1 USING fts5
|
||||
| 4032: 28 61 2c 62 2c 63 29 00 00 00 39 00 00 00 00 00 (a,b,c)...9.....
|
||||
| page 3 offset 8192
|
||||
| 0: 05 00 00 00 02 0f f1 00 00 00 00 0c 0f fb 0f f1 ................
|
||||
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0a ........:.^.:.:.
|
||||
| 4080: 0a 00 00 00 0b 84 80 80 80 80 01 00 00 00 0a 0a ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 7 offset 24576
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| page 10 offset 36864
|
||||
| 0: 0d 00 00 00 02 0f e2 00 0f e2 0f ef 00 00 00 00 ................
|
||||
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0f ........:.^.:.:.
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 02 00 01 01 01 09 ...$............
|
||||
| page 11 offset 40960
|
||||
| 0: 0d 00 00 00 01 00 22 00 00 22 00 00 00 00 00 00 ................
|
||||
| 32: 00 00 9f 56 84 80 80 80 80 01 04 00 bf 30 00 00 ...V.........0..
|
||||
| 48: 0f 58 02 30 30 19 02 05 01 02 05 01 02 05 16 02 .X.00...........
|
||||
| 64: 05 01 02 05 01 02 05 61 02 05 01 02 05 01 02 05 .......a........
|
||||
| 80: 13 02 05 01 02 05 01 02 05 0d 02 03 01 02 03 01 ................
|
||||
| 96: 02 03 02 09 78 66 66 66 66 66 66 66 65 81 17 02 ....xfffffffe...
|
||||
| 112: 05 01 02 05 01 02 05 01 01 31 04 02 04 01 02 04 .........1......
|
||||
| 128: 01 02 04 01 02 05 01 02 05 01 02 05 0d 02 06 01 ................
|
||||
| 144: 02 06 01 02 06 02 01 30 79 02 04 01 02 04 01 02 .......0y.......
|
||||
| 160: 04 03 02 30 30 2b 02 05 01 02 05 01 02 05 58 02 ...00+........X.
|
||||
| 176: 05 01 02 05 01 02 05 01 02 05 01 02 05 01 02 05 ................
|
||||
| 192: 16 02 05 01 02 05 01 02 05 05 06 30 30 30 30 30 ...........00000
|
||||
| 208: 30 81 0b 02 04 01 02 04 01 02 04 10 02 05 01 02 0...............
|
||||
| 224: 05 01 02 05 03 02 32 34 76 02 05 01 02 05 01 02 ......24v.......
|
||||
| 240: 05 02 01 38 07 02 04 01 02 04 01 02 04 01 01 32 ...8...........2
|
||||
| 256: 28 02 04 01 02 04 01 02 04 04 02 05 01 02 05 01 (...............
|
||||
| 272: 02 05 02 01 30 1f 02 05 01 02 05 01 02 05 03 02 ....0...........
|
||||
| 288: 30 30 10 02 05 01 02 05 01 02 05 6a 02 04 01 02 00.........j....
|
||||
| 304: 04 01 02 04 02 08 35 30 30 30 30 30 30 30 81 26 ......50000000.&
|
||||
| 320: 02 05 01 02 05 01 02 05 01 01 33 07 02 06 01 02 ..........3.....
|
||||
| 336: 06 01 02 06 81 2c 02 04 01 02 04 01 02 04 02 04 .....,..........
|
||||
| 352: 32 37 36 36 81 23 02 05 01 02 05 01 02 05 01 01 2766.#..........
|
||||
| 368: 34 13 02 05 01 02 05 01 02 05 02 03 30 39 36 1c 4...........096.
|
||||
| 384: 02 05 01 02 05 01 02 05 07 02 05 01 02 05 01 02 ................
|
||||
| 400: 05 01 03 35 30 30 7f 02 05 01 02 05 01 02 05 04 ...500..........
|
||||
| 416: 02 30 30 81 0e 02 06 01 02 06 01 02 06 06 03 30 .00............0
|
||||
| 432: 30 30 81 11 02 04 01 02 04 01 02 04 01 05 36 35 00............65
|
||||
| 448: 35 33 36 81 1a 02 05 01 02 05 01 02 05 01 04 38 536............8
|
||||
| 464: 31 39 32 81 02 02 06 01 02 06 01 02 06 01 05 61 192............a
|
||||
| 480: 6c 6c 6f 77 01 02 02 01 02 02 01 02 02 02 02 72 llow...........r
|
||||
| 496: 67 81 08 02 04 01 02 04 01 02 04 02 05 74 6f 6d g............tom
|
||||
| 512: 69 63 04 02 02 01 02 02 01 02 02 03 06 74 61 63 ic...........tac
|
||||
| 528: 68 65 64 79 02 03 01 02 03 01 02 03 02 0d 75 74 hedy..........ut
|
||||
| 544: 6f 63 68 65 63 6b 70 6f 69 6e 74 2b 02 04 01 02 ocheckpoint+....
|
||||
| 560: 04 01 02 04 05 06 76 61 63 75 75 6d 0d 02 03 01 ......vacuum....
|
||||
| 576: 02 03 01 02 03 01 06 62 69 6e 61 72 79 03 06 01 .......binary...
|
||||
| 592: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 608: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 624: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 640: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 656: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 672: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 688: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 704: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 720: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 736: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 752: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 768: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 784: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 800: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 816: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 832: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 848: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 864: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 880: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 896: 01 02 02 02 07 79 74 65 63 6f 64 65 37 02 03 01 .....ytecode7...
|
||||
| 912: 02 03 01 02 03 01 05 63 61 63 68 65 10 02 03 01 .......cache....
|
||||
| 928: 02 03 01 01 03 02 04 6c 61 6e 67 07 02 03 01 02 .......lang.....
|
||||
| 944: 03 01 02 03 02 05 6f 6c 75 6d 6e 7c 02 03 01 02 ......olumn|....
|
||||
| 960: 03 01 02 03 03 06 6d 6d 65 6e 74 73 43 02 04 01 ......mmentsC...
|
||||
| 976: 02 04 01 02 04 04 05 70 69 6c 65 72 07 02 02 01 .......piler....
|
||||
| 992: 02 02 01 02 02 05 04 6f 75 6e 64 7f 02 03 01 02 .......ound.....
|
||||
| 1008: 03 01 02 03 03 03 75 6e 74 81 17 02 04 01 02 04 ......unt.......
|
||||
| 1024: 01 02 04 02 05 75 72 73 6f 72 3a 02 03 01 02 03 .....ursor:.....
|
||||
| 1040: 01 02 03 01 06 64 62 70 61 67 65 3d 02 03 01 02 .....dbpage=....
|
||||
| 1056: 03 01 02 03 03 04 73 74 61 74 40 02 03 01 02 03 ......stat@.....
|
||||
| 1072: 01 02 03 02 04 65 62 75 67 0a 02 02 01 02 02 01 .....ebug.......
|
||||
| 1088: 02 02 03 05 66 61 75 6c 74 0d 02 02 01 02 02 01 ....fault.......
|
||||
| 1104: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1120: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1136: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1152: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1168: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1184: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1200: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 4f 02 ..............O.
|
||||
| 1216: 03 01 02 03 01 02 03 03 03 70 74 68 81 05 02 04 .........pth....
|
||||
| 1232: 01 02 04 01 02 04 19 02 04 01 02 04 01 02 04 02 ................
|
||||
| 1248: 05 69 72 65 63 74 34 02 02 01 02 02 01 02 02 01 .irect4.........
|
||||
| 1264: 06 65 6e 61 62 6c 65 37 02 02 01 02 02 01 02 02 .enable7........
|
||||
| 1280: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1296: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1312: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1328: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1344: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1360: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1376: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1392: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1408: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1424: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1440: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1456: 02 01 02 02 02 06 78 70 6c 61 69 6e 43 02 03 01 ......xplainC...
|
||||
| 1472: 02 03 01 02 03 04 01 72 81 05 02 03 01 02 03 01 .......r........
|
||||
| 1488: 02 03 03 07 74 65 6e 73 69 6f 6e 81 2f 02 04 01 ....tension./...
|
||||
| 1504: 02 04 01 02 04 01 04 66 69 6c 65 13 02 03 01 02 .......file.....
|
||||
| 1520: 03 01 02 03 02 05 6f 72 6d 61 74 13 02 04 01 02 ......ormat.....
|
||||
| 1536: 04 01 02 04 02 03 74 73 33 46 02 03 01 02 03 01 ......ts3F......
|
||||
| 1552: 02 03 01 02 03 01 02 03 01 02 03 04 01 34 4c 02 .............4L.
|
||||
| 1568: 03 01 02 03 01 02 03 04 01 35 4f 02 03 01 02 03 .........5O.....
|
||||
| 1584: 01 02 03 02 03 75 6e 63 5e 02 05 01 02 05 01 02 .....unc^.......
|
||||
| 1600: 05 05 04 74 69 6f 6e 73 02 05 01 02 05 01 02 05 ...tions........
|
||||
| 1616: 13 02 03 01 02 03 01 02 03 09 01 73 55 02 04 01 ...........sU...
|
||||
| 1632: 02 04 01 02 04 01 07 67 65 6f 70 6f 6c 79 52 02 .......geopolyR.
|
||||
| 1648: 03 01 02 03 01 02 03 01 05 68 69 6e 74 73 3a 02 .........hints:.
|
||||
| 1664: 04 01 02 04 01 02 04 02 03 6f 6f 6b 61 02 04 01 .........ooka...
|
||||
| 1680: 02 04 01 02 04 01 02 69 6e 01 02 04 01 02 04 01 .......in.......
|
||||
| 1696: 02 04 03 04 69 74 73 7a 1f 02 04 01 02 04 01 02 ....itsz........
|
||||
| 1712: 04 03 08 74 72 69 6e 73 69 63 73 04 02 03 01 02 ...trinsics.....
|
||||
| 1728: 03 01 02 03 01 07 6a 6f 75 72 6e 61 6c 16 02 03 ......journal...
|
||||
| 1744: 01 02 03 01 02 03 01 06 6c 65 6e 67 74 68 81 0b ........length..
|
||||
| 1760: 02 03 01 02 03 01 02 03 01 02 05 01 02 05 01 02 ................
|
||||
| 1776: 05 0d 02 04 01 02 04 01 02 04 02 03 69 6b 65 81 ............ike.
|
||||
| 1792: 0e 02 03 01 02 03 01 02 03 03 03 6d 69 74 16 02 ...........mit..
|
||||
| 1808: 05 01 02 05 01 02 05 5e 02 04 01 02 04 01 02 04 .......^........
|
||||
| 1824: 02 03 6f 61 64 81 2f 02 03 01 02 03 01 02 03 01 ..oad./.........
|
||||
| 1840: 06 6d 61 6c 6c 6f 63 76 02 02 01 02 02 01 02 02 .mallocv........
|
||||
| 1856: 3a 02 03 01 02 03 01 02 03 03 02 74 68 55 02 03 :..........thU..
|
||||
| 1872: 01 02 03 01 02 03 03 01 78 79 02 02 01 02 02 01 ........xy......
|
||||
| 1888: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1904: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1920: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1936: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1952: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1968: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1984: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 2000: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 2016: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 2032: 02 02 02 05 65 6d 6f 72 79 81 11 02 03 01 02 03 ....emory.......
|
||||
| 2048: 01 02 03 04 04 73 79 73 35 58 02 03 01 02 03 01 .....sys5X......
|
||||
| 2064: 02 03 02 03 6d 61 70 19 02 03 01 02 03 01 02 03 ....map.........
|
||||
| 2080: 79 02 03 01 02 03 01 02 03 02 04 75 74 65 78 81 y..........utex.
|
||||
| 2096: 2c 02 02 01 02 02 01 02 02 01 06 6e 6f 63 61 73 ,..........nocas
|
||||
| 2112: 65 02 06 01 02 02 03 06 01 02 02 03 06 01 02 02 e...............
|
||||
| 2128: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2144: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2160: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2176: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2192: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2208: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2224: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2240: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2256: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2272: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2288: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2304: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2320: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2336: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2352: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2368: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2384: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2400: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2416: 02 02 03 06 01 02 02 03 07 72 6d 61 6c 69 7a 65 .........rmalize
|
||||
| 2432: 5b 02 03 01 02 03 01 02 03 02 05 75 6d 62 65 72 [..........umber
|
||||
| 2448: 81 23 02 04 01 02 04 01 02 04 01 06 6f 66 66 73 .#..........offs
|
||||
| 2464: 65 74 5e 02 03 01 02 03 01 02 03 02 03 6d 69 74 et^..........mit
|
||||
| 2480: 81 2c 02 03 01 02 03 01 02 03 01 02 02 01 02 02 .,..............
|
||||
| 2496: 01 02 02 02 01 70 81 26 02 04 01 02 04 01 02 04 .....p.&........
|
||||
| 2512: 02 07 76 65 72 66 6c 6f 77 34 02 03 01 02 03 01 ..verflow4......
|
||||
| 2528: 02 03 01 04 70 61 67 65 1c 02 03 01 02 03 01 02 ....page........
|
||||
| 2544: 03 64 02 04 01 02 04 01 02 04 13 02 03 01 02 03 .d..............
|
||||
| 2560: 01 02 03 01 02 03 01 02 03 01 02 03 03 09 72 65 ..............re
|
||||
| 2576: 6e 74 68 65 73 69 73 49 02 04 01 02 04 01 02 04 nthesisI........
|
||||
| 2592: 03 05 74 74 65 72 6e 81 0e 02 04 01 02 04 01 02 ..ttern.........
|
||||
| 2608: 04 02 05 63 61 63 68 65 1f 02 03 01 02 03 01 02 ...cache........
|
||||
| 2624: 03 02 08 72 65 75 70 64 61 74 65 61 02 03 01 02 ...reupdatea....
|
||||
| 2640: 03 01 02 03 01 04 72 65 61 64 34 02 04 01 02 04 ......read4.....
|
||||
| 2656: 01 02 04 03 07 63 75 72 73 69 76 65 22 02 03 01 .....cursive....
|
||||
| 2672: 02 03 01 02 03 02 04 6f 77 69 64 01 02 03 01 02 .......owid.....
|
||||
| 2688: 03 01 02 03 02 04 74 72 65 65 64 02 03 01 02 03 ......treed.....
|
||||
| 2704: 01 02 03 04 02 69 6d 01 06 01 02 02 03 06 01 02 .....im.........
|
||||
| 2720: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2736: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2752: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2768: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2784: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2800: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2816: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2832: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2848: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2864: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2880: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2896: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2912: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2928: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2944: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2960: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2976: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2992: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 3008: 01 02 02 03 06 01 02 02 03 06 01 02 02 01 0a 73 ...............s
|
||||
| 3024: 63 61 6e 73 74 61 74 75 73 70 02 04 01 02 04 01 canstatusp......
|
||||
| 3040: 02 04 02 05 65 63 74 6f 72 25 02 03 01 02 03 01 ....ector%......
|
||||
| 3056: 02 03 03 04 6c 65 63 74 7f 02 04 01 02 04 01 02 ....lect........
|
||||
| 3072: 04 03 05 73 73 69 6f 6e 67 02 03 01 02 03 01 02 ...ssiong.......
|
||||
| 3088: 03 02 03 69 7a 65 10 02 04 01 02 04 01 02 04 04 ...ize..........
|
||||
| 3104: 02 04 01 02 04 01 02 04 01 02 04 01 02 04 01 02 ................
|
||||
| 3120: 04 01 02 04 01 02 04 01 02 04 07 02 04 01 02 04 ................
|
||||
| 3136: 01 02 04 5b 02 05 01 02 05 01 02 05 10 02 04 01 ...[............
|
||||
| 3152: 02 04 01 02 04 04 02 04 01 02 04 01 02 04 02 03 ................
|
||||
| 3168: 6f 66 74 76 02 03 01 02 03 01 02 03 02 02 71 6c oftv..........ql
|
||||
| 3184: 5e 02 04 01 02 04 01 02 04 13 02 04 01 02 04 01 ^...............
|
||||
| 3200: 02 04 28 02 03 01 02 03 01 02 03 02 04 74 61 74 ..(..........tat
|
||||
| 3216: 34 6a 02 03 01 02 03 01 02 03 03 02 6d 74 70 02 4j..........mtp.
|
||||
| 3232: 03 01 02 03 01 02 03 05 04 76 74 61 62 6d 02 03 .........vtabm..
|
||||
| 3248: 01 02 03 01 02 03 03 03 6f 72 65 81 35 02 03 01 ........ore.5...
|
||||
| 3264: 02 03 01 02 03 02 0a 79 6e 63 68 72 6f 6e 6f 75 .......ynchronou
|
||||
| 3280: 73 28 02 03 01 02 03 01 02 03 04 02 04 01 02 04 s(..............
|
||||
| 3296: 01 02 04 03 04 73 74 65 6d 81 32 02 02 01 02 02 .....stem.2.....
|
||||
| 3312: 01 02 02 01 04 74 65 6d 70 81 35 02 02 01 02 02 .....temp.5.....
|
||||
| 3328: 01 02 02 02 06 68 72 65 61 64 73 31 02 04 01 02 .....hreads1....
|
||||
| 3344: 04 01 02 04 76 02 04 01 02 04 01 02 04 08 03 61 ....v..........a
|
||||
| 3360: 66 65 81 38 02 02 01 02 02 01 02 02 02 06 72 69 fe.8..........ri
|
||||
| 3376: 67 67 65 72 81 20 02 03 01 02 03 01 02 03 08 01 gger. ..........
|
||||
| 3392: 73 22 02 04 01 02 04 01 02 04 01 07 75 6e 6b 6e s...........unkn
|
||||
| 3408: 6f 77 6e 73 02 03 01 02 03 01 02 03 01 08 76 61 owns..........va
|
||||
| 3424: 72 69 61 62 6c 65 81 23 02 03 01 02 03 01 02 03 riable.#........
|
||||
| 3440: 02 03 64 62 65 81 26 02 03 01 02 03 01 02 03 02 ..dbe.&.........
|
||||
| 3456: 03 69 65 77 01 02 05 01 02 05 01 02 05 02 03 74 .iew...........t
|
||||
| 3472: 61 62 37 02 04 01 02 04 01 02 04 04 02 04 01 02 ab7.............
|
||||
| 3488: 04 01 02 04 01 02 04 01 02 04 01 02 04 01 03 77 ...............w
|
||||
| 3504: 61 6c 2b 02 03 01 02 03 01 02 03 01 02 03 01 02 al+.............
|
||||
| 3520: 03 01 02 03 02 05 6f 72 6b 65 72 31 02 03 01 02 ......orker1....
|
||||
| 3536: 03 01 02 03 76 02 03 01 02 03 01 02 03 01 01 78 ....v..........x
|
||||
| 3552: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3568: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3584: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3600: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3616: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3632: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3648: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3664: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3680: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3696: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3712: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3728: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3744: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3760: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3776: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3792: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3808: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3824: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3840: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3856: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3872: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3888: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3904: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3920: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3936: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3952: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3968: 06 01 01 02 01 06 04 30 15 1e 0c 28 1b 0d 0c 15 .......0...(....
|
||||
| 3984: 0c 16 14 16 10 0c 17 0e 0e 0f 11 10 10 0e 10 11 ................
|
||||
| 4000: 18 11 82 3e 12 10 0f 10 11 10 0f 0f 10 11 0f 0f ...>............
|
||||
| 4016: 81 05 18 10 81 45 11 0d 13 0f 10 17 0c 0c 0e 18 .....E..........
|
||||
| 4032: 0c 12 10 0e 0d 0f 13 12 24 0f 17 0f 1a 0d 81 1c ........$.......
|
||||
| 4048: 11 0f 17 10 82 3e 12 11 11 18 0d 12 2a 14 11 10 .....>......*...
|
||||
| 4064: 13 0f 12 0f 0f 82 3a 15 10 0f 10 4d 0e 1f 0f 0d ......:....M....
|
||||
| 4080: 0f 0f 1e 10 10 1a 0f 12 0c 12 14 0f 0e 20 17 19 ............. ..
|
||||
| page 12 offset 45056
|
||||
| 0: 0d 00 00 00 01 0d f4 00 0d f4 00 00 00 00 00 00 ................
|
||||
| 3568: 00 00 00 00 84 04 84 80 80 80 80 02 04 00 88 0c ................
|
||||
| 3584: 00 07 02 00 01 01 02 56 06 01 01 02 01 06 01 01 .......V........
|
||||
| 3600: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3616: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3632: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3648: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3664: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3680: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3696: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3712: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3728: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3744: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3760: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3776: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3792: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3808: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3824: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3840: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3856: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3872: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3888: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3904: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3920: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3936: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3952: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3968: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3984: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 4000: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 4016: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 4032: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 4048: 52 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 R...............
|
||||
| 4064: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 4080: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| end c1a.txt.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 84.1 {
|
||||
SELECT * FROM t1('R*R*x') ORDER BY rowid DESC;
|
||||
} {1 {fts5: corruption found reading blob 137438953475 from table "t1"}}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
@@ -1220,7 +1220,7 @@ do_test 8.0 {
|
||||
|
||||
do_catchsql_test 8.1 {
|
||||
SELECT rowid FROM t1('a* NOT ý') ;
|
||||
} {1 {fts5: corruption on page 1, segment 1, table "t1"}}
|
||||
} {0 {1 2 3 4 5 6 7}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -1917,299 +1917,6 @@ do_catchsql_test 11.3 {
|
||||
REPLACE INTO t1(rowid,b,a,rowid) VALUES(x'44023b9eb002d28b0ee90c',1,2,3);
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 12.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename p.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 03 00 00 00 06 .....@ ........
|
||||
| 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 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................
|
||||
| 96: 00 2e 76 89 0d 00 00 00 06 0d ed 00 0f bb 0f 64 ..v............d
|
||||
| 112: 0e f9 0e a2 0e 45 0d ed 00 00 00 00 00 00 00 00 .....E..........
|
||||
| 3552: 00 00 00 00 00 00 00 00 00 00 00 00 00 56 06 06 .............V..
|
||||
| 3568: 17 1f 1f 01 7d 74 61 62 6c 65 66 74 5f 63 6f 6e .....tableft_con
|
||||
| 3584: 66 69 67 66 74 5f 63 6f 6e 66 69 67 06 43 52 45 figft_config.CRE
|
||||
| 3600: 41 54 45 20 54 41 42 4c 45 20 27 66 74 5f 63 6f ATE TABLE 'ft_co
|
||||
| 3616: 6e 66 69 67 27 28 6b 20 50 52 49 4d 41 52 59 20 nfig'(k PRIMARY
|
||||
| 3632: 4b 45 59 2c 20 76 29 20 57 49 54 48 4f 55 54 20 KEY, v) WITHOUT
|
||||
| 3648: 52 4f 57 49 44 5b 05 07 17 21 21 01 81 01 74 61 ROWID[...!!...ta
|
||||
| 3664: 62 6c 65 66 74 5f 64 6f 63 73 69 7a 65 66 74 5f bleft_docsizeft_
|
||||
| 3680: 64 6f 63 73 69 7a 65 05 43 52 45 41 54 45 20 54 docsize.CREATE T
|
||||
| 3696: 41 42 4c 45 20 27 66 74 5f 64 6f 63 73 69 7a 65 ABLE 'ft_docsize
|
||||
| 3712: 27 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 '(id INTEGER PRI
|
||||
| 3728: 4d 41 52 59 20 4b 45 59 2c 20 73 7a 20 42 4c 4f MARY KEY, sz BLO
|
||||
| 3744: 42 29 55 04 06 17 21 21 01 77 74 61 62 6c 65 66 B)U...!!.wtablef
|
||||
| 3760: 74 5f 63 6f 6e 74 65 6e 74 66 74 5f 63 6f 6e 74 t_contentft_cont
|
||||
| 3776: 65 6e 74 04 43 52 45 41 54 45 20 54 41 42 4c 45 ent.CREATE TABLE
|
||||
| 3792: 20 27 66 74 5f 63 6f 6e 74 65 6e 74 27 28 69 64 'ft_content'(id
|
||||
| 3808: 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 INTEGER PRIMARY
|
||||
| 3824: 20 4b 45 59 2c 20 63 30 29 69 03 07 17 19 19 01 KEY, c0)i......
|
||||
| 3840: 81 2d 74 61 62 6c 65 66 74 5f 69 64 78 66 74 5f .-tableft_idxft_
|
||||
| 3856: 69 64 78 03 43 52 45 41 54 45 20 54 41 42 4c 45 idx.CREATE TABLE
|
||||
| 3872: 20 27 66 74 5f 69 64 78 27 28 73 65 67 69 64 2c 'ft_idx'(segid,
|
||||
| 3888: 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 52 49 term, pgno, PRI
|
||||
| 3904: 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 2c 20 MARY KEY(segid,
|
||||
| 3920: 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 20 52 term)) WITHOUT R
|
||||
| 3936: 4f 57 49 44 55 02 07 17 1b 1b 01 81 01 74 61 62 OWIDU........tab
|
||||
| 3952: 6c 65 66 74 5f 64 61 74 61 66 74 5f 64 61 74 61 left_dataft_data
|
||||
| 3968: 02 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 66 .CREATE TABLE 'f
|
||||
| 3984: 74 5f 64 61 74 61 27 28 69 64 20 49 4e 54 45 47 t_data'(id INTEG
|
||||
| 4000: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
|
||||
| 4016: 62 6c 6f 63 6b 20 42 4c 4f 42 29 43 01 06 17 11 block BLOB)C....
|
||||
| 4032: 11 08 75 74 61 62 6c 65 66 74 66 74 43 52 45 41 ..utableftftCREA
|
||||
| 4048: 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 4c 45 TE VIRTUAL TABLE
|
||||
| 4064: 20 66 74 20 55 53 49 4e 47 20 66 74 73 35 28 61 ft USING fts5(a
|
||||
| 4080: 2c 20 64 65 74 61 69 6c 3d 63 6f 6c 75 6d 6e 29 , detail=column)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 03 0f 7f 00 0f e8 0f ef 0f 7f 00 00 ................
|
||||
| 3952: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62 ...............b
|
||||
| 3968: 84 80 80 80 80 01 04 00 81 48 00 00 00 54 04 30 .........H...T.0
|
||||
| 3984: 61 61 61 01 81 02 02 02 02 02 02 02 02 02 02 02 aaa.............
|
||||
| 4000: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 4016: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 4032: 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 ................
|
||||
| 4048: 02 02 02 02 02 02 02 03 6a 6a 6a 01 02 02 04 08 ........jjj.....
|
||||
| 4064: 08 08 08 08 08 08 08 08 05 01 03 00 10 01 0a 0f ................
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0d 00 00 00 01 0f d4 00 0f d4 00 00 00 00 00 00 ................
|
||||
| 4048: 00 00 00 00 2a 01 03 00 5b 61 61 61 20 62 62 62 ....*...[aaa bbb
|
||||
| 4064: 20 63 63 63 20 64 64 64 20 65 65 65 20 66 66 66 ccc ddd eee fff
|
||||
| 4080: 20 67 67 67 20 68 68 68 20 69 69 69 20 6a 6a 6a ggg hhh iii jjj
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 04 01 03 00 0e 0a ................
|
||||
| page 6 offset 20480
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| end p.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 12.1 {
|
||||
SELECT rowid FROM ft('a:aaa')
|
||||
} {0 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 13.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename vuln_001.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 03 00 00 00 06 .....@ ........
|
||||
| 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 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................
|
||||
| 96: 00 2e 76 89 0d 00 00 00 06 0e 09 00 0f ca 0f 77 ..v............w
|
||||
| 112: 0f 0f 0e b7 0e 5e 0e 09 00 00 00 00 00 00 00 00 .....^..........
|
||||
| 3584: 00 00 00 00 00 00 00 00 00 53 06 06 17 1d 1d 01 .........S......
|
||||
| 3600: 7b 74 61 62 6c 65 74 5f 63 6f 6e 66 69 67 74 5f .tablet_configt_
|
||||
| 3616: 63 6f 6e 66 69 67 06 43 52 45 41 54 45 20 54 41 config.CREATE TA
|
||||
| 3632: 42 4c 45 20 27 74 5f 63 6f 6e 66 69 67 27 28 6b BLE 't_config'(k
|
||||
| 3648: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
|
||||
| 3664: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 57 05 WITHOUT ROWIDW.
|
||||
| 3680: 06 17 1f 1f 01 7f 74 61 62 6c 65 74 5f 64 6f 63 ......tablet_doc
|
||||
| 3696: 73 69 7a 65 74 5f 64 6f 63 73 69 7a 65 05 43 52 sizet_docsize.CR
|
||||
| 3712: 45 41 54 45 20 54 41 42 4c 45 20 27 74 5f 64 6f EATE TABLE 't_do
|
||||
| 3728: 63 73 69 7a 65 27 28 69 64 20 49 4e 54 45 47 45 csize'(id INTEGE
|
||||
| 3744: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 73 R PRIMARY KEY, s
|
||||
| 3760: 7a 20 42 4c 4f 42 29 56 04 06 17 1f 1f 01 7d 74 z BLOB)V.......t
|
||||
| 3776: 61 62 6c 65 74 5f 63 6f 6e 74 65 6e 74 74 5f 63 ablet_contentt_c
|
||||
| 3792: 6f 6e 74 65 6e 74 04 43 52 45 41 54 45 20 54 41 ontent.CREATE TA
|
||||
| 3808: 42 4c 45 20 27 74 5f 63 6f 6e 74 65 6e 74 27 28 BLE 't_content'(
|
||||
| 3824: 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 id INTEGER PRIMA
|
||||
| 3840: 52 59 20 4b 45 59 2c 20 63 30 2c 20 63 31 29 66 RY KEY, c0, c1)f
|
||||
| 3856: 03 07 17 17 17 01 81 2b 74 61 62 6c 65 74 5f 69 .......+tablet_i
|
||||
| 3872: 64 78 74 5f 69 64 78 03 43 52 45 41 54 45 20 54 dxt_idx.CREATE T
|
||||
| 3888: 41 42 4c 45 20 27 74 5f 69 64 78 27 28 73 65 67 ABLE 't_idx'(seg
|
||||
| 3904: 69 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 id, term, pgno,
|
||||
| 3920: 50 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 PRIMARY KEY(segi
|
||||
| 3936: 64 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 d, term)) WITHOU
|
||||
| 3952: 54 20 52 4f 57 49 44 51 02 06 17 19 19 01 7f 74 T ROWIDQ.......t
|
||||
| 3968: 61 62 6c 65 74 5f 64 61 74 61 74 5f 64 61 74 61 ablet_datat_data
|
||||
| 3984: 02 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
|
||||
| 4000: 5f 64 61 74 61 27 28 69 64 20 49 4e 54 45 47 45 _data'(id INTEGE
|
||||
| 4016: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 R PRIMARY KEY, b
|
||||
| 4032: 6c 6f 63 6b 20 42 4c 4f 42 29 34 01 06 17 0f 0f lock BLOB)4.....
|
||||
| 4048: 08 5b 74 61 62 6c 65 74 74 43 52 45 41 54 45 20 .[tablettCREATE
|
||||
| 4064: 56 49 52 54 55 41 4c 20 54 41 42 4c 45 20 74 20 VIRTUAL TABLE t
|
||||
| 4080: 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 29 USING fts5(a, b)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 04 0b da 00 0f e7 0f ef 0f ce 0b da ................
|
||||
| 3024: 00 00 00 00 00 00 00 00 00 00 87 6c 84 80 80 80 ...........l....
|
||||
| 3040: 80 02 04 00 8f 5c 00 00 03 e8 80 80 80 80 80 80 ................
|
||||
| 3056: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3072: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3088: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3104: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3120: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3136: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3152: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3168: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3184: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3200: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3216: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3232: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3248: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3264: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3280: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3296: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3312: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3328: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3344: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3360: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3376: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3392: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3408: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3424: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3440: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3456: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3472: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3488: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3504: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3520: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3536: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3552: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3568: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3584: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3600: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3616: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3632: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3648: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3664: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3680: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3696: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3712: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3728: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3744: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3760: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3776: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3792: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3808: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3824: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3840: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3856: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3872: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3888: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3904: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3920: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3936: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3952: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3968: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3984: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 4000: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 4016: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 4032: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 12 84 ................
|
||||
| 4048: 80 80 80 80 01 03 00 2a 00 0b 00 0e 06 30 68 65 .......*.....0he
|
||||
| 4064: 6c 6c 6f 01 04 04 04 06 01 03 00 12 01 02 02 0f llo.............
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 02 ...$............
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0d 00 00 00 01 0f e8 00 0f e8 00 00 00 00 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 16 01 04 00 23 1b 68 65 ............#.he
|
||||
| 4080: 6c 6c 6f 20 77 6f 72 6c 64 66 6f 6f 20 62 61 72 llo worldfoo bar
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 01 0f f9 00 0f f9 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 05 01 03 00 10 02 02 ................
|
||||
| page 6 offset 20480
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| end vuln_001.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 13.1 {
|
||||
SELECT * FROM t('a:hello')
|
||||
} {0 {{hello world} {foo bar}}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 14.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename vuln_001.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 03 00 00 00 06 .....@ ........
|
||||
| 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 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................
|
||||
| 96: 00 2e 76 89 0d 00 00 00 06 0e 0a 00 0f c7 0f 74 ..v............t
|
||||
| 112: 0f 0c 0e b8 0e 5f 0e 0a 00 00 00 00 00 00 00 00 ....._..........
|
||||
| 3584: 00 00 00 00 00 00 00 00 00 00 53 06 06 17 1d 1d ..........S.....
|
||||
| 3600: 01 7b 74 61 62 6c 65 74 5f 63 6f 6e 66 69 67 74 ..tablet_configt
|
||||
| 3616: 5f 63 6f 6e 66 69 67 06 43 52 45 41 54 45 20 54 _config.CREATE T
|
||||
| 3632: 41 42 4c 45 20 27 74 5f 63 6f 6e 66 69 67 27 28 ABLE 't_config'(
|
||||
| 3648: 6b 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 k PRIMARY KEY, v
|
||||
| 3664: 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 57 ) WITHOUT ROWIDW
|
||||
| 3680: 05 06 17 1f 1f 01 7f 74 61 62 6c 65 74 5f 64 6f .......tablet_do
|
||||
| 3696: 63 73 69 7a 65 74 5f 64 6f 63 73 69 7a 65 05 43 csizet_docsize.C
|
||||
| 3712: 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 5f 64 REATE TABLE 't_d
|
||||
| 3728: 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 54 45 47 ocsize'(id INTEG
|
||||
| 3744: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
|
||||
| 3760: 73 7a 20 42 4c 4f 42 29 52 04 06 17 1f 1f 01 75 sz BLOB)R......u
|
||||
| 3776: 74 61 62 6c 65 74 5f 63 6f 6e 74 65 6e 74 74 5f tablet_contentt_
|
||||
| 3792: 63 6f 6e 74 65 6e 74 04 43 52 45 41 54 45 20 54 content.CREATE T
|
||||
| 3808: 41 42 4c 45 20 27 74 5f 63 6f 6e 74 65 6e 74 27 ABLE 't_content'
|
||||
| 3824: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM
|
||||
| 3840: 41 52 59 20 4b 45 59 2c 20 63 30 29 66 03 07 17 ARY KEY, c0)f...
|
||||
| 3856: 17 17 01 81 2b 74 61 62 6c 65 74 5f 69 64 78 74 ....+tablet_idxt
|
||||
| 3872: 5f 69 64 78 03 43 52 45 41 54 45 20 54 41 42 4c _idx.CREATE TABL
|
||||
| 3888: 45 20 27 74 5f 69 64 78 27 28 73 65 67 69 64 2c E 't_idx'(segid,
|
||||
| 3904: 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 52 49 term, pgno, PRI
|
||||
| 3920: 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 2c 20 MARY KEY(segid,
|
||||
| 3936: 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 20 52 term)) WITHOUT R
|
||||
| 3952: 4f 57 49 44 51 02 06 17 19 19 01 7f 74 61 62 6c OWIDQ.......tabl
|
||||
| 3968: 65 74 5f 64 61 74 61 74 5f 64 61 74 61 02 43 52 et_datat_data.CR
|
||||
| 3984: 45 41 54 45 20 54 41 42 4c 45 20 27 74 5f 64 61 EATE TABLE 't_da
|
||||
| 4000: 74 61 27 28 69 64 20 49 4e 54 45 47 45 52 20 50 ta'(id INTEGER P
|
||||
| 4016: 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 RIMARY KEY, bloc
|
||||
| 4032: 6b 20 42 4c 4f 42 29 37 01 06 17 0f 0f 08 61 74 k BLOB)7......at
|
||||
| 4048: 61 62 6c 65 74 74 43 52 45 41 54 45 20 56 49 52 ablettCREATE VIR
|
||||
| 4064: 54 55 41 4c 20 54 41 42 4c 45 20 74 20 55 53 49 TUAL TABLE t USI
|
||||
| 4080: 4e 47 20 66 74 73 35 28 63 6f 6e 74 65 6e 74 29 NG fts5(content)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 0f ef 00 03 0f ac 00 0f e8 0f ac 0f c4 00 00 ................
|
||||
| 4000: 00 00 00 00 00 00 00 00 00 00 00 00 16 0a 03 00 ................
|
||||
| 4016: 32 01 00 00 00 01 01 01 00 d5 aa d5 2b 01 01 01 2...........+...
|
||||
| 4032: 83 74 01 01 1d 84 80 80 80 80 01 03 00 40 00 00 .t...........@..
|
||||
| 4048: 00 18 06 30 68 65 6c 6c 6f 01 02 02 01 05 77 6f ...0hello.....wo
|
||||
| 4064: 72 6c 64 01 02 03 04 0a 05 01 03 00 10 01 02 00 rld.............
|
||||
| 4080: 00 00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0d 00 00 00 01 0f f0 00 0f f0 00 00 00 00 00 00 ................
|
||||
| 4080: 0e 01 03 00 23 68 65 6c 6c 6f 20 77 6f 72 6c 64 ....#hello world
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 04 01 03 00 0e 02 ................
|
||||
| page 6 offset 20480
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| end vuln_001.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 14.1 {
|
||||
SELECT * FROM t('hello');
|
||||
} {1 {out of memory}}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
@@ -125,40 +125,4 @@ do_catchsql_test 2.3 {
|
||||
DELETE FROM t1 WHERE rowid = 1
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
PRAGMA page_size=4096;
|
||||
PRAGMA journal_mode=DELETE;
|
||||
CREATE VIRTUAL TABLE t USING fts5(x, detail=none);
|
||||
|
||||
WITH s(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<5000
|
||||
)
|
||||
INSERT INTO t(rowid, x) SELECT i, 'vulnerabilitytest' FROM s;
|
||||
|
||||
INSERT INTO t(t) VALUES('optimize');
|
||||
INSERT INTO t(t, rank) VALUES('secure-delete', 1);
|
||||
} {delete}
|
||||
|
||||
do_test 3.1 {
|
||||
db eval { SELECT rowid AS rowid, block FROM t_data ORDER BY rowid } {
|
||||
if {$rowid>=10 && [string length $block]>=4} {
|
||||
binary scan $block Su first_rowid_off
|
||||
set pgno [expr ($rowid & 0x7FFFFFFF)]
|
||||
if {$pgno>=2 && $first_rowid_off>0} break
|
||||
}
|
||||
}
|
||||
|
||||
set bad [binary format a*a* "\xFF\xFF" [string range $block 2 end]]
|
||||
db eval {
|
||||
UPDATE t_data SET block = $bad WHERE rowid=$rowid
|
||||
}
|
||||
} {}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
DELETE FROM t WHERE rowid=4500;
|
||||
} {1 {fts5: corruption in table "t"}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,273 +0,0 @@
|
||||
# 2026 May 11
|
||||
#
|
||||
# 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]] fts5_common.tcl]
|
||||
set testprefix fts5corruptA
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
sqlite3_fts5_may_be_corrupt 1
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t USING fts5(x, detail='full');
|
||||
INSERT INTO t(t, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
set big [string repeat "a " 200]
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO t(rowid, x) VALUES(1, $big)
|
||||
}
|
||||
|
||||
do_test 1.2 {
|
||||
db eval {
|
||||
SELECT min(rowid) AS base_rowid, count(*) AS page_count FROM t_data
|
||||
WHERE rowid>1000
|
||||
} {}
|
||||
} {}
|
||||
|
||||
do_test 1.3 {
|
||||
for {set ii 0} {$ii < 5} {incr ii} {
|
||||
db eval {
|
||||
INSERT INTO t_data(rowid, block)
|
||||
VALUES( $base_rowid + $page_count + $ii, zeroblob(4) );
|
||||
}
|
||||
}
|
||||
db eval {
|
||||
INSERT INTO t_data(rowid, block)
|
||||
VALUES( $base_rowid + $page_count + 5,
|
||||
unhex('00000080' || 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC') );
|
||||
}
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
db close
|
||||
|
||||
do_test 1.4 {
|
||||
set hex [hexio_read test.db 0 [file size test.db]]
|
||||
|
||||
set off [string first "023061018310" $hex]
|
||||
set hex [string replace $hex $off [expr $off+11] 023061018370]
|
||||
hexio_write test.db 0 $hex
|
||||
} {6144}
|
||||
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 1.5 {
|
||||
SELECT rowid FROM t WHERE t MATCH 'a'
|
||||
} {1 {fts5: corruption found reading blob 137438953481 from table "t"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, a) VALUES(1, 'aaaa');
|
||||
INSERT INTO t1(rowid, a) VALUES(2, 'bbbb');
|
||||
INSERT INTO t1(rowid, a) VALUES(3, 'cccc');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
do_test 2.1 {
|
||||
set hex [hexio_read test.db 0 [file size test.db]]
|
||||
set off [string first "010463636363" $hex]
|
||||
set hex [string replace $hex $off [expr $off+11] 017F63636363]
|
||||
hexio_write test.db 0 $hex
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT rowid FROM t1('cccccccccccccccccccccccccccccccccccccccccccccccc');
|
||||
} {1 {fts5: corruption on page 1, segment 1, table "t1"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
|
||||
INSERT INTO t1 VALUES(
|
||||
'hello',
|
||||
'alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xray yankee zulu',
|
||||
'ant bear cat dog elephant fox gorilla hippo iguana jaguar kangaroo lion monkey newt octopus panda quail rabbit snake turtle unicorn vulture walrus xerus yak zebra'
|
||||
);
|
||||
|
||||
UPDATE t1_data SET block = unhex(
|
||||
-- Preserve bytes 0-168: header through hello's nSz field
|
||||
hex(substr(block, 1, 169))
|
||||
-- Inject 448 bytes of 0xFF: corrupted poslist + subsequent term data
|
||||
|| replace(hex(zeroblob(448)), '00', 'FF')
|
||||
-- Preserve 16 bytes of page index needed for hello lookup
|
||||
|| hex(substr(block, 618, 16))
|
||||
-- Corrupt remaining 37 footer bytes (not needed for hello lookup)
|
||||
|| replace(hex(zeroblob(37)), '00', 'FF')
|
||||
)
|
||||
WHERE id = 137438953473;
|
||||
}
|
||||
|
||||
do_test 3.1 {
|
||||
execsql { SELECT * FROM t1 WHERE t1 MATCH '{a b}: hello' }
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(content);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
BEGIN;
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<5000
|
||||
)
|
||||
INSERT INTO t1(content) SELECT 'xyzterm' FROM s;
|
||||
COMMIT;
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_test 4.1 {
|
||||
db eval {
|
||||
SELECT rowid AS a, hex(block) AS block FROM t1_data
|
||||
WHERE ((rowid>>36) & 0x01)==1
|
||||
AND ((rowid>>31) & 0x1F)==0
|
||||
} {
|
||||
set n [string length $block]
|
||||
set block [string range $block 0 11]
|
||||
append block [string repeat "00" [expr ($n/2)-7-2]]
|
||||
append block "8080"
|
||||
db eval {
|
||||
UPDATE t1_data SET block = unhex($block) WHERE rowid = $a
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_catchsql_test 4.2 {
|
||||
SELECT count(*) FROM (
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'xyzterm' ORDER BY rowid ASC
|
||||
)
|
||||
} {0 5000}
|
||||
|
||||
do_catchsql_test 4.3 {
|
||||
SELECT count(*) FROM (
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'xyzterm' ORDER BY rowid DESC
|
||||
)
|
||||
} {0 4987}
|
||||
|
||||
do_test 4.4 {
|
||||
db eval {
|
||||
SELECT rowid AS a, hex(block) AS block FROM t1_data
|
||||
WHERE ((rowid>>36) & 0x01)==1
|
||||
AND ((rowid>>31) & 0x1F)==0
|
||||
} {
|
||||
set n [string length $block]
|
||||
set block [string range $block 0 $n-5]
|
||||
append block [string repeat "00" 2]
|
||||
db eval {
|
||||
UPDATE t1_data SET block = unhex($block) WHERE rowid = $a
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_catchsql_test 4.5 {
|
||||
SELECT count(*) FROM (
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'xyzterm' ORDER BY rowid ASC
|
||||
)
|
||||
} {0 5000}
|
||||
|
||||
do_catchsql_test 4.6 {
|
||||
SELECT count(*) FROM (
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'xyzterm' ORDER BY rowid DESC
|
||||
)
|
||||
} {0 4879}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE t USING fts5(x);
|
||||
INSERT INTO t(t,rank) VALUES('pgsz', 64);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<80
|
||||
)
|
||||
INSERT INTO t SELECT 'shared word' || (i%10) || ' shared shared' FROM s;
|
||||
INSERT INTO t(t) VALUES('optimize');
|
||||
}
|
||||
|
||||
set lLeaf [db eval { SELECT id FROM t_data }]
|
||||
db_save_and_close
|
||||
|
||||
foreach leaf $lLeaf {
|
||||
db_restore_and_reopen
|
||||
set leaf [expr $leaf]
|
||||
set hex [db one {
|
||||
SELECT hex(block) FROM t_data WHERE id=$leaf
|
||||
}]
|
||||
|
||||
# Replace the first 4 bytes of each leaf page with the size of the leaf in
|
||||
# bytes plus 50 as a 2 byte integer, followed by 0x7FFF.
|
||||
#
|
||||
set nn [expr [string length $hex]/2]
|
||||
set first "[format %.4x [expr $nn+50]]7FFF"
|
||||
set hex [string replace $hex 0 7 $first]
|
||||
|
||||
db eval { UPDATE t_data SET block=unhex($hex) WHERE id=$leaf }
|
||||
do_test 5.1.$leaf {
|
||||
catchsql {
|
||||
SELECT rowid FROM t WHERE t MATCH 'shared' ORDER BY rowid DESC;
|
||||
}
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
do_test 5.1.$leaf.2 {
|
||||
catchsql {
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
set {} {}
|
||||
} {}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE t USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO t(rowid,x)
|
||||
VALUES(1,'a b'), (2,'c d'), (3,'e f'), (4,'g h'), (5,'i j');
|
||||
INSERT INTO t(t) VALUES('optimize');
|
||||
DELETE FROM t WHERE rowid=2;
|
||||
}
|
||||
|
||||
do_test 6.1 {
|
||||
db eval {
|
||||
SELECT rowid AS rid, hex(block) AS blk
|
||||
FROM t_data WHERE rowid>1_000_000_000_000
|
||||
} {}
|
||||
|
||||
set blk [string replace $blk 8 15 20000000]
|
||||
execsql {
|
||||
UPDATE t_data SET block = unhex($blk) WHERE rowid=$rid
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
DELETE FROM t WHERE rowid=3;
|
||||
}
|
||||
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -379,6 +379,9 @@ do_execsql_test 12.2 {
|
||||
db close
|
||||
sqlite3 db test.db -readonly 1
|
||||
|
||||
explain_i {
|
||||
PRAGMA integrity_check
|
||||
}
|
||||
do_execsql_test 12.3 {
|
||||
PRAGMA integrity_check
|
||||
} {ok}
|
||||
|
||||
@@ -64,21 +64,4 @@ foreach {tn sql} {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Verify that https://sqlite.org/forum/forumpost/95413eb410 has been
|
||||
# fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE f1 USING fts5(x);
|
||||
BEGIN TRANSACTION;
|
||||
INSERT INTO f1(x) VALUES('abc def ghi');
|
||||
}
|
||||
do_test 2.1 {
|
||||
sqlite3_interrupt db
|
||||
} {}
|
||||
do_execsql_test 2.2 {
|
||||
ROLLBACK
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -65,14 +65,5 @@ do_eqp_test 1.4 {
|
||||
`--SCAN vt VIRTUAL TABLE INDEX 0:=
|
||||
}
|
||||
|
||||
do_eqp_test 1.5 {
|
||||
SELECT * FROM vt, t1
|
||||
WHERE vt.rowid = t1.rowid AND vt MATCH ? AND b = ?
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN vt VIRTUAL TABLE INDEX 0:M1
|
||||
`--SEARCH t1 USING INTEGER PRIMARY KEY (rowid=?)
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -743,19 +743,6 @@ do_execsql_test 15.4 {
|
||||
SELECT c, fts5_columnlocale(ft, 2) FROM ft
|
||||
} {three {} three loc}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
set nMaxCol 500
|
||||
set nStep 20
|
||||
|
||||
for {set ii $nStep} {$ii <= $nMaxCol} {incr ii $nStep} {
|
||||
set C [list]
|
||||
for {set iCol 0} {$iCol<$ii} {incr iCol} {
|
||||
lappend C col$iCol
|
||||
}
|
||||
set ct "CREATE VIRTUAL TABLE t$ii USING fts5([join $C ,], locale=1)"
|
||||
do_execsql_test 16.$ii $ct
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -238,22 +238,6 @@ do_execsql_test 6.3 {
|
||||
INSERT INTO g1(g1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Check that passing -2147483648 as the parameter to a merge command
|
||||
# does not cause a signed integer overflow error.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE f1 USING fts5(a);
|
||||
}
|
||||
do_execsql_test 7.1 {
|
||||
INSERT INTO f1 VALUES('one two three');
|
||||
INSERT INTO f1 VALUES('four five six');
|
||||
INSERT INTO f1 VALUES('seven eight nine');
|
||||
}
|
||||
do_execsql_test 7.2 {
|
||||
INSERT INTO f1(f1, rank) VALUES('merge', -2147483648);
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -66,18 +66,5 @@ do_near_test 1.23 "a b c d e f g h i" { NEAR(a+b+c+d i b+c, 4) } 0
|
||||
do_near_test 1.24 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 5) } 1
|
||||
do_near_test 1.25 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 4) } 0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that https://sqlite.org/bugs/forumpost/16bb36f7e8 is fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t USING fts5(x, tokenize=trigram);
|
||||
INSERT INTO t
|
||||
VALUES('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT count(*) FROM t WHERE t MATCH 'NEAR(aaa aaa, 4)';
|
||||
} {1}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -65,13 +65,5 @@ foreach {in out} $test_vocab {
|
||||
incr i
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE tbl USING fts5(t, tokenize='porter unicode61');
|
||||
INSERT INTO tbl VALUES(' andeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee');
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -418,34 +418,6 @@ do_execsql_test 7.1.2 {
|
||||
INSERT INTO t2(t2) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
|
||||
proc tcl_create {args} { return "tcl_tokenize" }
|
||||
sqlite3_fts5_create_tokenizer db tcl tcl_create
|
||||
|
||||
proc tcl_tokenize {tflags text} {
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
if {$w=="usa"} {
|
||||
sqlite3_fts5_token -colo "united" $iStart $iEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize='tcl', tokendata=1);
|
||||
INSERT INTO t1 VALUES('usa is great');
|
||||
INSERT INTO t1 VALUES('canada is cool');
|
||||
INSERT INTO t1 VALUES('australia is what matters');
|
||||
}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'usa*';
|
||||
} {{usa is great}}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -371,14 +371,4 @@ do_execsql_test 10.10 {
|
||||
a 2 2 b 2 2 c 2 2 d 1 1 e 1 1 f 1 1
|
||||
}
|
||||
|
||||
# Bug report 2026-05-18T05:19:01Z
|
||||
#
|
||||
set manyporters {porter Porter}
|
||||
for {set i 0} {$i<18} {incr i} {
|
||||
set manyporters "$manyporters $manyporters"
|
||||
}
|
||||
do_execsql_test 11.1 "
|
||||
CREATE VIRTUAL TABLE t11 USING fts5(a,b,tokenize=\"$manyporters\");
|
||||
" {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -305,28 +305,6 @@ do_catchsql_test 6.2 {
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, col);
|
||||
|
||||
INSERT INTO t1 VALUES('xx', 'xx');
|
||||
|
||||
CREATE TABLE x1(t);
|
||||
INSERT INTO x1 VALUES('xx');
|
||||
INSERT INTO x1 VALUES('xx');
|
||||
|
||||
SELECT term, col FROM v1;
|
||||
} {
|
||||
xx a xx b
|
||||
}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT * FROM x1 WHERE 'a'=(SELECT col FROM v1 WHERE term=t)
|
||||
} {xx xx}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
#
|
||||
# 2026 March 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
# This script extracts the documentation for the API used by fts5 auxiliary
|
||||
# functions from header file fts5.h. It outputs html text on stdout that
|
||||
# is included in the documentation on the web.
|
||||
#
|
||||
|
||||
|
||||
sqlite3 db fts5cost.db
|
||||
|
||||
# Create an IPK table with 1,000,000 entries. Short records.
|
||||
#
|
||||
set res [list [catch { db eval {SELECT count(*) FROM t1} } msg] $msg]
|
||||
if {$res!="0 1000000"} {
|
||||
db eval {
|
||||
PRAGMA mmap_size = 1000000000; -- 1GB
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1_000_000
|
||||
)
|
||||
INSERT INTO t1 SELECT i, hex(randomblob(8)) FROM s;
|
||||
}
|
||||
}
|
||||
|
||||
# Create an FTS5 table with 1,000,000 entries. Each row contains a single
|
||||
# column containing a document of 100 terms chosen pseudo-randomly from
|
||||
# a vocabularly of 2000.
|
||||
set res [list [catch { db eval {SELECT count(*) FROM f1} } msg] $msg]
|
||||
if {$res!="0 1000000"} {
|
||||
set nVocab 2000
|
||||
set nTerm 100
|
||||
db eval {
|
||||
BEGIN;
|
||||
DROP TABLE IF EXISTS vocab1;
|
||||
CREATE TABLE vocab1(w);
|
||||
}
|
||||
for {set ii 0} {$ii<$nVocab} {incr ii} {
|
||||
set word [format %06x [expr {int(abs(rand()) * 0xFFFFFF)}]]
|
||||
db eval { INSERT INTO vocab1 VALUES($word) }
|
||||
lappend lVocab $word
|
||||
}
|
||||
db func doc doc
|
||||
proc doc {} {
|
||||
for {set ii 0} {$ii<$::nTerm} {incr ii} {
|
||||
lappend ret [lindex $::lVocab [expr int(abs(rand())*$::nVocab)]]
|
||||
}
|
||||
set ret
|
||||
}
|
||||
db eval {
|
||||
DROP TABLE IF EXISTS f1;
|
||||
CREATE VIRTUAL TABLE f1 USING fts5(x);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1_000_000
|
||||
)
|
||||
INSERT INTO f1(rowid, x) SELECT i, doc() FROM s;
|
||||
COMMIT;
|
||||
}
|
||||
} else {
|
||||
set lVocab [db eval { SELECT * FROM vocab1 }]
|
||||
set nVocab [llength $lVocab]
|
||||
}
|
||||
|
||||
proc rowid_query {n} {
|
||||
set rowid 654
|
||||
for {set ii 0} {$ii<$n} {incr ii} {
|
||||
db eval { SELECT b FROM t1 WHERE a = $rowid }
|
||||
set rowid [expr {($rowid + 7717) % 1000000}]
|
||||
}
|
||||
}
|
||||
|
||||
proc rowid_query_fts {n} {
|
||||
set rowid 654
|
||||
for {set ii 0} {$ii<$n} {incr ii} {
|
||||
db eval { SELECT * FROM f1 WHERE rowid = $rowid }
|
||||
set rowid [expr {($rowid + 7717) % 1000000}]
|
||||
}
|
||||
}
|
||||
|
||||
proc match_query_fts {n} {
|
||||
set idx 654
|
||||
for {set ii 0} {$ii<$n} {incr ii} {
|
||||
set match [lrange $::lVocab $idx $idx+1]
|
||||
db eval { SELECT * FROM f1($match) }
|
||||
set idx [expr {($idx + 7717) % $::nVocab}]
|
||||
}
|
||||
}
|
||||
|
||||
proc prefix_query_fts {n} {
|
||||
set idx 654
|
||||
for {set ii 0} {$ii<$n} {incr ii} {
|
||||
set match "[lindex $::lVocab $idx]*"
|
||||
db eval { SELECT * FROM f1($match) }
|
||||
set idx [expr {($idx + 7717) % $::nVocab}]
|
||||
}
|
||||
}
|
||||
|
||||
proc match_rowid_query_fts {n} {
|
||||
set idx 654
|
||||
for {set ii 0} {$ii<$n} {incr ii} {
|
||||
set match "[lindex $::lVocab $idx]"
|
||||
db eval { SELECT * FROM f1($match) WHERE rowid=500000 }
|
||||
set idx [expr {($idx + 7717) % $::nVocab}]
|
||||
}
|
||||
}
|
||||
|
||||
proc prefix_rowid_query_fts {n} {
|
||||
set idx 654
|
||||
for {set ii 0} {$ii<$n} {incr ii} {
|
||||
set match "[lindex $::lVocab $idx]*"
|
||||
db eval { SELECT * FROM f1($match) WHERE rowid=500000 }
|
||||
set idx [expr {($idx + 7717) % $::nVocab}]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
proc mytime {cmd div} {
|
||||
set tm [time $cmd]
|
||||
expr {[lindex $tm 0] / $div}
|
||||
}
|
||||
|
||||
#set us [mytime { match_rowid_query_fts 1000 } 1000]
|
||||
#puts "1000 match/rowid queries on fts5 table: ${us} per query"
|
||||
|
||||
set us [mytime { prefix_rowid_query_fts 1000 } 1000]
|
||||
puts "1000 prefix/rowid queries on fts5 table: ${us} per query"
|
||||
|
||||
set us [mytime { match_query_fts 10 } 10]
|
||||
puts "10 match queries on fts5 table: ${us} per query"
|
||||
|
||||
set us [mytime { prefix_query_fts 10 } 10]
|
||||
puts "10 prefix queries on fts5 table: ${us} per query"
|
||||
|
||||
set us [mytime { prefix_rowid_query_fts 1000 } 1000]
|
||||
puts "1000 prefix/rowid queries on fts5 table: ${us} per query"
|
||||
|
||||
set us [mytime { rowid_query 10000 } 10000]
|
||||
puts "10000 by-rowid queries on normal table: ${us} per query"
|
||||
|
||||
set us [mytime { rowid_query_fts 10000 } 10000]
|
||||
puts "10000 by-rowid queries on fts5 table: ${us} per query"
|
||||
|
||||
|
||||
+4
-8
@@ -105,9 +105,6 @@ static const unsigned char icuUtf8Trans1[] = {
|
||||
while( (*zIn & 0xc0)==0x80 ){ \
|
||||
c = (c<<6) + (0x3f & *(zIn++)); \
|
||||
} \
|
||||
if( c<0x80 \
|
||||
|| (c&0xFFFFF800)==0xD800 \
|
||||
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
|
||||
}
|
||||
|
||||
#define SQLITE_ICU_SKIP_UTF8(zIn) \
|
||||
@@ -365,7 +362,7 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
const UChar *zInput; /* Pointer to input string */
|
||||
UChar *zOutput = 0; /* Pointer to output buffer */
|
||||
int nInput; /* Size of utf-16 input string in bytes */
|
||||
sqlite3_int64 nOut; /* Size of output buffer in bytes */
|
||||
int nOut; /* Size of output buffer in bytes */
|
||||
int cnt;
|
||||
int bToUpper; /* True for toupper(), false for tolower() */
|
||||
UErrorCode status;
|
||||
@@ -388,7 +385,7 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
}
|
||||
|
||||
for(cnt=0; cnt<2; cnt++){
|
||||
UChar *zNew = sqlite3_realloc64(zOutput, nOut);
|
||||
UChar *zNew = sqlite3_realloc(zOutput, nOut);
|
||||
if( zNew==0 ){
|
||||
sqlite3_free(zOutput);
|
||||
sqlite3_result_error_nomem(p);
|
||||
@@ -397,9 +394,9 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
zOutput = zNew;
|
||||
status = U_ZERO_ERROR;
|
||||
if( bToUpper ){
|
||||
nOut = 2LL*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
||||
nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
||||
}else{
|
||||
nOut = 2LL*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
||||
nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
||||
}
|
||||
|
||||
if( U_SUCCESS(status) ){
|
||||
@@ -519,7 +516,6 @@ static void icuLoadCollation(
|
||||
}
|
||||
sqlite3_result_error(p, sqlite3_str_value(pStr), -1);
|
||||
sqlite3_free(sqlite3_str_finish(pStr));
|
||||
ucol_close(pUCollator);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ static int intckGetToken(const char *z){
|
||||
char c = z[0];
|
||||
int iRet = 1;
|
||||
if( c=='\'' || c=='"' || c=='`' ){
|
||||
while( z[iRet] ){
|
||||
while( 1 ){
|
||||
if( z[iRet]==c ){
|
||||
iRet++;
|
||||
if( z[iRet]!=c ) break;
|
||||
|
||||
+3
-3
@@ -847,7 +847,7 @@ static int amatchConnect(
|
||||
|
||||
(void)pAux;
|
||||
*ppVtab = 0;
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -928,7 +928,7 @@ amatchConnectError:
|
||||
static int amatchOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
amatch_vtab *p = (amatch_vtab*)pVTab;
|
||||
amatch_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->pVtab = p;
|
||||
@@ -1157,7 +1157,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
|
||||
#endif
|
||||
nWord = (int)strlen(pWord->zWord+2);
|
||||
if( nWord+20>nBuf ){
|
||||
nBuf = nWord+100;
|
||||
nBuf = (char)(nWord+100);
|
||||
zBuf = sqlite3_realloc64(zBuf, nBuf);
|
||||
if( zBuf==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
+3
-3
@@ -232,7 +232,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
cBuf = sqlite3_malloc64(nc);
|
||||
cBuf = sqlite3_malloc(nc);
|
||||
if( !cBuf ) goto memFail;
|
||||
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
|
||||
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
|
||||
@@ -254,7 +254,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
sqlite3_result_zeroblob(context, 0);
|
||||
break;
|
||||
}
|
||||
bBuf = sqlite3_malloc64(nb);
|
||||
bBuf = sqlite3_malloc(nb);
|
||||
if( !bBuf ) goto memFail;
|
||||
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
|
||||
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
|
||||
@@ -275,7 +275,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_base64_init
|
||||
int sqlite3_base_init
|
||||
#else
|
||||
static int sqlite3_base64_init
|
||||
#endif
|
||||
|
||||
+9
-9
@@ -262,7 +262,7 @@ static int allBase85( char *p, int len ){
|
||||
|
||||
#ifndef BASE85_STANDALONE
|
||||
|
||||
#ifndef OMIT_BASE85_CHECKER
|
||||
# ifndef OMIT_BASE85_CHECKER
|
||||
/* This function does the work for the SQLite is_base85(t) UDF. */
|
||||
static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
assert(na==1);
|
||||
@@ -282,7 +282,7 @@ static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
|
||||
/* This function does the work for the SQLite base85(x) UDF. */
|
||||
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
@@ -309,7 +309,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
cBuf = sqlite3_malloc64(nc);
|
||||
cBuf = sqlite3_malloc(nc);
|
||||
if( !cBuf ) goto memFail;
|
||||
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
|
||||
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
|
||||
@@ -331,7 +331,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
sqlite3_result_zeroblob(context, 0);
|
||||
break;
|
||||
}
|
||||
bBuf = sqlite3_malloc64(nb);
|
||||
bBuf = sqlite3_malloc(nb);
|
||||
if( !bBuf ) goto memFail;
|
||||
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
|
||||
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
|
||||
@@ -352,14 +352,14 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_base85_init
|
||||
int sqlite3_base_init
|
||||
#else
|
||||
static int sqlite3_base85_init
|
||||
#endif
|
||||
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErr;
|
||||
#ifndef OMIT_BASE85_CHECKER
|
||||
# ifndef OMIT_BASE85_CHECKER
|
||||
{
|
||||
int rc = sqlite3_create_function
|
||||
(db, "is_base85", 1,
|
||||
@@ -367,7 +367,7 @@ static int sqlite3_base85_init
|
||||
0, is_base85, 0, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
return sqlite3_create_function
|
||||
(db, "base85", 1,
|
||||
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
|
||||
@@ -432,9 +432,9 @@ int main(int na, char *av[]){
|
||||
int nc = strlen(cBuf);
|
||||
size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf;
|
||||
if( 1 != fwrite(bBuf, nbo, 1, fb) ) rc = 1;
|
||||
#ifndef OMIT_BASE85_CHECKER
|
||||
# ifndef OMIT_BASE85_CHECKER
|
||||
b85Clean &= allBase85( cBuf, nc );
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
** 2019-03-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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** An SQL function that uses the incremental BLOB I/O mechanism of SQLite
|
||||
** to read or write part of a blob. This is intended for debugging use
|
||||
** in the CLI.
|
||||
**
|
||||
** readblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,N)
|
||||
**
|
||||
** Returns N bytes of the blob starting at OFFSET.
|
||||
**
|
||||
** writeblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,NEWDATA)
|
||||
**
|
||||
** NEWDATA must be a blob. The content of NEWDATA overwrites the
|
||||
** existing BLOB data at SCHEMA.TABLE.COLUMN for row ROWID beginning
|
||||
** at OFFSET bytes into the blob.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static void readblobFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_blob *pBlob = 0;
|
||||
const char *zSchema;
|
||||
const char *zTable;
|
||||
const char *zColumn;
|
||||
sqlite3_int64 iRowid;
|
||||
int iOfst;
|
||||
unsigned char *aData;
|
||||
int nData;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
zTable = (const char*)sqlite3_value_text(argv[1]);
|
||||
if( zTable==0 ){
|
||||
sqlite3_result_error(context, "bad table name", -1);
|
||||
return;
|
||||
}
|
||||
zColumn = (const char*)sqlite3_value_text(argv[2]);
|
||||
if( zTable==0 ){
|
||||
sqlite3_result_error(context, "bad column name", -1);
|
||||
return;
|
||||
}
|
||||
iRowid = sqlite3_value_int64(argv[3]);
|
||||
iOfst = sqlite3_value_int(argv[4]);
|
||||
nData = sqlite3_value_int(argv[5]);
|
||||
if( nData<=0 ) return;
|
||||
aData = sqlite3_malloc64( nData+1 );
|
||||
if( aData==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
db = sqlite3_context_db_handle(context);
|
||||
rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 0, &pBlob);
|
||||
if( rc ){
|
||||
sqlite3_free(aData);
|
||||
sqlite3_result_error(context, "cannot open BLOB pointer", -1);
|
||||
return;
|
||||
}
|
||||
rc = sqlite3_blob_read(pBlob, aData, nData, iOfst);
|
||||
sqlite3_blob_close(pBlob);
|
||||
if( rc ){
|
||||
sqlite3_free(aData);
|
||||
sqlite3_result_error(context, "BLOB read failed", -1);
|
||||
}else{
|
||||
sqlite3_result_blob(context, aData, nData, sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
static void writeblobFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_blob *pBlob = 0;
|
||||
const char *zSchema;
|
||||
const char *zTable;
|
||||
const char *zColumn;
|
||||
sqlite3_int64 iRowid;
|
||||
int iOfst;
|
||||
unsigned char *aData;
|
||||
int nData;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
zTable = (const char*)sqlite3_value_text(argv[1]);
|
||||
if( zTable==0 ){
|
||||
sqlite3_result_error(context, "bad table name", -1);
|
||||
return;
|
||||
}
|
||||
zColumn = (const char*)sqlite3_value_text(argv[2]);
|
||||
if( zTable==0 ){
|
||||
sqlite3_result_error(context, "bad column name", -1);
|
||||
return;
|
||||
}
|
||||
iRowid = sqlite3_value_int64(argv[3]);
|
||||
iOfst = sqlite3_value_int(argv[4]);
|
||||
if( sqlite3_value_type(argv[5])!=SQLITE_BLOB ){
|
||||
sqlite3_result_error(context, "6th argument must be a BLOB", -1);
|
||||
return;
|
||||
}
|
||||
nData = sqlite3_value_bytes(argv[5]);
|
||||
aData = (unsigned char *)sqlite3_value_blob(argv[5]);
|
||||
db = sqlite3_context_db_handle(context);
|
||||
rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 1, &pBlob);
|
||||
if( rc ){
|
||||
sqlite3_result_error(context, "cannot open BLOB pointer", -1);
|
||||
return;
|
||||
}
|
||||
rc = sqlite3_blob_write(pBlob, aData, nData, iOfst);
|
||||
sqlite3_blob_close(pBlob);
|
||||
if( rc ){
|
||||
sqlite3_result_error(context, "BLOB write failed", -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_blobio_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "readblob", 6, SQLITE_UTF8, 0,
|
||||
readblobFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "writeblob", 6, SQLITE_UTF8, 0,
|
||||
writeblobFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
+1
-17
@@ -60,13 +60,6 @@
|
||||
**
|
||||
** SELECT name FROM sqlite_btreeinfo
|
||||
** WHERE type='table' AND NOT hasRowid;
|
||||
**
|
||||
** UNUSED, UNTESTED, UNSUPPORTED
|
||||
**
|
||||
** This extension exists for information and demonstration purposes
|
||||
** only. The developers are not aware of any real-world use of this
|
||||
** extension. The extension has no formal test cases. The developers
|
||||
** do not actively support it.
|
||||
*/
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
@@ -293,11 +286,6 @@ static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
|
||||
pCsr->depth = 1;
|
||||
while(1){
|
||||
sqlite3_bind_int(pStmt, 1, pgno);
|
||||
if( pCsr->depth>25 ){
|
||||
sqlite3_set_errmsg(db, SQLITE_CORRUPT, "btree nested too deep");
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
rc = SQLITE_ERROR;
|
||||
@@ -318,10 +306,6 @@ static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
|
||||
nEntry *= (nCell+1);
|
||||
if( aData[0]==10 || aData[0]==13 ) break;
|
||||
nPage *= (nCell+1);
|
||||
if( 14+2*(nCell/2)>=pgsz ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
if( nCell<=1 ){
|
||||
pgno = get_uint32(aData+8);
|
||||
}else{
|
||||
@@ -355,7 +339,7 @@ static int binfoColumn(
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
int rc = binfoCompute(db, pgno, pCsr);
|
||||
if( rc ){
|
||||
pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errstr(rc));
|
||||
pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -516,7 +516,7 @@ static int closureConnect(
|
||||
|
||||
(void)pAux;
|
||||
*ppVtab = 0;
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -579,7 +579,7 @@ closureConnectError:
|
||||
static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
closure_vtab *p = (closure_vtab*)pVTab;
|
||||
closure_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->pVtab = p;
|
||||
@@ -638,7 +638,7 @@ static int closureInsertNode(
|
||||
sqlite3_int64 id, /* The node ID */
|
||||
int iGeneration /* The generation number for this node */
|
||||
){
|
||||
closure_avl *pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->id = id;
|
||||
|
||||
+15
-27
@@ -132,7 +132,7 @@ static int completionConnect(
|
||||
" phase INT HIDDEN" /* Used for debugging only */
|
||||
")");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -154,7 +154,7 @@ static int completionDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
completion_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->db = ((completion_vtab*)p)->db;
|
||||
@@ -199,7 +199,6 @@ static int completionNext(sqlite3_vtab_cursor *cur){
|
||||
completion_cursor *pCur = (completion_cursor*)cur;
|
||||
int eNextPhase = 0; /* Next phase to try if current phase reaches end */
|
||||
int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */
|
||||
int rc;
|
||||
pCur->iRowid++;
|
||||
while( pCur->ePhase!=COMPLETION_EOF ){
|
||||
switch( pCur->ePhase ){
|
||||
@@ -225,27 +224,22 @@ static int completionNext(sqlite3_vtab_cursor *cur){
|
||||
case COMPLETION_TABLES: {
|
||||
if( pCur->pStmt==0 ){
|
||||
sqlite3_stmt *pS2;
|
||||
sqlite3_str* pStr = sqlite3_str_new(pCur->db);
|
||||
char *zSql = 0;
|
||||
const char *zSep = "";
|
||||
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
|
||||
while( sqlite3_step(pS2)==SQLITE_ROW ){
|
||||
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
|
||||
sqlite3_str_appendf(pStr,
|
||||
"%s"
|
||||
zSql = sqlite3_mprintf(
|
||||
"%z%s"
|
||||
"SELECT name FROM \"%w\".sqlite_schema",
|
||||
zSep, zDb
|
||||
zSql, zSep, zDb
|
||||
);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
zSep = " UNION ";
|
||||
}
|
||||
rc = sqlite3_finalize(pS2);
|
||||
zSql = sqlite3_str_finish(pStr);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pS2);
|
||||
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
iCol = 0;
|
||||
eNextPhase = COMPLETION_COLUMNS;
|
||||
@@ -254,29 +248,24 @@ static int completionNext(sqlite3_vtab_cursor *cur){
|
||||
case COMPLETION_COLUMNS: {
|
||||
if( pCur->pStmt==0 ){
|
||||
sqlite3_stmt *pS2;
|
||||
sqlite3_str *pStr = sqlite3_str_new(pCur->db);
|
||||
char *zSql = 0;
|
||||
const char *zSep = "";
|
||||
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
|
||||
while( sqlite3_step(pS2)==SQLITE_ROW ){
|
||||
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
|
||||
sqlite3_str_appendf(pStr,
|
||||
"%s"
|
||||
zSql = sqlite3_mprintf(
|
||||
"%z%s"
|
||||
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
|
||||
" JOIN pragma_table_xinfo(sm.name,%Q) AS pti"
|
||||
" WHERE sm.type='table'",
|
||||
zSep, zDb, zDb
|
||||
zSql, zSep, zDb, zDb
|
||||
);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
zSep = " UNION ";
|
||||
}
|
||||
rc = sqlite3_finalize(pS2);
|
||||
zSql = sqlite3_str_finish(pStr);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pS2);
|
||||
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
iCol = 0;
|
||||
eNextPhase = COMPLETION_EOF;
|
||||
@@ -293,10 +282,9 @@ static int completionNext(sqlite3_vtab_cursor *cur){
|
||||
pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
|
||||
}else{
|
||||
/* When all rows are finished, advance to the next phase */
|
||||
rc = sqlite3_finalize(pCur->pStmt);
|
||||
sqlite3_finalize(pCur->pStmt);
|
||||
pCur->pStmt = 0;
|
||||
pCur->ePhase = eNextPhase;
|
||||
if( rc ) return rc;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -59,11 +59,7 @@ static void compressFunc(
|
||||
pIn = sqlite3_value_blob(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
nOut = 13 + nIn + (nIn+999)/1000;
|
||||
pOut = sqlite3_malloc64( nOut+5 );
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
pOut = sqlite3_malloc( nOut+5 );
|
||||
for(i=4; i>=0; i--){
|
||||
x[i] = (nIn >> (7*(4-i)))&0x7f;
|
||||
}
|
||||
@@ -102,11 +98,7 @@ static void uncompressFunc(
|
||||
nOut = (nOut<<7) | (pIn[i]&0x7f);
|
||||
if( (pIn[i]&0x80)!=0 ){ i++; break; }
|
||||
}
|
||||
pOut = sqlite3_malloc64( nOut+1 );
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
pOut = sqlite3_malloc( nOut+1 );
|
||||
rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
|
||||
if( rc==Z_OK ){
|
||||
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
||||
|
||||
+16
-24
@@ -24,8 +24,8 @@
|
||||
** schema= parameter, like this:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE temp.csv2 USING csv(
|
||||
** filename = '../http.log',
|
||||
** schema = 'CREATE TABLE x(date,ipaddr,url,referrer,userAgent)'
|
||||
** filename = "../http.log",
|
||||
** schema = "CREATE TABLE x(date,ipaddr,url,referrer,userAgent)"
|
||||
** );
|
||||
**
|
||||
** Instead of specifying a file, the text of the CSV can be loaded using
|
||||
@@ -62,10 +62,6 @@ SQLITE_EXTENSION_INIT1
|
||||
# define CSV_NOINLINE
|
||||
#endif
|
||||
|
||||
#ifndef SQLITEINT_H
|
||||
typedef sqlite3_int64 i64;
|
||||
typedef sqlite3_uint64 u64;
|
||||
#endif
|
||||
|
||||
/* Max size of the error message in a CsvReader */
|
||||
#define CSV_MXERR 200
|
||||
@@ -78,9 +74,9 @@ typedef struct CsvReader CsvReader;
|
||||
struct CsvReader {
|
||||
FILE *in; /* Read the CSV text from this input stream */
|
||||
char *z; /* Accumulated text for a field */
|
||||
i64 n; /* Number of bytes in z */
|
||||
i64 nAlloc; /* Space allocated for z[] */
|
||||
i64 nLine; /* Current line number */
|
||||
int n; /* Number of bytes in z */
|
||||
int nAlloc; /* Space allocated for z[] */
|
||||
int nLine; /* Current line number */
|
||||
int bNotFirst; /* True if prior text has been seen */
|
||||
int cTerm; /* Character that terminated the most recent field */
|
||||
size_t iIn; /* Next unread character in the input buffer */
|
||||
@@ -129,7 +125,7 @@ static int csv_reader_open(
|
||||
const char *zData /* ... or use this data */
|
||||
){
|
||||
if( zFilename ){
|
||||
p->zIn = sqlite3_malloc64( CSV_INBUFSZ );
|
||||
p->zIn = sqlite3_malloc( CSV_INBUFSZ );
|
||||
if( p->zIn==0 ){
|
||||
csv_errmsg(p, "out of memory");
|
||||
return 1;
|
||||
@@ -178,7 +174,7 @@ static int csv_getc(CsvReader *p){
|
||||
** Return 0 on success and non-zero if there is an OOM error */
|
||||
static CSV_NOINLINE int csv_resize_and_append(CsvReader *p, char c){
|
||||
char *zNew;
|
||||
i64 nNew = p->nAlloc*2 + 100;
|
||||
int nNew = p->nAlloc*2 + 100;
|
||||
zNew = sqlite3_realloc64(p->z, nNew);
|
||||
if( zNew ){
|
||||
p->z = zNew;
|
||||
@@ -222,7 +218,7 @@ static char *csv_read_one_field(CsvReader *p){
|
||||
}
|
||||
if( c=='"' ){
|
||||
int pc, ppc;
|
||||
i64 startLine = p->nLine;
|
||||
int startLine = p->nLine;
|
||||
pc = ppc = 0;
|
||||
while( 1 ){
|
||||
c = csv_getc(p);
|
||||
@@ -326,7 +322,7 @@ typedef struct CsvCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
CsvReader rdr; /* The CsvReader object */
|
||||
char **azVal; /* Value of the current row */
|
||||
i64 *aLen; /* Length of each entry */
|
||||
int *aLen; /* Length of each entry */
|
||||
sqlite3_int64 iRowid; /* The current rowid. Negative for EOF */
|
||||
} CsvCursor;
|
||||
|
||||
@@ -498,7 +494,7 @@ static int csvtabConnect(
|
||||
CsvTable *pNew = 0; /* The CsvTable object to construct */
|
||||
int bHeader = -1; /* header= flags. -1 means not seen yet */
|
||||
int rc = SQLITE_OK; /* Result code from this routine */
|
||||
u64 i, j; /* Loop counters */
|
||||
int i, j; /* Loop counters */
|
||||
#ifdef SQLITE_TEST
|
||||
int tstFlags = 0; /* Value for testflags=N parameter */
|
||||
#endif
|
||||
@@ -514,10 +510,11 @@ static int csvtabConnect(
|
||||
# define CSV_DATA (azPValue[1])
|
||||
# define CSV_SCHEMA (azPValue[2])
|
||||
|
||||
|
||||
assert( sizeof(azPValue)==sizeof(azParam) );
|
||||
memset(&sRdr, 0, sizeof(sRdr));
|
||||
memset(azPValue, 0, sizeof(azPValue));
|
||||
for(i=3; i<(u64)argc; i++){
|
||||
for(i=3; i<argc; i++){
|
||||
const char *z = argv[i];
|
||||
const char *zValue;
|
||||
for(j=0; j<sizeof(azParam)/sizeof(azParam[0]); j++){
|
||||
@@ -547,10 +544,6 @@ static int csvtabConnect(
|
||||
if( nCol<=0 ){
|
||||
csv_errmsg(&sRdr, "column= value must be positive");
|
||||
goto csvtab_connect_error;
|
||||
}else if( nCol>sqlite3_limit(db,SQLITE_LIMIT_COLUMN,-1) ){
|
||||
csv_errmsg(&sRdr, "column= value too big, max %d",
|
||||
sqlite3_limit(db, SQLITE_LIMIT_COLUMN,-1));
|
||||
goto csvtab_connect_error;
|
||||
}
|
||||
}else
|
||||
{
|
||||
@@ -568,7 +561,7 @@ static int csvtabConnect(
|
||||
){
|
||||
goto csvtab_connect_error;
|
||||
}
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) goto csvtab_connect_oom;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -713,14 +706,13 @@ static int csvtabClose(sqlite3_vtab_cursor *cur){
|
||||
static int csvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
CsvTable *pTab = (CsvTable*)p;
|
||||
CsvCursor *pCur;
|
||||
sqlite3_int64 nByte = pTab->nCol;
|
||||
assert( pTab->nCol<32768 );
|
||||
nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(i64))*nByte;
|
||||
size_t nByte;
|
||||
nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(int))*pTab->nCol;
|
||||
pCur = sqlite3_malloc64( nByte );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, nByte);
|
||||
pCur->azVal = (char**)&pCur[1];
|
||||
pCur->aLen = (i64*)&pCur->azVal[pTab->nCol];
|
||||
pCur->aLen = (int*)&pCur->azVal[pTab->nCol];
|
||||
*ppCursor = &pCur->base;
|
||||
if( csv_reader_open(&pCur->rdr, pTab->zFilename, pTab->zData) ){
|
||||
csv_xfer_error(pTab, &pCur->rdr);
|
||||
|
||||
+16
-75
@@ -31,10 +31,6 @@ SQLITE_EXTENSION_INIT1
|
||||
#define IsSpace(X) isspace((unsigned char)X)
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_DECIMAL_MAX_DIGIT
|
||||
# define SQLITE_DECIMAL_MAX_DIGIT 10000000
|
||||
#endif
|
||||
|
||||
/* A decimal object */
|
||||
typedef struct Decimal Decimal;
|
||||
struct Decimal {
|
||||
@@ -73,8 +69,7 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
|
||||
int i;
|
||||
int iExp = 0;
|
||||
|
||||
if( zIn==0 ) goto new_from_text_failed;
|
||||
p = sqlite3_malloc64( sizeof(*p) );
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ) goto new_from_text_failed;
|
||||
p->sign = 0;
|
||||
p->oom = 0;
|
||||
@@ -133,10 +128,9 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
|
||||
}
|
||||
}
|
||||
if( iExp>0 ){
|
||||
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
|
||||
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
|
||||
+ (sqlite3_int64)iExp + 1 );
|
||||
if( a==0 ) goto new_from_text_failed;
|
||||
p->a = a;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
memset(p->a+p->nDigit, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
}
|
||||
@@ -154,10 +148,9 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
|
||||
}
|
||||
}
|
||||
if( iExp>0 ){
|
||||
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
|
||||
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
|
||||
+ (sqlite3_int64)iExp + 1 );
|
||||
if( a==0 ) goto new_from_text_failed;
|
||||
p->a = a;
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
memmove(p->a+iExp, p->a, p->nDigit);
|
||||
memset(p->a, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
@@ -168,7 +161,6 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
|
||||
for(i=0; i<p->nDigit && p->a[i]==0; i++){}
|
||||
if( i>=p->nDigit ) p->sign = 0;
|
||||
}
|
||||
if( p->nDigit>SQLITE_DECIMAL_MAX_DIGIT ) goto new_from_text_failed;
|
||||
return p;
|
||||
|
||||
new_from_text_failed:
|
||||
@@ -261,7 +253,7 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
|
||||
sqlite3_result_null(pCtx);
|
||||
return;
|
||||
}
|
||||
z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+8 );
|
||||
z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+4 );
|
||||
if( z==0 ){
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
return;
|
||||
@@ -299,47 +291,12 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
|
||||
sqlite3_result_text(pCtx, z, i, sqlite3_free);
|
||||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static void decimal_expand(Decimal *p, int nDigit, int nFrac);
|
||||
|
||||
/*
|
||||
** Round a decimal value to N significant digits. N must be positive.
|
||||
*/
|
||||
static void decimal_round(Decimal *p, int N){
|
||||
int i;
|
||||
int nZero; /* Number of leading zeros */
|
||||
if( N<1 ) return;
|
||||
if( p==0 ) return;
|
||||
if( p->nDigit<=N ) return;
|
||||
for(nZero=0; nZero<p->nDigit && p->a[nZero]==0; nZero++){}
|
||||
N += nZero;
|
||||
if( p->nDigit<=N ) return;
|
||||
if( p->a[N]>=5 ){
|
||||
/* If all leading digits are 9, increase the number of digits
|
||||
** by adding a new 0 to the front */
|
||||
for(i=0; i<N && p->a[i]==9; i++){}
|
||||
if( i==N ){
|
||||
decimal_expand(p, p->nDigit+1, 0);
|
||||
if( p->oom ) return;
|
||||
}
|
||||
|
||||
/* Do the rounding */
|
||||
p->a[N-1]++;
|
||||
for(i=N-1; i>0 && p->a[i]>9; i--){
|
||||
p->a[i] = 0;
|
||||
p->a[i-1]++;
|
||||
}
|
||||
assert( p->a[0]<=9 );
|
||||
}
|
||||
memset(&p->a[N], 0, p->nDigit - N);
|
||||
}
|
||||
|
||||
/*
|
||||
** Make the given Decimal the result in an format similar to '%+#e'.
|
||||
** In other words, show exponential notation with leading and trailing
|
||||
** zeros omitted.
|
||||
*/
|
||||
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
|
||||
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
|
||||
char *z; /* The output buffer */
|
||||
int i; /* Loop counter */
|
||||
int nZero; /* Number of leading zeros */
|
||||
@@ -357,8 +314,7 @@ static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
|
||||
sqlite3_result_null(pCtx);
|
||||
return;
|
||||
}
|
||||
if( N<1 ) N = 0;
|
||||
for(nDigit=p->nDigit; nDigit>N && p->a[nDigit-1]==0; nDigit--){}
|
||||
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
|
||||
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
|
||||
nFrac = p->nFrac + (nDigit - p->nDigit);
|
||||
nDigit -= nZero;
|
||||
@@ -474,18 +430,15 @@ cmp_done:
|
||||
static void decimal_expand(Decimal *p, int nDigit, int nFrac){
|
||||
int nAddSig;
|
||||
int nAddFrac;
|
||||
signed char *a;
|
||||
if( p==0 ) return;
|
||||
nAddFrac = nFrac - p->nFrac;
|
||||
nAddSig = (nDigit - p->nDigit) - nAddFrac;
|
||||
if( nAddFrac==0 && nAddSig==0 ) return;
|
||||
if( nDigit+1>SQLITE_DECIMAL_MAX_DIGIT ){ p->oom = 1; return; }
|
||||
a = sqlite3_realloc64(p->a, nDigit+1);
|
||||
if( a==0 ){
|
||||
p->a = sqlite3_realloc64(p->a, nDigit+1);
|
||||
if( p->a==0 ){
|
||||
p->oom = 1;
|
||||
return;
|
||||
}
|
||||
p->a = a;
|
||||
if( nAddSig ){
|
||||
memmove(p->a+nAddSig, p->a, p->nDigit);
|
||||
memset(p->a, 0, nAddSig);
|
||||
@@ -580,18 +533,14 @@ static void decimalMul(Decimal *pA, Decimal *pB){
|
||||
signed char *acc = 0;
|
||||
int i, j, k;
|
||||
int minFrac;
|
||||
sqlite3_int64 sumDigit;
|
||||
|
||||
if( pA==0 || pA->oom || pA->isNull
|
||||
|| pB==0 || pB->oom || pB->isNull
|
||||
){
|
||||
goto mul_end;
|
||||
}
|
||||
sumDigit = pA->nDigit;
|
||||
sumDigit += pB->nDigit;
|
||||
sumDigit += 2;
|
||||
if( sumDigit>SQLITE_DECIMAL_MAX_DIGIT ){ pA->oom = 1; return; }
|
||||
acc = sqlite3_malloc64( sumDigit );
|
||||
acc = sqlite3_malloc64( (sqlite3_int64)pA->nDigit +
|
||||
(sqlite3_int64)pB->nDigit + 2 );
|
||||
if( acc==0 ){
|
||||
pA->oom = 1;
|
||||
goto mul_end;
|
||||
@@ -728,16 +677,10 @@ static void decimalFunc(
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *p = decimal_new(context, argv[0], 0);
|
||||
int N;
|
||||
if( argc==2 ){
|
||||
N = sqlite3_value_int(argv[1]);
|
||||
if( N>0 ) decimal_round(p, N);
|
||||
}else{
|
||||
N = 0;
|
||||
}
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( p ){
|
||||
if( sqlite3_user_data(context)!=0 ){
|
||||
decimal_result_sci(context, p, N);
|
||||
decimal_result_sci(context, p);
|
||||
}else{
|
||||
decimal_result(context, p);
|
||||
}
|
||||
@@ -823,7 +766,7 @@ static void decimalSumStep(
|
||||
if( p==0 ) return;
|
||||
if( !p->isInit ){
|
||||
p->isInit = 1;
|
||||
p->a = sqlite3_malloc64(2);
|
||||
p->a = sqlite3_malloc(2);
|
||||
if( p->a==0 ){
|
||||
p->oom = 1;
|
||||
}else{
|
||||
@@ -907,7 +850,7 @@ static void decimalPow2Func(
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
|
||||
Decimal *pA = decimalPow2(sqlite3_value_int(argv[0]));
|
||||
decimal_result_sci(context, pA, 0);
|
||||
decimal_result_sci(context, pA);
|
||||
decimal_free(pA);
|
||||
}
|
||||
}
|
||||
@@ -928,9 +871,7 @@ int sqlite3_decimal_init(
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "decimal", 1, 0, decimalFunc },
|
||||
{ "decimal", 2, 0, decimalFunc },
|
||||
{ "decimal_exp", 1, 1, decimalFunc },
|
||||
{ "decimal_exp", 2, 1, decimalFunc },
|
||||
{ "decimal_cmp", 2, 0, decimalCmpFunc },
|
||||
{ "decimal_add", 2, 0, decimalAddFunc },
|
||||
{ "decimal_sub", 2, 0, decimalSubFunc },
|
||||
|
||||
@@ -1,847 +0,0 @@
|
||||
/*
|
||||
** 2026-04-13
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This extension implements an SQL function:
|
||||
**
|
||||
** diskused(X)
|
||||
**
|
||||
** Where X is the schema name (typically 'main'). The output is text
|
||||
** that describes how much filesystem space the various tables and indexes
|
||||
** of the database consume.
|
||||
**
|
||||
** This function is a replacement for the (now deprecated)
|
||||
** "sqlite3_analyzer" utility program. This function is built
|
||||
** into the CLI and is used to implement the ".diskused" command there.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
** State information for the analysis
|
||||
*/
|
||||
typedef struct DiskUsed DiskUsed;
|
||||
struct DiskUsed {
|
||||
sqlite3 *db; /* Database connection */
|
||||
sqlite3_context *context; /* SQL function context */
|
||||
sqlite3_str *pOut; /* Write output here */
|
||||
char *zSU; /* Name of the temp.space_used table */
|
||||
const char *zSchema; /* Schema to be analyzed */
|
||||
};
|
||||
|
||||
/*
|
||||
** Free all resources that the DiskUsed object references and
|
||||
** reset the DiskUsed object.
|
||||
**
|
||||
** Call this routine multiple times on the same DiskUsed object
|
||||
** is a harmless no-op, as long as the memory for the object itself
|
||||
** has not been freed.
|
||||
*/
|
||||
static void diskusedReset(DiskUsed *p){
|
||||
if( p->zSU ){
|
||||
char *zSql = sqlite3_mprintf("DROP TABLE temp.%s;", p->zSU);
|
||||
if( zSql ){
|
||||
sqlite3_exec(p->db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
}
|
||||
sqlite3_str_free(p->pOut);
|
||||
sqlite3_free(p->zSU);
|
||||
memset(p, 0, sizeof(*p));
|
||||
}
|
||||
|
||||
/*
|
||||
** Report an error using formatted text. If zFormat==NULL then report
|
||||
** an OOM error.
|
||||
*/
|
||||
static void diskusedError(DiskUsed *p, const char *zFormat, ...){
|
||||
char *zErr;
|
||||
if( zFormat ){
|
||||
va_list ap;
|
||||
va_start(ap, zFormat);
|
||||
zErr = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
}else{
|
||||
zErr = 0;
|
||||
}
|
||||
if( zErr==0 ){
|
||||
sqlite3_result_error_nomem(p->context);
|
||||
}else{
|
||||
sqlite3_result_error(p->context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
diskusedReset(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare and return an SQL statement.
|
||||
*/
|
||||
static sqlite3_stmt *diskusedVPrep(DiskUsed *p, const char *zFmt, va_list ap){
|
||||
char *zSql;
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
zSql = sqlite3_vmprintf(zFmt, ap);
|
||||
if( zSql==0 ){ diskusedError(p,0); return 0; }
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
if( rc ){
|
||||
diskusedError(p, "SQL parse error: %s\nOriginal SQL: %s",
|
||||
sqlite3_errmsg(p->db), zSql);
|
||||
sqlite3_finalize(pStmt);
|
||||
diskusedReset(p);
|
||||
pStmt = 0;
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
return pStmt;
|
||||
}
|
||||
static sqlite3_stmt *diskusedPrepare(DiskUsed *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
va_start(ap, zFormat);
|
||||
pStmt = diskusedVPrep(p,zFormat,ap);
|
||||
va_end(ap);
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
/*
|
||||
** If rc is something other than SQLITE_DONE or SQLITE_OK, then report
|
||||
** an error and return true.
|
||||
**
|
||||
** If rc is SQLITE_DONE or SQLITE_OK, then return false.
|
||||
**
|
||||
** The prepared statement is closed in either case.
|
||||
*/
|
||||
static int diskusedStmtFinish(DiskUsed *p, int rc, sqlite3_stmt *pStmt){
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
if( rc!=SQLITE_OK || (rc = sqlite3_reset(pStmt))!=SQLITE_OK ){
|
||||
diskusedError(p, "SQL run-time error: %s\nOriginal SQL: %s",
|
||||
sqlite3_errmsg(p->db), sqlite3_sql(pStmt));
|
||||
diskusedReset(p);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run SQL. Return the number of errors.
|
||||
*/
|
||||
static int diskusedSql(DiskUsed *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
va_start(ap, zFormat);
|
||||
pStmt = diskusedVPrep(p,zFormat,ap);
|
||||
va_end(ap);
|
||||
if( pStmt==0 ) return 1;
|
||||
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){}
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
diskusedError(p, "SQL run-time error: %s\nOriginal SQL: %s",
|
||||
sqlite3_errmsg(p->db), sqlite3_sql(pStmt));
|
||||
diskusedReset(p);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run an SQL query that returns an integer. Write that integer
|
||||
** into *piRes. Return the number of errors.
|
||||
*/
|
||||
static int diskusedSqlInt(
|
||||
DiskUsed *p,
|
||||
sqlite3_int64 *piRes,
|
||||
const char *zFormat, ...
|
||||
){
|
||||
va_list ap;
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
va_start(ap, zFormat);
|
||||
pStmt = diskusedVPrep(p,zFormat,ap);
|
||||
va_end(ap);
|
||||
if( pStmt==0 ) return 1;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
*piRes = sqlite3_column_int64(pStmt, 0);
|
||||
rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
if( p->db ){
|
||||
/* p->db is NULL if there was some prior error */
|
||||
diskusedError(p, "SQL run-time error: %s\nOriginal SQL: %s",
|
||||
sqlite3_errmsg(p->db), sqlite3_sql(pStmt));
|
||||
}
|
||||
diskusedReset(p);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add to the output a title line that contains the text determined
|
||||
** by the format string. If the output is initially empty, begin
|
||||
** the title line with "/" so that it forms the beginning of a C-style
|
||||
** comment. Otherwise begin with a new-line. Always finish with a
|
||||
** newline.
|
||||
*/
|
||||
static void diskusedTitle(DiskUsed *p, const char *zFormat, ...){
|
||||
char *zFirst;
|
||||
char *zTitle;
|
||||
size_t nTitle;
|
||||
va_list ap;
|
||||
va_start(ap, zFormat);
|
||||
zTitle = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
if( zTitle==0 ){
|
||||
diskusedError(p, 0);
|
||||
return;
|
||||
}
|
||||
zFirst = sqlite3_str_length(p->pOut)==0 ? "/" : "\n*";
|
||||
nTitle = strlen(zTitle);
|
||||
if( nTitle>=75 ){
|
||||
sqlite3_str_appendf(p->pOut, "%s** %z\n\n", zFirst, zTitle);
|
||||
}else{
|
||||
int nExtra = 74 - (int)nTitle;
|
||||
sqlite3_str_appendf(p->pOut, "%s** %z %.*c\n\n", zFirst, zTitle,
|
||||
nExtra, '*');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an output line that begins with the zDesc text extended out to
|
||||
** 50 columns with "." characters, and followed by whatever text is
|
||||
** described by zFormat.
|
||||
*/
|
||||
static void diskusedLine(
|
||||
DiskUsed *p, /* DiskUsed context */
|
||||
const char *zDesc, /* Description */
|
||||
const char *zFormat, /* Argument to the description */
|
||||
...
|
||||
){
|
||||
char *zTxt;
|
||||
size_t nDesc;
|
||||
va_list ap;
|
||||
va_start(ap, zFormat);
|
||||
zTxt = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
if( zTxt==0 ){
|
||||
diskusedError(p, 0);
|
||||
return;
|
||||
}
|
||||
nDesc = zDesc ? strlen(zDesc) : 0;
|
||||
if( nDesc>=50 ){
|
||||
sqlite3_str_appendf(p->pOut, "%s %z", zDesc, zTxt);
|
||||
}else{
|
||||
int nExtra = 50 - (int)nDesc;
|
||||
sqlite3_str_appendf(p->pOut, "%s%.*c %z", zDesc, nExtra, '.', zTxt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a percentage into the output. The number written should show
|
||||
** two or three significant digits, with the decimal point being the fourth
|
||||
** character.
|
||||
*/
|
||||
static void diskusedPercent(DiskUsed *p, double r){
|
||||
char zNum[100];
|
||||
char *zDP;
|
||||
int nLeadingDigit;
|
||||
int sz;
|
||||
sqlite3_snprintf(sizeof(zNum)-5, zNum, r>=10.0 ? "%.3g" :"%.2g", r);
|
||||
sz = (int)strlen(zNum);
|
||||
zDP = strchr(zNum, '.');
|
||||
if( zDP==0 ){
|
||||
memcpy(zNum+sz,".0",3);
|
||||
nLeadingDigit = sz;
|
||||
sz += 2;
|
||||
}else{
|
||||
nLeadingDigit = (int)(zDP - zNum);
|
||||
}
|
||||
if( nLeadingDigit<3 ){
|
||||
sqlite3_str_appendchar(p->pOut, 3-nLeadingDigit, ' ');
|
||||
}
|
||||
sqlite3_str_append(p->pOut, zNum, sz);
|
||||
sqlite3_str_append(p->pOut, "%\n", 2);
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a subreport on a subset of tables and/or indexes.
|
||||
**
|
||||
** The title if the subreport is given by zTitle. zWhere is
|
||||
** a boolean expression that can go in the WHERE clause to select
|
||||
** the relevant rows of the s.zSU table.
|
||||
*/
|
||||
static int diskusedSubreport(
|
||||
DiskUsed *p, /* DiskUsed context */
|
||||
char *zTitle, /* Title for this subreport */
|
||||
char *zWhere, /* WHERE clause for this subreport */
|
||||
sqlite3_int64 pgsz, /* Database page size */
|
||||
sqlite3_int64 nPage /* Number of pages in entire database */
|
||||
){
|
||||
sqlite3_stmt *pStmt; /* Statement to query p->zSU */
|
||||
sqlite3_int64 nentry; /* Number of btree entires */
|
||||
sqlite3_int64 payload; /* Payload in bytes */
|
||||
sqlite3_int64 ovfl_payload; /* overflow payload in bytes */
|
||||
sqlite3_int64 mx_payload; /* largest individual payload */
|
||||
sqlite3_int64 ovfl_cnt; /* Number entries using overflow */
|
||||
sqlite3_int64 leaf_pages; /* Leaf pages */
|
||||
sqlite3_int64 int_pages; /* internal pages */
|
||||
sqlite3_int64 ovfl_pages; /* overflow pages */
|
||||
sqlite3_int64 leaf_unused; /* unused bytes on leaf pages */
|
||||
sqlite3_int64 int_unused; /* unused bytes on internal pages */
|
||||
sqlite3_int64 ovfl_unused; /* unused bytes on overflow pages */
|
||||
sqlite3_int64 int_cell; /* B-tree entries on internal pages */
|
||||
sqlite3_int64 depth; /* btree depth */
|
||||
sqlite3_int64 cnt; /* Number of s.zSU entries that match */
|
||||
sqlite3_int64 storage; /* Total bytes */
|
||||
sqlite3_int64 total_pages; /* Total page count */
|
||||
sqlite3_int64 total_unused; /* Total unused bytes */
|
||||
sqlite3_int64 total_meta; /* Total metadata */
|
||||
int rc;
|
||||
|
||||
if( zTitle==0 || zWhere==0 ){
|
||||
diskusedError(p, 0);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pStmt = diskusedPrepare(p,
|
||||
"SELECT\n"
|
||||
" sum(if(is_without_rowid OR is_index,nentry,leaf_entries)),\n" /* 0 */
|
||||
" sum(payload),\n" /* 1 */
|
||||
" sum(ovfl_payload),\n" /* 2 */
|
||||
" max(mx_payload),\n" /* 3 */
|
||||
" sum(ovfl_cnt),\n" /* 4 */
|
||||
" sum(leaf_pages),\n" /* 5 */
|
||||
" sum(int_pages),\n" /* 6 */
|
||||
" sum(ovfl_pages),\n" /* 7 */
|
||||
" sum(leaf_unused),\n" /* 8 */
|
||||
" sum(int_unused),\n" /* 9 */
|
||||
" sum(ovfl_unused),\n" /* 10 */
|
||||
" max(depth),\n" /* 11 */
|
||||
" count(*),\n" /* 12 */
|
||||
" sum(int_entries)\n" /* 13 */
|
||||
" FROM temp.%s WHERE %s",
|
||||
p->zSU, zWhere);
|
||||
if( pStmt==0 ) return 1;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
diskusedTitle(p, "%s", zTitle);
|
||||
|
||||
nentry = sqlite3_column_int64(pStmt, 0);
|
||||
payload = sqlite3_column_int64(pStmt, 1);
|
||||
ovfl_payload = sqlite3_column_int64(pStmt, 2);
|
||||
mx_payload = sqlite3_column_int64(pStmt, 3);
|
||||
ovfl_cnt = sqlite3_column_int64(pStmt, 4);
|
||||
leaf_pages = sqlite3_column_int64(pStmt, 5);
|
||||
int_pages = sqlite3_column_int64(pStmt, 6);
|
||||
ovfl_pages = sqlite3_column_int64(pStmt, 7);
|
||||
leaf_unused = sqlite3_column_int64(pStmt, 8);
|
||||
int_unused = sqlite3_column_int64(pStmt, 9);
|
||||
ovfl_unused = sqlite3_column_int64(pStmt, 10);
|
||||
depth = sqlite3_column_int64(pStmt, 11);
|
||||
cnt = sqlite3_column_int64(pStmt, 12);
|
||||
int_cell = sqlite3_column_int64(pStmt, 13);
|
||||
rc = SQLITE_DONE;
|
||||
|
||||
total_pages = leaf_pages + int_pages + ovfl_pages;
|
||||
diskusedLine(p, "Percentage of total database", "%.3g%%\n",
|
||||
(total_pages*100.0)/(double)nPage);
|
||||
diskusedLine(p, "Number of entries", "%lld\n", nentry);
|
||||
storage = total_pages*pgsz;
|
||||
diskusedLine(p, "Bytes of storage consumed", "%lld\n", storage);
|
||||
diskusedLine(p, "Bytes of payload", "%-11lld ", payload);
|
||||
diskusedPercent(p, payload*100.0/(double)storage);
|
||||
if( ovfl_cnt>0 ){
|
||||
diskusedLine(p, "Bytes of payload in overflow","%-11lld ",ovfl_payload);
|
||||
diskusedPercent(p, ovfl_payload*100.0/(double)payload);
|
||||
}
|
||||
total_unused = leaf_unused + int_unused + ovfl_unused;
|
||||
total_meta = storage - payload - total_unused;
|
||||
diskusedLine(p, "Bytes of metadata","%-11lld ", total_meta);
|
||||
diskusedPercent(p, total_meta*100.0/(double)storage);
|
||||
if( cnt==1 ){
|
||||
diskusedLine(p, "B-tree depth", "%lld\n", depth);
|
||||
if( int_cell>1 ){
|
||||
diskusedLine(p, "Average fanout", "%.1f\n",
|
||||
(double)(int_cell+int_pages)/(double)int_pages);
|
||||
}
|
||||
}
|
||||
if( nentry>0 ){
|
||||
diskusedLine(p, "Average payload per entry", "%.1f\n",
|
||||
(double)payload/(double)nentry);
|
||||
diskusedLine(p, "Average unused bytes per entry", "%.1f\n",
|
||||
(double)total_unused/(double)nentry);
|
||||
diskusedLine(p, "Average metadata per entry", "%.1f\n",
|
||||
(double)total_meta/(double)nentry);
|
||||
}
|
||||
diskusedLine(p, "Maximum single-entry payload", "%lld\n", mx_payload);
|
||||
if( nentry>0 ){
|
||||
diskusedLine(p, "Entries that use overflow", "%-11lld ", ovfl_cnt);
|
||||
diskusedPercent(p, ovfl_cnt*100.0/(double)nentry);
|
||||
}
|
||||
if( int_pages>0 ){
|
||||
diskusedLine(p, "Index pages used", "%lld\n", int_pages);
|
||||
}
|
||||
diskusedLine(p, "Primary pages used", "%lld\n", leaf_pages);
|
||||
if( ovfl_cnt ){
|
||||
diskusedLine(p, "Overflow pages used", "%lld\n", ovfl_pages);
|
||||
}
|
||||
diskusedLine(p, "Total pages used", "%lld\n", total_pages);
|
||||
if( int_pages>0 ){
|
||||
diskusedLine(p, "Unused bytes on index pages", "%lld\n", int_unused);
|
||||
}
|
||||
diskusedLine(p, "Unused bytes on primary pages", "%lld\n", leaf_unused);
|
||||
if( ovfl_cnt ){
|
||||
diskusedLine(p, "Unused bytes on overflow pages", "%lld\n", ovfl_unused);
|
||||
}
|
||||
diskusedLine(p, "Unused bytes on all pages", "%-11lld ", total_unused);
|
||||
diskusedPercent(p, total_unused*100.0/(double)storage);
|
||||
}
|
||||
return diskusedStmtFinish(p, rc, pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: diskused(SCHEMA)
|
||||
**
|
||||
** Analyze the database schema named in the argument. Return text
|
||||
** containing the space utilization stats.
|
||||
*/
|
||||
static void diskusedFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt;
|
||||
int n;
|
||||
sqlite3_int64 ii;
|
||||
sqlite3_int64 pgsz;
|
||||
sqlite3_int64 nPage;
|
||||
sqlite3_int64 nPageInUse;
|
||||
sqlite3_int64 nFreeList;
|
||||
sqlite3_int64 nIndex;
|
||||
sqlite3_int64 nWORowid;
|
||||
DiskUsed s;
|
||||
sqlite3_uint64 r[2];
|
||||
|
||||
(void)argc;
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.db = sqlite3_context_db_handle(context);
|
||||
s.context = context;
|
||||
s.pOut = sqlite3_str_new(0);
|
||||
if( sqlite3_str_errcode(s.pOut) ){
|
||||
diskusedError(&s, 0);
|
||||
return;
|
||||
}
|
||||
s.zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( s.zSchema==0 ){
|
||||
s.zSchema = "main";
|
||||
}else if( sqlite3_strlike("temp",s.zSchema,0)==0 ){
|
||||
diskusedReset(&s);
|
||||
sqlite3_result_text(context, "cannot analyze \"temp\"",-1,SQLITE_STATIC);
|
||||
return;
|
||||
}
|
||||
ii = 0;
|
||||
rc = diskusedSqlInt(&s,&ii,"SELECT 1 FROM pragma_database_list"
|
||||
" WHERE name=%Q COLLATE nocase",s.zSchema);
|
||||
if( rc || ii==0 ){
|
||||
diskusedReset(&s);
|
||||
sqlite3_result_text(context,"no such database",-1,SQLITE_STATIC);
|
||||
return;
|
||||
}
|
||||
sqlite3_randomness(sizeof(r), &r);
|
||||
s.zSU = sqlite3_mprintf("diskused%016llx%016llx", r[0], r[1]);
|
||||
if( s.zSU==0 ){ diskusedError(&s, 0); return; }
|
||||
|
||||
/* The s.zSU table contains the data used for the analysis.
|
||||
** The table name contains 128-bits of randomness to avoid
|
||||
** collisions with preexisting tables in temp.
|
||||
*/
|
||||
rc = diskusedSql(&s,
|
||||
"CREATE TABLE temp.%s(\n"
|
||||
" name text, -- A table or index\n"
|
||||
" tblname text, -- Table that owns name\n"
|
||||
" is_index boolean, -- TRUE if it is an index\n"
|
||||
" is_without_rowid boolean, -- TRUE if WITHOUT ROWID table\n"
|
||||
" nentry int, -- Number of entries in the BTree\n"
|
||||
" leaf_entries int, -- Number of leaf entries\n"
|
||||
" depth int, -- Depth of the b-tree\n"
|
||||
" payload int, -- Total data stored in this table/index\n"
|
||||
" ovfl_payload int, -- Total data stored on overflow pages\n"
|
||||
" ovfl_cnt int, -- Number of entries that use overflow\n"
|
||||
" mx_payload int, -- Maximum payload size\n"
|
||||
" int_pages int, -- Interior pages used\n"
|
||||
" leaf_pages int, -- Leaf pages used\n"
|
||||
" ovfl_pages int, -- Overflow pages used\n"
|
||||
" int_unused int, -- Unused bytes on interior pages\n"
|
||||
" leaf_unused int, -- Unused bytes on primary pages\n"
|
||||
" ovfl_unused int, -- Unused bytes on overflow pages\n"
|
||||
" int_entries int -- Btree cells on internal pages\n"
|
||||
");",
|
||||
s.zSU
|
||||
);
|
||||
if( rc ) return;
|
||||
|
||||
/* Populate the s.zSU table
|
||||
*/
|
||||
rc = diskusedSql(&s,
|
||||
"WITH\n"
|
||||
" allidx(idxname) AS (\n"
|
||||
" SELECT name FROM \"%w\".sqlite_schema WHERE type='index'\n"
|
||||
" ),\n"
|
||||
" allobj(allname,tblname,isidx,isworowid) AS (\n"
|
||||
" SELECT 'sqlite_schema',\n"
|
||||
" 'sqlite_schema',\n"
|
||||
" 0,\n"
|
||||
" 0\n"
|
||||
" UNION ALL\n"
|
||||
" SELECT name,\n"
|
||||
" tbl_name,\n"
|
||||
" type='index',\n"
|
||||
" EXISTS(SELECT 1\n"
|
||||
" FROM pragma_index_list(sqlite_schema.name,%Q)\n"
|
||||
" WHERE pragma_index_list.origin='pk'\n"
|
||||
" AND pragma_index_list.name NOT IN allidx)\n"
|
||||
" FROM \"%w\".sqlite_schema\n"
|
||||
" )\n"
|
||||
"INSERT INTO temp.%s\n"
|
||||
" SELECT\n"
|
||||
" allname,\n"
|
||||
" tblname,\n"
|
||||
" isidx,\n"
|
||||
" isworowid,\n"
|
||||
" sum(ncell),\n"
|
||||
" sum((pagetype='leaf')*ncell),\n"
|
||||
" max((length(if(path GLOB '*+*','',path))+3)/4),\n"
|
||||
" sum(payload),\n"
|
||||
" sum((pagetype='overflow')*payload),\n"
|
||||
" sum(path GLOB '*+000000'),\n"
|
||||
" max(mx_payload),\n"
|
||||
" sum(pagetype='internal'),\n"
|
||||
" sum(pagetype='leaf'),\n"
|
||||
" sum(pagetype='overflow'),\n"
|
||||
" sum((pagetype='internal')*unused),\n"
|
||||
" sum((pagetype='leaf')*unused),\n"
|
||||
" sum((pagetype='overflow')*unused),\n"
|
||||
" sum(if(pagetype='internal',ncell))\n"
|
||||
" FROM allobj CROSS JOIN dbstat(%Q) \n"
|
||||
" WHERE dbstat.name=allobj.allname\n"
|
||||
" GROUP BY allname;\n",
|
||||
s.zSchema, /* %w.sqlite_schema -- in allidx */
|
||||
s.zSchema, /* pragma_index_list(...,%Q) */
|
||||
s.zSchema, /* %w.sqlite_schema */
|
||||
s.zSU, /* INTO temp.%s */
|
||||
s.zSchema /* JOIN dbstat(%Q) */
|
||||
);
|
||||
if( rc ) return;
|
||||
|
||||
nPage = 0;
|
||||
rc = diskusedSqlInt(&s, &nPage, "PRAGMA \"%w\".page_count", s.zSchema);
|
||||
if( rc ) return;
|
||||
if( nPage<=0 ){
|
||||
/* Very brief reply for an empty database */
|
||||
diskusedReset(&s);
|
||||
sqlite3_result_text(context, "empty database", -1, SQLITE_STATIC);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Begin generating the report */
|
||||
diskusedTitle(&s, "Database storage utilization report");
|
||||
pgsz = 0;
|
||||
rc = diskusedSqlInt(&s, &pgsz, "PRAGMA \"%w\".page_size", s.zSchema);
|
||||
if( rc ) return;
|
||||
diskusedLine(&s, "Page size in bytes","%lld\n",pgsz);
|
||||
diskusedLine(&s, "Pages in the database", "%lld\n", nPage);
|
||||
|
||||
nPageInUse = 0;
|
||||
rc = diskusedSqlInt(&s, &nPageInUse,
|
||||
"SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM temp.%s", s.zSU);
|
||||
if( rc ) return;
|
||||
diskusedLine(&s, "Pages that store data", "%-11lld ", nPageInUse);
|
||||
diskusedPercent(&s, (nPageInUse*100.0)/(double)nPage);
|
||||
|
||||
nFreeList = 0;
|
||||
rc = diskusedSqlInt(&s, &nFreeList, "PRAGMA \"%w\".freelist_count",s.zSchema);
|
||||
if( rc ) return;
|
||||
diskusedLine(&s, "Pages on the freelist", "%-11lld ", nFreeList);
|
||||
diskusedPercent(&s, (nFreeList*100.0)/(double)nPage);
|
||||
|
||||
ii = 0;
|
||||
rc = diskusedSqlInt(&s, &ii, "PRAGMA \"%w\".auto_vacuum", s.zSchema);
|
||||
if( rc ) return;
|
||||
if( ii==0 || nPage<=1 ){
|
||||
ii = 0;
|
||||
}else{
|
||||
double rPtrsPerPage = pgsz/5;
|
||||
double rAvPage = (nPage-1.0)/(rPtrsPerPage+1.0);
|
||||
ii = (sqlite3_int64)ceil(rAvPage);
|
||||
}
|
||||
diskusedLine(&s, "Pages of auto-vacuum overhead", "%-11lld ", ii);
|
||||
diskusedPercent(&s, (ii*100.0)/(double)nPage);
|
||||
|
||||
ii = 0;
|
||||
rc = diskusedSqlInt(&s, &ii,
|
||||
"SELECT count(*)+1 FROM \"%w\".sqlite_schema WHERE type='table'",
|
||||
s.zSchema);
|
||||
if( rc ) return;
|
||||
diskusedLine(&s, "Number of tables", "%lld\n", ii);
|
||||
nWORowid = 0;
|
||||
rc = diskusedSqlInt(&s, &nWORowid,
|
||||
"SELECT count(*) FROM \"%w\".pragma_table_list WHERE wr",
|
||||
s.zSchema);
|
||||
if( rc ) return;
|
||||
if( nWORowid>0 ){
|
||||
diskusedLine(&s, "Number of WITHOUT ROWID tables", "%lld\n", nWORowid);
|
||||
diskusedLine(&s, "Number of rowid tables", "%lld\n", ii - nWORowid);
|
||||
}
|
||||
nIndex = 0;
|
||||
rc = diskusedSqlInt(&s, &nIndex,
|
||||
"SELECT count(*) FROM \"%w\".sqlite_schema WHERE type='index'",
|
||||
s.zSchema);
|
||||
if( rc ) return;
|
||||
diskusedLine(&s, "Number of indexes", "%lld\n", nIndex);
|
||||
ii = 0;
|
||||
rc = diskusedSqlInt(&s, &ii,
|
||||
"SELECT count(*) FROM \"%w\".sqlite_schema"
|
||||
" WHERE name GLOB 'sqlite_autoindex_*' AND type='index'",
|
||||
s.zSchema);
|
||||
if( rc ) return;
|
||||
diskusedLine(&s, "Number of defined indexes", "%lld\n", nIndex - ii);
|
||||
diskusedLine(&s, "Number of implied indexes", "%lld\n", ii);
|
||||
diskusedLine(&s, "Size of the database in bytes", "%lld\n", pgsz*nPage);
|
||||
ii = 0;
|
||||
rc = diskusedSqlInt(&s, &ii,
|
||||
"SELECT sum(payload) FROM temp.%s"
|
||||
" WHERE NOT is_index AND name NOT LIKE 'sqlite_schema'",
|
||||
s.zSU);
|
||||
if( rc ) return;
|
||||
diskusedLine(&s, "Bytes of payload", "%-11lld ", ii);
|
||||
diskusedPercent(&s, ii*100.0/(double)(pgsz*nPage));
|
||||
|
||||
diskusedTitle(&s, "Page counts for all tables with their indexes");
|
||||
pStmt = diskusedPrepare(&s,
|
||||
"SELECT upper(tblname),\n"
|
||||
" sum(int_pages+leaf_pages+ovfl_pages)\n"
|
||||
" FROM temp.%s\n"
|
||||
" WHERE tblname IS NOT NULL\n"
|
||||
" GROUP BY 1\n"
|
||||
" ORDER BY 2 DESC, 1;",
|
||||
s.zSU);
|
||||
if( pStmt==0 ) return;
|
||||
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
|
||||
sqlite3_int64 nn = sqlite3_column_int64(pStmt,1);
|
||||
diskusedLine(&s, (const char*)sqlite3_column_text(pStmt,0), "%-11lld ", nn);
|
||||
diskusedPercent(&s, (nn*100.0)/(double)nPage);
|
||||
}
|
||||
if( diskusedStmtFinish(&s, rc, pStmt) ) return;
|
||||
|
||||
diskusedTitle(&s, "Page counts for all tables and indexes separately");
|
||||
pStmt = diskusedPrepare(&s,
|
||||
"SELECT upper(name),\n"
|
||||
" sum(int_pages+leaf_pages+ovfl_pages)\n"
|
||||
" FROM temp.%s\n"
|
||||
" WHERE name IS NOT NULL\n"
|
||||
" GROUP BY 1\n"
|
||||
" ORDER BY 2 DESC, 1;",
|
||||
s.zSU);
|
||||
if( pStmt==0 ) return;
|
||||
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
|
||||
sqlite3_int64 nn = sqlite3_column_int64(pStmt,1);
|
||||
diskusedLine(&s, (const char*)sqlite3_column_text(pStmt,0), "%-11lld ", nn);
|
||||
diskusedPercent(&s, (nn*100.0)/(double)nPage);
|
||||
}
|
||||
if( diskusedStmtFinish(&s, rc, pStmt) ) return;
|
||||
|
||||
rc = diskusedSubreport(&s, "All tables and indexes", "1", pgsz, nPage);
|
||||
if( rc ) return;
|
||||
rc = diskusedSubreport(&s, "All tables", "NOT is_index", pgsz, nPage);
|
||||
if( rc ) return;
|
||||
if( nWORowid>0 ){
|
||||
rc = diskusedSubreport(&s, "All WITHOUT ROWID tables", "is_without_rowid",
|
||||
pgsz, nPage);
|
||||
if( rc ) return;
|
||||
rc = diskusedSubreport(&s, "All rowid tables",
|
||||
"NOT is_without_rowid AND NOT is_index",
|
||||
pgsz, nPage);
|
||||
if( rc ) return;
|
||||
}
|
||||
rc = diskusedSubreport(&s, "All indexes", "is_index", pgsz, nPage);
|
||||
if( rc ) return;
|
||||
|
||||
pStmt = diskusedPrepare(&s,
|
||||
"SELECT upper(tblname), tblname, sum(is_index) FROM temp.%s"
|
||||
" GROUP BY 1 ORDER BY 1",
|
||||
s.zSU);
|
||||
if( pStmt==0 ) return;
|
||||
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
|
||||
const char *zUpper = (const char*)sqlite3_column_text(pStmt, 0);
|
||||
const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
|
||||
int nSubIndex = sqlite3_column_int(pStmt, 2);
|
||||
if( nSubIndex==0 ){
|
||||
char *zTitle = sqlite3_mprintf("Table %s", zUpper);
|
||||
char *zWhere = sqlite3_mprintf("name=%Q", zName);
|
||||
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
|
||||
sqlite3_free(zTitle);
|
||||
sqlite3_free(zWhere);
|
||||
if( rc ) break;
|
||||
}else{
|
||||
sqlite3_stmt *pS2;
|
||||
char *zTitle = sqlite3_mprintf("Table %s and all its indexes", zUpper);
|
||||
char *zWhere = sqlite3_mprintf("tblname=%Q", zName);
|
||||
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
|
||||
sqlite3_free(zTitle);
|
||||
sqlite3_free(zWhere);
|
||||
if( rc ) break;
|
||||
zTitle = sqlite3_mprintf("Table %s w/o any indexes", zUpper);
|
||||
zWhere = sqlite3_mprintf("name=%Q", zName);
|
||||
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
|
||||
sqlite3_free(zTitle);
|
||||
sqlite3_free(zWhere);
|
||||
if( rc ) break;
|
||||
if( nSubIndex>1 ){
|
||||
zTitle = sqlite3_mprintf("All indexes of table %s", zUpper);
|
||||
zWhere = sqlite3_mprintf("tblname=%Q AND is_index", zName);
|
||||
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
|
||||
sqlite3_free(zTitle);
|
||||
sqlite3_free(zWhere);
|
||||
if( rc ) break;
|
||||
}
|
||||
pS2 = diskusedPrepare(&s,
|
||||
"SELECT name, upper(name) FROM temp.%s"
|
||||
" WHERE is_index AND tblname=%Q",
|
||||
s.zSU, zName);
|
||||
if( pS2==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
while( (rc = sqlite3_step(pS2))==SQLITE_ROW ){
|
||||
const char *zU = (const char*)sqlite3_column_text(pS2, 1);
|
||||
const char *zN = (const char*)sqlite3_column_text(pS2, 0);
|
||||
zTitle = sqlite3_mprintf("Index %s", zU);
|
||||
zWhere = sqlite3_mprintf("name=%Q", zN);
|
||||
rc = diskusedSubreport(&s, zTitle, zWhere, pgsz, nPage);
|
||||
sqlite3_free(zTitle);
|
||||
sqlite3_free(zWhere);
|
||||
if( rc ) break;
|
||||
}
|
||||
rc = diskusedStmtFinish(&s, rc, pS2);
|
||||
if( rc ) break;
|
||||
}
|
||||
}
|
||||
if( diskusedStmtFinish(&s, rc, pStmt) ) return;
|
||||
|
||||
/* Append SQL statements that will recreate the raw data used for
|
||||
** the analysis.
|
||||
*/
|
||||
diskusedTitle(&s, "Raw data used to generate this report");
|
||||
sqlite3_str_appendf(s.pOut,
|
||||
"The following SQL will create a table named \"space_used\" which\n"
|
||||
"contains most of the information used to generate the report above.\n"
|
||||
"*/\n"
|
||||
);
|
||||
sqlite3_str_appendf(s.pOut,
|
||||
"BEGIN;\n"
|
||||
"CREATE TABLE space_used(\n"
|
||||
" name text, -- A table or index\n" /* 0 */
|
||||
" tblname text, -- Table that owns name\n" /* 1 */
|
||||
" is_index boolean, -- TRUE if it is an index\n" /* 2 */
|
||||
" is_without_rowid boolean, -- TRUE if WITHOUT ROWID table\n" /* 3 */
|
||||
" nentry int, -- Number of entries in the BTree\n" /* 4 */
|
||||
" leaf_entries int, -- Number of leaf entries\n" /* 5 */
|
||||
" depth int, -- Depth of the b-tree\n" /* 6 */
|
||||
" payload int, -- Total data in this table/index\n" /* 7 */
|
||||
" ovfl_payload int, -- Total data on overflow pages\n" /* 8 */
|
||||
" ovfl_cnt int, -- Entries that use overflow\n" /* 9 */
|
||||
" mx_payload int, -- Maximum payload size\n" /* 10 */
|
||||
" int_pages int, -- Interior pages used\n" /* 11 */
|
||||
" leaf_pages int, -- Leaf pages used\n" /* 12 */
|
||||
" ovfl_pages int, -- Overflow pages used\n" /* 13 */
|
||||
" int_unused int, -- Unused bytes on interior pages\n" /* 14 */
|
||||
" leaf_unused int, -- Unused bytes on primary pages\n" /* 15 */
|
||||
" ovfl_unused int, -- Unused bytes on overflow pages\n" /* 16 */
|
||||
" int_entries int -- B-tree entries on internal pages\n"/* 17 */
|
||||
");\n"
|
||||
"INSERT INTO space_used VALUES\n"
|
||||
);
|
||||
pStmt = diskusedPrepare(&s,
|
||||
"SELECT quote(name), quote(tblname),\n" /* 0..1 */
|
||||
" is_index, is_without_rowid, nentry, leaf_entries,\n" /* 2..5 */
|
||||
" depth, payload, ovfl_payload, ovfl_cnt, mx_payload,\n" /* 6..10 */
|
||||
" int_pages, leaf_pages, ovfl_pages, int_unused,\n" /* 11..14 */
|
||||
" leaf_unused, ovfl_unused, int_entries\n" /* 15..17 */
|
||||
" FROM temp.%s;",
|
||||
s.zSU);
|
||||
if( pStmt==0 ) return;
|
||||
n = 0;
|
||||
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
|
||||
if( n++ ) sqlite3_str_appendf(s.pOut,",\n");
|
||||
sqlite3_str_appendf(s.pOut,
|
||||
" (%s,%s,%lld,%lld,%lld,%lld,%lld,%lld,%lld,"
|
||||
"%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld,%lld)",
|
||||
sqlite3_column_text(pStmt, 0),
|
||||
sqlite3_column_text(pStmt, 1),
|
||||
sqlite3_column_int64(pStmt, 2),
|
||||
sqlite3_column_int64(pStmt, 3),
|
||||
sqlite3_column_int64(pStmt, 4),
|
||||
sqlite3_column_int64(pStmt, 5),
|
||||
sqlite3_column_int64(pStmt, 6),
|
||||
sqlite3_column_int64(pStmt, 7),
|
||||
sqlite3_column_int64(pStmt, 8),
|
||||
sqlite3_column_int64(pStmt, 9),
|
||||
sqlite3_column_int64(pStmt, 10),
|
||||
sqlite3_column_int64(pStmt, 11),
|
||||
sqlite3_column_int64(pStmt, 12),
|
||||
sqlite3_column_int64(pStmt, 13),
|
||||
sqlite3_column_int64(pStmt, 14),
|
||||
sqlite3_column_int64(pStmt, 15),
|
||||
sqlite3_column_int64(pStmt, 16),
|
||||
sqlite3_column_int64(pStmt, 17));
|
||||
}
|
||||
if( rc!=SQLITE_DONE ){
|
||||
diskusedError(&s, "SQL run-time error: %s\nSQL: %s",
|
||||
sqlite3_errmsg(s.db), sqlite3_sql(pStmt));
|
||||
sqlite3_finalize(pStmt);
|
||||
return;
|
||||
}
|
||||
sqlite3_str_appendf(s.pOut,";\nCOMMIT;");
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
if( sqlite3_str_length(s.pOut) ){
|
||||
sqlite3_result_text(context, sqlite3_str_finish(s.pOut), -1,
|
||||
sqlite3_free);
|
||||
s.pOut = 0;
|
||||
}
|
||||
diskusedReset(&s);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_diskused_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "diskused", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS,
|
||||
0, diskusedFunc, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
+2
-2
@@ -92,7 +92,7 @@ static int explainConnect(
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(addr,opcode,p1,p2,p3,p4,p5,comment,sql HIDDEN)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -114,7 +114,7 @@ static int explainDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int explainOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
explain_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->db = ((explain_vtab*)p)->db;
|
||||
|
||||
+68
-235
@@ -73,6 +73,11 @@
|
||||
** $path is a relative path, then $path is interpreted relative to $dir.
|
||||
** And the paths returned in the "name" column of the table are also
|
||||
** relative to directory $dir.
|
||||
**
|
||||
** Notes on building this extension for Windows:
|
||||
** Unless linked statically with the SQLite library, a preprocessor
|
||||
** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone
|
||||
** DLL form of this extension for WIN32. See its use below for details.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
@@ -89,8 +94,6 @@ SQLITE_EXTENSION_INIT1
|
||||
# include <utime.h>
|
||||
# include <sys/time.h>
|
||||
# define STRUCT_STAT struct stat
|
||||
# include <limits.h>
|
||||
# include <stdlib.h>
|
||||
#else
|
||||
# include "windirent.h"
|
||||
# include <direct.h>
|
||||
@@ -123,70 +126,17 @@ SQLITE_EXTENSION_INIT1
|
||||
#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
|
||||
#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
|
||||
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
** Convert a UTF-8 string to Microsoft Unicode.
|
||||
**
|
||||
** Space to hold the returned string is obtained from sqlite3_malloc().
|
||||
*/
|
||||
static wchar_t *winUtf8To16(const char *zText){
|
||||
int nChar;
|
||||
wchar_t *zWideText;
|
||||
|
||||
nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
|
||||
if( nChar==0 ){
|
||||
return 0;
|
||||
}
|
||||
zWideText = sqlite3_malloc64(nChar*sizeof(WCHAR) );
|
||||
if( zWideText==0 ){
|
||||
return 0;
|
||||
}
|
||||
nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText,
|
||||
nChar);
|
||||
if( nChar==0 ){
|
||||
sqlite3_free(zWideText);
|
||||
zWideText = 0;
|
||||
}
|
||||
return zWideText;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
** Convert a Microsoft Unicode string to UTF-8.
|
||||
**
|
||||
** Space to hold the returned string is obtained from sqlite3_malloc().
|
||||
*/
|
||||
static char *winUtf16To8(wchar_t *zWideText){
|
||||
int nByte;
|
||||
char *zText;
|
||||
|
||||
nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
|
||||
if( nByte == 0 ){
|
||||
return 0;
|
||||
}
|
||||
zText = sqlite3_malloc64( nByte );
|
||||
if( zText==0 ){
|
||||
return 0;
|
||||
}
|
||||
nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte,
|
||||
0, 0);
|
||||
if( nByte == 0 ){
|
||||
sqlite3_free(zText);
|
||||
zText = 0;
|
||||
}
|
||||
return zText;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/*
|
||||
** UTF8 chmod() function for Windows
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
static int fileio_chmod(const char *zPath, int pmode){
|
||||
sqlite3_int64 sz = strlen(zPath);
|
||||
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
|
||||
int rc;
|
||||
wchar_t *b1 = winUtf8To16(zPath);
|
||||
if( b1==0 ) return -1;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
rc = _wchmod(b1, pmode);
|
||||
sqlite3_free(b1);
|
||||
return rc;
|
||||
@@ -198,9 +148,12 @@ static int fileio_chmod(const char *zPath, int pmode){
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
static int fileio_mkdir(const char *zPath){
|
||||
sqlite3_int64 sz = strlen(zPath);
|
||||
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
|
||||
int rc;
|
||||
wchar_t *b1 = winUtf8To16(zPath);
|
||||
if( b1==0 ) return -1;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
rc = _wmkdir(b1);
|
||||
sqlite3_free(b1);
|
||||
return rc;
|
||||
@@ -313,7 +266,50 @@ static sqlite3_uint64 fileTimeToUnixTime(
|
||||
|
||||
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
|
||||
#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
|
||||
# /* To allow a standalone DLL, use this next replacement function: */
|
||||
# undef sqlite3_win32_utf8_to_unicode
|
||||
# define sqlite3_win32_utf8_to_unicode utf8_to_utf16
|
||||
#
|
||||
LPWSTR utf8_to_utf16(const char *z){
|
||||
int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0);
|
||||
LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR));
|
||||
if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) )
|
||||
return rv;
|
||||
sqlite3_free(rv);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function attempts to normalize the time values found in the stat()
|
||||
** buffer to UTC. This is necessary on Win32, where the runtime library
|
||||
** appears to return these values as local times.
|
||||
*/
|
||||
static void statTimesToUtc(
|
||||
const char *zPath,
|
||||
STRUCT_STAT *pStatBuf
|
||||
){
|
||||
HANDLE hFindFile;
|
||||
WIN32_FIND_DATAW fd;
|
||||
LPWSTR zUnicodeName;
|
||||
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
|
||||
zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
|
||||
if( zUnicodeName ){
|
||||
memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
|
||||
hFindFile = FindFirstFileW(zUnicodeName, &fd);
|
||||
if( hFindFile!=NULL ){
|
||||
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
|
||||
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
|
||||
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
|
||||
FindClose(hFindFile);
|
||||
}
|
||||
sqlite3_free(zUnicodeName);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function is used in place of stat(). On Windows, special handling
|
||||
@@ -325,23 +321,14 @@ static int fileStat(
|
||||
STRUCT_STAT *pStatBuf
|
||||
){
|
||||
#if defined(_WIN32)
|
||||
sqlite3_int64 sz = strlen(zPath);
|
||||
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
|
||||
int rc;
|
||||
wchar_t *b1 = winUtf8To16(zPath);
|
||||
if( b1==0 ) return 1;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
rc = _wstat(b1, pStatBuf);
|
||||
if( rc==0 ){
|
||||
HANDLE hFindFile;
|
||||
WIN32_FIND_DATAW fd;
|
||||
memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
|
||||
hFindFile = FindFirstFileW(b1, &fd);
|
||||
if( hFindFile!=NULL ){
|
||||
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
|
||||
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
|
||||
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
|
||||
FindClose(hFindFile);
|
||||
}
|
||||
}
|
||||
sqlite3_free(b1);
|
||||
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
||||
return rc;
|
||||
#else
|
||||
return stat(zPath, pStatBuf);
|
||||
@@ -472,6 +459,7 @@ static int writeFile(
|
||||
|
||||
if( mtime>=0 ){
|
||||
#if defined(_WIN32)
|
||||
#if !SQLITE_OS_WINRT
|
||||
/* Windows */
|
||||
FILETIME lastAccess;
|
||||
FILETIME lastWrite;
|
||||
@@ -479,13 +467,14 @@ static int writeFile(
|
||||
LONGLONG intervals;
|
||||
HANDLE hFile;
|
||||
LPWSTR zUnicodeName;
|
||||
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
|
||||
|
||||
GetSystemTime(¤tTime);
|
||||
SystemTimeToFileTime(¤tTime, &lastAccess);
|
||||
intervals = (mtime*10000000) + 116444736000000000;
|
||||
lastWrite.dwLowDateTime = (DWORD)intervals;
|
||||
lastWrite.dwHighDateTime = intervals >> 32;
|
||||
zUnicodeName = winUtf8To16(zFile);
|
||||
zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile);
|
||||
if( zUnicodeName==0 ){
|
||||
return 1;
|
||||
}
|
||||
@@ -501,6 +490,7 @@ static int writeFile(
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
|
||||
/* Recent unix */
|
||||
struct timespec times[2];
|
||||
@@ -666,7 +656,7 @@ static int fsdirConnect(
|
||||
(void)pzErr;
|
||||
rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = (fsdir_tab*)sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
|
||||
@@ -689,7 +679,7 @@ static int fsdirDisconnect(sqlite3_vtab *pVtab){
|
||||
static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
fsdir_cursor *pCur;
|
||||
(void)p;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->iLvl = -1;
|
||||
@@ -1104,158 +1094,6 @@ static int fsdirRegister(sqlite3 *db){
|
||||
# define fsdirRegister(x) SQLITE_OK
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This version of realpath() works on any system. The string
|
||||
** returned is held in memory allocated using sqlite3_malloc64().
|
||||
** The caller is responsible for calling sqlite3_free().
|
||||
*/
|
||||
static char *portable_realpath(const char *zPath){
|
||||
#if !defined(_WIN32) /* BEGIN unix */
|
||||
|
||||
char *zOut = 0; /* Result */
|
||||
char *z; /* Temporary buffer */
|
||||
#if defined(PATH_MAX)
|
||||
char zBuf[PATH_MAX+1]; /* Space for the temporary buffer */
|
||||
#endif
|
||||
|
||||
if( zPath==0 ) return 0;
|
||||
#if defined(PATH_MAX)
|
||||
z = realpath(zPath, zBuf);
|
||||
if( z ){
|
||||
zOut = sqlite3_mprintf("%s", zBuf);
|
||||
}
|
||||
#endif /* defined(PATH_MAX) */
|
||||
if( zOut==0 ){
|
||||
/* Try POSIX.1-2008 malloc behavior */
|
||||
z = realpath(zPath, NULL);
|
||||
if( z ){
|
||||
zOut = sqlite3_mprintf("%s", z);
|
||||
free(z);
|
||||
}
|
||||
}
|
||||
return zOut;
|
||||
|
||||
#else /* End UNIX, Begin WINDOWS */
|
||||
|
||||
wchar_t *zPath16; /* UTF16 translation of zPath */
|
||||
char *zOut = 0; /* Result */
|
||||
wchar_t *z = 0; /* Temporary buffer */
|
||||
|
||||
if( zPath==0 ) return 0;
|
||||
|
||||
zPath16 = winUtf8To16(zPath);
|
||||
if( zPath16==0 ) return 0;
|
||||
z = _wfullpath(NULL, zPath16, 0);
|
||||
sqlite3_free(zPath16);
|
||||
if( z ){
|
||||
zOut = winUtf16To8(z);
|
||||
free(z);
|
||||
}
|
||||
return zOut;
|
||||
|
||||
#endif /* End WINDOWS, Begin common code */
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: realpath(X)
|
||||
**
|
||||
** Try to convert file or pathname X into its real, absolute pathname.
|
||||
** Return NULL if unable.
|
||||
**
|
||||
** The file or directory X is not required to exist. The answer is formed
|
||||
** by calling system realpath() on the prefix of X that does exist and
|
||||
** appending the tail of X that does not (yet) exist.
|
||||
**
|
||||
** FIXME: This routine sometimes returns NULL rather than raising
|
||||
** an SQLITE_NOMEM error if an OOM is encountered.
|
||||
*/
|
||||
static void realpathFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zPath; /* Original input path */
|
||||
char *zCopy; /* An editable copy of zPath */
|
||||
char *zOut; /* The result */
|
||||
char cSep = 0; /* Separator turned into \000 */
|
||||
size_t len; /* Prefix length before cSep */
|
||||
#ifdef _WIN32
|
||||
const int isWin = 1;
|
||||
#else
|
||||
const int isWin = 0;
|
||||
#endif
|
||||
|
||||
(void)argc;
|
||||
zPath = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zPath==0 ) return;
|
||||
if( zPath[0]==0 ) zPath = ".";
|
||||
zCopy = sqlite3_mprintf("%s",zPath);
|
||||
if( zCopy==0 ) return;
|
||||
len = strlen(zCopy);
|
||||
while( len>1 && (zCopy[len-1]=='/' || (isWin && zCopy[len-1]=='\\')) ){
|
||||
len--;
|
||||
}
|
||||
zCopy[len] = 0;
|
||||
while( 1 /*exit-by-break*/ ){
|
||||
zOut = portable_realpath(zCopy);
|
||||
zCopy[len] = cSep;
|
||||
if( zOut ){
|
||||
if( cSep ){
|
||||
zOut = sqlite3_mprintf("%z%s",zOut,&zCopy[len]);
|
||||
}
|
||||
break;
|
||||
}else{
|
||||
size_t i = len-1;
|
||||
while( i>0 ){
|
||||
if( zCopy[i]=='/' || (isWin && zCopy[i]=='\\') ) break;
|
||||
i--;
|
||||
}
|
||||
if( i<=0 ){
|
||||
if( zCopy[0]=='/' ){
|
||||
zOut = zCopy;
|
||||
zCopy = 0;
|
||||
}else if( (zOut = portable_realpath("."))!=0 ){
|
||||
zOut = sqlite3_mprintf("%z/%s", zOut, zCopy);
|
||||
}
|
||||
break;
|
||||
}
|
||||
cSep = zCopy[i];
|
||||
zCopy[i] = 0;
|
||||
len = i;
|
||||
}
|
||||
}
|
||||
sqlite3_free(zCopy);
|
||||
if( zOut ){
|
||||
/* Simplify any "/./" or "/../" that might have snuck into the
|
||||
** pathname due to appending of zCopy. We only have to consider
|
||||
** unix "/" separators, because the _wfilepath() system call on
|
||||
** Windows will have already done this simplification for us. */
|
||||
size_t i, j, n;
|
||||
n = strlen(zOut);
|
||||
for(i=j=0; i<n; i++){
|
||||
if( zOut[i]=='/' ){
|
||||
if( zOut[i+1]=='/' ) continue;
|
||||
if( zOut[i+1]=='.' && i+2<n && zOut[i+2]=='/' ){
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if( zOut[i+1]=='.' && i+3<n && zOut[i+2]=='.' && zOut[i+3]=='/' ){
|
||||
while( j>0 && zOut[j-1]!='/' ){ j--; }
|
||||
if( j>0 ){ j--; }
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
zOut[j++] = zOut[i];
|
||||
}
|
||||
zOut[j] = 0;
|
||||
|
||||
/* Return the result */
|
||||
sqlite3_result_text(context, zOut, -1, sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
@@ -1282,10 +1120,5 @@ int sqlite3_fileio_init(
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fsdirRegister(db);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "realpath", 1,
|
||||
SQLITE_UTF8, 0,
|
||||
realpathFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+22
-49
@@ -29,17 +29,6 @@
|
||||
** RBU does not use this extension directly. Rather, this extension is
|
||||
** provided as a convenience to developers who want to analyze RBU files
|
||||
** that contain deltas.
|
||||
**
|
||||
** Typical build commands assuming
|
||||
**
|
||||
** DIR=ext/misc
|
||||
* NAME=fossildelta
|
||||
**
|
||||
** First run "make sqlite3ext.h" then:
|
||||
**
|
||||
** linux: gcc -shared -I. -fPIC -o $NAME.so $DIR/$NAME.c
|
||||
** OS-X: gcc -dynamiclib -fPIC -I. -o $NAME.dylib $DIR/$NAME.c
|
||||
** Win11: cl -I. $DIR/$NAME.c -link -dll -out:$NAME.dll
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -49,12 +38,9 @@ SQLITE_EXTENSION_INIT1
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
/*
|
||||
** The "u32" type must be an unsigned 32-bit integer. "u64" is
|
||||
** an unsigned 64-bit integer.
|
||||
** The "u32" type must be an unsigned 32-bit integer. Adjust this
|
||||
*/
|
||||
typedef unsigned int u32;
|
||||
typedef sqlite3_uint64 u64;
|
||||
typedef sqlite3_int64 i64;
|
||||
|
||||
/*
|
||||
** Must be a 16-bit value
|
||||
@@ -179,26 +165,16 @@ static unsigned int deltaGetInt(const char **pz, int *pLen){
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36,
|
||||
-1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, 63, -1,
|
||||
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
unsigned int v = 0;
|
||||
int c;
|
||||
unsigned char *z = (unsigned char*)*pz;
|
||||
unsigned char *zEnd = z + (*pLen);
|
||||
while( z<zEnd && (c = zValue[*z])>=0 ){
|
||||
v = (v<<6) + c;
|
||||
z++;
|
||||
unsigned char *zStart = z;
|
||||
while( (c = zValue[0x7f&*(z++)])>=0 ){
|
||||
v = (v<<6) + c;
|
||||
}
|
||||
|
||||
*pLen -= (int)(z - (unsigned char*)*pz);
|
||||
z--;
|
||||
*pLen -= z - zStart;
|
||||
*pz = (char*)z;
|
||||
return v;
|
||||
}
|
||||
@@ -288,8 +264,7 @@ static unsigned int checksum(const char *zIn, size_t N){
|
||||
** The delta string will be NUL-terminated, but it might also contain
|
||||
** embedded NUL characters if either the zSrc or zOut files are
|
||||
** binary. This function returns the length of the delta string
|
||||
** in bytes, excluding the final NUL terminator character. It returns
|
||||
** 0 if an OOM occurs.
|
||||
** in bytes, excluding the final NUL terminator character.
|
||||
**
|
||||
** Output Format:
|
||||
**
|
||||
@@ -381,7 +356,6 @@ static int delta_create(
|
||||
*/
|
||||
nHash = lenSrc/NHASH;
|
||||
collide = sqlite3_malloc64( (sqlite3_int64)nHash*2*sizeof(int) );
|
||||
if( collide==0 ) return 0;
|
||||
memset(collide, -1, nHash*2*sizeof(int));
|
||||
landmark = &collide[nHash];
|
||||
for(i=0; i<lenSrc-NHASH; i+=NHASH){
|
||||
@@ -532,7 +506,7 @@ static int delta_create(
|
||||
static int delta_output_size(const char *zDelta, int lenDelta){
|
||||
int size;
|
||||
size = deltaGetInt(&zDelta, &lenDelta);
|
||||
if( lenDelta<=0 || *zDelta!='\n' ){
|
||||
if( *zDelta!='\n' ){
|
||||
/* ERROR: size integer not terminated by "\n" */
|
||||
return -1;
|
||||
}
|
||||
@@ -567,14 +541,14 @@ static int delta_apply(
|
||||
int lenDelta, /* Length of the delta */
|
||||
char *zOut /* Write the output into this preallocated buffer */
|
||||
){
|
||||
sqlite3_uint64 limit;
|
||||
sqlite3_uint64 total = 0;
|
||||
unsigned int limit;
|
||||
unsigned int total = 0;
|
||||
#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
|
||||
char *zOrigOut = zOut;
|
||||
#endif
|
||||
|
||||
limit = deltaGetInt(&zDelta, &lenDelta);
|
||||
if( lenDelta<=0 || *zDelta!='\n' ){
|
||||
if( *zDelta!='\n' ){
|
||||
/* ERROR: size integer not terminated by "\n" */
|
||||
return -1;
|
||||
}
|
||||
@@ -582,7 +556,6 @@ static int delta_apply(
|
||||
while( *zDelta && lenDelta>0 ){
|
||||
unsigned int cnt, ofst;
|
||||
cnt = deltaGetInt(&zDelta, &lenDelta);
|
||||
if( lenDelta<=0 ) return -1;
|
||||
switch( zDelta[0] ){
|
||||
case '@': {
|
||||
zDelta++; lenDelta--;
|
||||
@@ -597,7 +570,7 @@ static int delta_apply(
|
||||
/* ERROR: copy exceeds output file size */
|
||||
return -1;
|
||||
}
|
||||
if( (u64)ofst+(u64)cnt > (u64)lenSrc ){
|
||||
if( ofst+cnt > lenSrc ){
|
||||
/* ERROR: copy extends past end of input */
|
||||
return -1;
|
||||
}
|
||||
@@ -794,11 +767,11 @@ struct deltaparsevtab_vtab {
|
||||
struct deltaparsevtab_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
char *aDelta; /* The delta being parsed */
|
||||
i64 iCursor; /* Current cursor location */
|
||||
i64 iNext; /* Next cursor value */
|
||||
i64 nDelta; /* Number of bytes in the delta */
|
||||
int nDelta; /* Number of bytes in the delta */
|
||||
int iCursor; /* Current cursor location */
|
||||
int eOp; /* Name of current operator */
|
||||
unsigned int a1, a2; /* Arguments to current operator */
|
||||
int iNext; /* Next cursor value */
|
||||
};
|
||||
|
||||
/* Operator names:
|
||||
@@ -868,7 +841,7 @@ static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
deltaparsevtab_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
@@ -902,7 +875,7 @@ static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
|
||||
}
|
||||
z = pCur->aDelta + pCur->iCursor;
|
||||
pCur->a1 = deltaGetInt(&z, &i);
|
||||
switch( i>0 ? z[0] : 0 ){
|
||||
switch( z[0] ){
|
||||
case '@': {
|
||||
z++;
|
||||
if( pCur->iNext>=pCur->nDelta ){
|
||||
@@ -912,14 +885,14 @@ static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
|
||||
}
|
||||
pCur->a2 = deltaGetInt(&z, &i);
|
||||
pCur->eOp = DELTAPARSE_OP_COPY;
|
||||
pCur->iNext = (i64)(&z[1] - pCur->aDelta);
|
||||
pCur->iNext = (int)(&z[1] - pCur->aDelta);
|
||||
break;
|
||||
}
|
||||
case ':': {
|
||||
z++;
|
||||
pCur->a2 = (unsigned int)(z - pCur->aDelta);
|
||||
pCur->eOp = DELTAPARSE_OP_INSERT;
|
||||
pCur->iNext = (i64)(&z[pCur->a1] - pCur->aDelta);
|
||||
pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta);
|
||||
break;
|
||||
}
|
||||
case ';': {
|
||||
@@ -963,7 +936,7 @@ static int deltaparsevtabColumn(
|
||||
if( pCur->eOp==DELTAPARSE_OP_COPY ){
|
||||
sqlite3_result_int(ctx, pCur->a2);
|
||||
}else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
|
||||
if( (i64)pCur->a2 + (i64)pCur->a1 > pCur->nDelta ){
|
||||
if( pCur->a2 + pCur->a1 > pCur->nDelta ){
|
||||
sqlite3_result_zeroblob(ctx, pCur->a1);
|
||||
}else{
|
||||
sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
|
||||
@@ -1032,14 +1005,14 @@ static int deltaparsevtabFilter(
|
||||
a = pCur->aDelta;
|
||||
pCur->eOp = DELTAPARSE_OP_SIZE;
|
||||
pCur->a1 = deltaGetInt(&a, &i);
|
||||
if( i<=0 || a[0]!='\n' ){
|
||||
if( a[0]!='\n' ){
|
||||
pCur->eOp = DELTAPARSE_OP_ERROR;
|
||||
pCur->a1 = pCur->a2 = 0;
|
||||
pCur->iNext = pCur->nDelta;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
a++;
|
||||
pCur->iNext = (i64)(a - pCur->aDelta);
|
||||
pCur->iNext = (unsigned int)(a - pCur->aDelta);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -556,7 +556,7 @@ static int fuzzerConnect(
|
||||
static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
|
||||
fuzzer_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->pVtab = p;
|
||||
@@ -617,12 +617,12 @@ static int fuzzerRender(
|
||||
int *pnBuf /* Size of the buffer */
|
||||
){
|
||||
const fuzzer_rule *pRule = pStem->pRule;
|
||||
sqlite3_int64 n; /* Size of output term without nul-term */
|
||||
int n; /* Size of output term without nul-term */
|
||||
char *z; /* Buffer to assemble output term in */
|
||||
|
||||
n = pStem->nBasis + pRule->nTo - pRule->nFrom;
|
||||
if( (*pnBuf)<n+1 ){
|
||||
(*pzBuf) = sqlite3_realloc64((*pzBuf), n+100);
|
||||
(*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
|
||||
if( (*pzBuf)==0 ) return SQLITE_NOMEM;
|
||||
(*pnBuf) = n+100;
|
||||
}
|
||||
|
||||
+1
-35
@@ -179,9 +179,9 @@ static void ieee754func(
|
||||
}
|
||||
|
||||
if( m<0 ){
|
||||
if( m<(-9223372036854775807LL) ) return;
|
||||
isNeg = 1;
|
||||
m = -m;
|
||||
if( m<0 ) return;
|
||||
}else if( m==0 && e>-1000 && e<1000 ){
|
||||
sqlite3_result_double(context, 0.0);
|
||||
return;
|
||||
@@ -259,38 +259,6 @@ static void ieee754func_to_blob(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Functions to convert between 64-bit integers and floats.
|
||||
**
|
||||
** The bit patterns are copied. The numeric values are different.
|
||||
*/
|
||||
static void ieee754func_from_int(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
|
||||
double r;
|
||||
sqlite3_int64 v = sqlite3_value_int64(argv[0]);
|
||||
memcpy(&r, &v, sizeof(r));
|
||||
sqlite3_result_double(context, r);
|
||||
}
|
||||
}
|
||||
static void ieee754func_to_int(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_FLOAT ){
|
||||
double r = sqlite3_value_double(argv[0]);
|
||||
sqlite3_uint64 v;
|
||||
memcpy(&v, &r, sizeof(v));
|
||||
sqlite3_result_int64(context, v);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: ieee754_inc(r,N)
|
||||
**
|
||||
@@ -343,8 +311,6 @@ int sqlite3_ieee_init(
|
||||
{ "ieee754_exponent", 1, 2, ieee754func },
|
||||
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
|
||||
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
|
||||
{ "ieee754_to_int", 1, 0, ieee754func_to_int },
|
||||
{ "ieee754_from_int", 1, 0, ieee754func_from_int },
|
||||
{ "ieee754_inc", 2, 0, ieee754inc },
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
+2
-2
@@ -85,7 +85,7 @@ static int memstatConnect(
|
||||
|
||||
rc = sqlite3_declare_vtab(db,"CREATE TABLE x(name,schema,value,hiwtr)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -107,7 +107,7 @@ static int memstatDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int memstatOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
memstat_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->db = ((memstat_vtab*)p)->db;
|
||||
|
||||
+38
-38
@@ -340,45 +340,45 @@ static void percentSort(double *a, unsigned int n){
|
||||
int iGt; /* Entries at or after a[iGt] are greater than rPivot */
|
||||
int i; /* Loop counter */
|
||||
double rPivot; /* The pivot value */
|
||||
|
||||
while( n>=2 ){
|
||||
if( a[0]>a[n-1] ){
|
||||
SWAP_DOUBLE(a[0],a[n-1])
|
||||
}
|
||||
if( n==2 ) return;
|
||||
iGt = n-1;
|
||||
i = n/2;
|
||||
if( a[0]>a[i] ){
|
||||
SWAP_DOUBLE(a[0],a[i])
|
||||
}else if( a[i]>a[iGt] ){
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}
|
||||
if( n==3 ) return;
|
||||
rPivot = a[i];
|
||||
iLt = i = 1;
|
||||
do{
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}else{
|
||||
i++;
|
||||
}
|
||||
}while( i<iGt );
|
||||
if( iLt>n/2 ){
|
||||
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
|
||||
n = iLt;
|
||||
}else{
|
||||
if( iLt>=2 ) percentSort(a, iLt);
|
||||
a += iGt;
|
||||
n -= iGt;
|
||||
}
|
||||
|
||||
assert( n>=2 );
|
||||
if( a[0]>a[n-1] ){
|
||||
SWAP_DOUBLE(a[0],a[n-1])
|
||||
}
|
||||
if( n==2 ) return;
|
||||
iGt = n-1;
|
||||
i = n/2;
|
||||
if( a[0]>a[i] ){
|
||||
SWAP_DOUBLE(a[0],a[i])
|
||||
}else if( a[i]>a[iGt] ){
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}
|
||||
if( n==3 ) return;
|
||||
rPivot = a[i];
|
||||
iLt = i = 1;
|
||||
do{
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}else{
|
||||
i++;
|
||||
}
|
||||
}while( i<iGt );
|
||||
if( iLt>=2 ) percentSort(a, iLt);
|
||||
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
|
||||
|
||||
/* Uncomment for testing */
|
||||
#if 0
|
||||
for(i=0; i<n-1; i++){
|
||||
assert( a[i]<=a[i+1] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-7
@@ -75,7 +75,7 @@ static int prefixesConnect(
|
||||
"CREATE TABLE prefixes(prefix TEXT, original_string TEXT HIDDEN)"
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -98,7 +98,7 @@ static int prefixesDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int prefixesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
prefixes_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
@@ -289,14 +289,10 @@ static void prefixLengthFunc(
|
||||
int nL = sqlite3_value_bytes(apVal[0]);
|
||||
int nR = sqlite3_value_bytes(apVal[1]);
|
||||
int i;
|
||||
if( zL==0 || zR==0 ){
|
||||
sqlite3_result_int(ctx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
nByte = (nL > nR ? nL : nR);
|
||||
for(i=0; i<nByte; i++){
|
||||
if( zL[i]==0 || zL[i]!=zR[i] ) break;
|
||||
if( zL[i]!=zR[i] ) break;
|
||||
if( (zL[i] & 0xC0)!=0x80 ) nRet++;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -152,7 +152,7 @@ static int qpvtabConnect(
|
||||
#define QPVTAB_FLAGS 11
|
||||
#define QPVTAB_NONE 12
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -174,7 +174,7 @@ static int qpvtabDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int qpvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
qpvtab_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
@@ -363,7 +363,7 @@ static int qpvtabBestIndex(
|
||||
}
|
||||
sqlite3_str_appendf(pStr,"aConstraint,%d,%s,%d,%d,",
|
||||
i,
|
||||
iCol>=0 ? azColname[iCol] : "rowid",
|
||||
azColname[iCol],
|
||||
op,
|
||||
pIdxInfo->aConstraint[i].usable);
|
||||
pVal = 0;
|
||||
|
||||
+17
-30
@@ -32,7 +32,7 @@
|
||||
** ^X X occurring at the beginning of the string
|
||||
** X$ X occurring at the end of the string
|
||||
** . Match any single character
|
||||
** \c Character c where c is one of \{}()[]|*+?-.
|
||||
** \c Character c where c is one of \{}()[]|*+?.
|
||||
** \c C-language escapes for c in afnrtv. ex: \t or \n
|
||||
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
|
||||
** \xXX Where XX is exactly 2 hex digits, unicode value XX
|
||||
@@ -417,7 +417,7 @@ static int re_hex(int c, int *pV){
|
||||
** return its interpretation.
|
||||
*/
|
||||
static unsigned re_esc_char(ReCompiled *p){
|
||||
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-";
|
||||
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
|
||||
static const char zTrans[] = "\a\f\n\r\t\v";
|
||||
int i, v = 0;
|
||||
char c;
|
||||
@@ -676,7 +676,7 @@ static const char *re_compile(
|
||||
int i, j;
|
||||
|
||||
*ppRe = 0;
|
||||
pRe = sqlite3_malloc64( sizeof(*pRe) );
|
||||
pRe = sqlite3_malloc( sizeof(*pRe) );
|
||||
if( pRe==0 ){
|
||||
return "out of memory";
|
||||
}
|
||||
@@ -740,18 +740,11 @@ static const char *re_compile(
|
||||
}
|
||||
|
||||
/*
|
||||
** The value of LIMIT_MAX_PATTERN_LENGTH.
|
||||
** Compute a reasonable limit on the length of the REGEXP NFA.
|
||||
*/
|
||||
static int re_maxlen(sqlite3_context *context){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Maximum NFA size given a maximum pattern length.
|
||||
*/
|
||||
static int re_maxnfa(int mxlen){
|
||||
return 75+mxlen/2;
|
||||
return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -777,17 +770,10 @@ static void re_sql_func(
|
||||
(void)argc; /* Unused */
|
||||
pRe = sqlite3_get_auxdata(context, 0);
|
||||
if( pRe==0 ){
|
||||
int mxLen = re_maxlen(context);
|
||||
int nPattern;
|
||||
zPattern = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zPattern==0 ) return;
|
||||
nPattern = sqlite3_value_bytes(argv[0]);
|
||||
if( nPattern>mxLen ){
|
||||
zErr = "REGEXP pattern too big";
|
||||
}else{
|
||||
zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen),
|
||||
sqlite3_user_data(context)!=0);
|
||||
}
|
||||
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
|
||||
sqlite3_user_data(context)!=0);
|
||||
if( zErr ){
|
||||
re_free(pRe);
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
@@ -828,6 +814,7 @@ static void re_bytecode_func(
|
||||
int i;
|
||||
int n;
|
||||
char *z;
|
||||
(void)argc;
|
||||
static const char *ReOpName[] = {
|
||||
"EOF",
|
||||
"MATCH",
|
||||
@@ -850,10 +837,9 @@ static void re_bytecode_func(
|
||||
"ATSTART",
|
||||
};
|
||||
|
||||
(void)argc;
|
||||
zPattern = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zPattern==0 ) return;
|
||||
zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)),
|
||||
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
|
||||
sqlite3_user_data(context)!=0);
|
||||
if( zErr ){
|
||||
re_free(pRe);
|
||||
@@ -865,6 +851,7 @@ static void re_bytecode_func(
|
||||
return;
|
||||
}
|
||||
pStr = sqlite3_str_new(0);
|
||||
if( pStr==0 ) goto re_bytecode_func_err;
|
||||
if( pRe->nInit>0 ){
|
||||
sqlite3_str_appendf(pStr, "INIT ");
|
||||
for(i=0; i<pRe->nInit; i++){
|
||||
@@ -876,15 +863,15 @@ static void re_bytecode_func(
|
||||
sqlite3_str_appendf(pStr, "%-8s %4d\n",
|
||||
ReOpName[(unsigned char)pRe->aOp[i]], pRe->aArg[i]);
|
||||
}
|
||||
if( sqlite3_str_errcode(pStr)==SQLITE_NOMEM ){
|
||||
sqlite3_str_finish(pStr);
|
||||
re_free(pRe);
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
n = sqlite3_str_length(pStr);
|
||||
z = sqlite3_str_finish(pStr);
|
||||
sqlite3_result_text(context, z, n-1, sqlite3_free);
|
||||
if( n==0 ){
|
||||
sqlite3_free(z);
|
||||
}else{
|
||||
sqlite3_result_text(context, z, n-1, sqlite3_free);
|
||||
}
|
||||
|
||||
re_bytecode_func_err:
|
||||
re_free(pRe);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
** 2016-05-05
|
||||
**
|
||||
** 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 a utility function (and a utility program) that
|
||||
** makes a copy of an SQLite database while simultaneously zeroing out all
|
||||
** deleted content.
|
||||
**
|
||||
** Normally (when PRAGMA secure_delete=OFF, which is the default) when SQLite
|
||||
** deletes content, it does not overwrite the deleted content but rather marks
|
||||
** the region of the file that held that content as being reusable. This can
|
||||
** cause deleted content to recoverable from the database file. This stale
|
||||
** content is removed by the VACUUM command, but VACUUM can be expensive for
|
||||
** large databases. When in PRAGMA secure_delete=ON mode, the deleted content
|
||||
** is zeroed, but secure_delete=ON has overhead as well.
|
||||
**
|
||||
** This utility attempts to make a copy of a complete SQLite database where
|
||||
** all of the deleted content is zeroed out in the copy, and it attempts to
|
||||
** do so while being faster than running VACUUM.
|
||||
**
|
||||
** Usage:
|
||||
**
|
||||
** int sqlite3_scrub_backup(
|
||||
** const char *zSourceFile, // Source database filename
|
||||
** const char *zDestFile, // Destination database filename
|
||||
** char **pzErrMsg // Write error message here
|
||||
** );
|
||||
**
|
||||
** Simply call the API above specifying the filename of the source database
|
||||
** and the name of the backup copy. The source database must already exist
|
||||
** and can be in active use. (A read lock is held during the backup.) The
|
||||
** destination file should not previously exist. If the pzErrMsg parameter
|
||||
** is non-NULL and if an error occurs, then an error message might be written
|
||||
** into memory obtained from sqlite3_malloc() and *pzErrMsg made to point to
|
||||
** that error message. But if the error is an OOM, the error might not be
|
||||
** reported. The routine always returns non-zero if there is an error.
|
||||
**
|
||||
** If compiled with -DSCRUB_STANDALONE then a main() procedure is added and
|
||||
** this file becomes a standalone program that can be run as follows:
|
||||
**
|
||||
** ./sqlite3scrub SOURCE DEST
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct ScrubState ScrubState;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
|
||||
|
||||
/* State information for a scrub-and-backup operation */
|
||||
struct ScrubState {
|
||||
const char *zSrcFile; /* Name of the source file */
|
||||
const char *zDestFile; /* Name of the destination file */
|
||||
int rcErr; /* Error code */
|
||||
char *zErr; /* Error message text */
|
||||
sqlite3 *dbSrc; /* Source database connection */
|
||||
sqlite3_file *pSrc; /* Source file handle */
|
||||
sqlite3 *dbDest; /* Destination database connection */
|
||||
sqlite3_file *pDest; /* Destination file handle */
|
||||
u32 szPage; /* Page size */
|
||||
u32 szUsable; /* Usable bytes on each page */
|
||||
u32 nPage; /* Number of pages */
|
||||
u32 iLastPage; /* Page number of last page written so far*/
|
||||
u8 *page1; /* Content of page 1 */
|
||||
};
|
||||
|
||||
/* Store an error message */
|
||||
static void scrubBackupErr(ScrubState *p, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
sqlite3_free(p->zErr);
|
||||
va_start(ap, zFormat);
|
||||
p->zErr = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
if( p->rcErr==0 ) p->rcErr = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Allocate memory to hold a single page of content */
|
||||
static u8 *scrubBackupAllocPage(ScrubState *p){
|
||||
u8 *pPage;
|
||||
if( p->rcErr ) return 0;
|
||||
pPage = sqlite3_malloc( p->szPage );
|
||||
if( pPage==0 ) p->rcErr = SQLITE_NOMEM;
|
||||
return pPage;
|
||||
}
|
||||
|
||||
/* Read a page from the source database into memory. Use the memory
|
||||
** provided by pBuf if not NULL or allocate a new page if pBuf==NULL.
|
||||
*/
|
||||
static u8 *scrubBackupRead(ScrubState *p, int pgno, u8 *pBuf){
|
||||
int rc;
|
||||
sqlite3_int64 iOff;
|
||||
u8 *pOut = pBuf;
|
||||
if( p->rcErr ) return 0;
|
||||
if( pOut==0 ){
|
||||
pOut = scrubBackupAllocPage(p);
|
||||
if( pOut==0 ) return 0;
|
||||
}
|
||||
iOff = (pgno-1)*(sqlite3_int64)p->szPage;
|
||||
rc = p->pSrc->pMethods->xRead(p->pSrc, pOut, p->szPage, iOff);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( pBuf==0 ) sqlite3_free(pOut);
|
||||
pOut = 0;
|
||||
scrubBackupErr(p, "read failed for page %d", pgno);
|
||||
p->rcErr = SQLITE_IOERR;
|
||||
}
|
||||
return pOut;
|
||||
}
|
||||
|
||||
/* Write a page to the destination database */
|
||||
static void scrubBackupWrite(ScrubState *p, int pgno, const u8 *pData){
|
||||
int rc;
|
||||
sqlite3_int64 iOff;
|
||||
if( p->rcErr ) return;
|
||||
iOff = (pgno-1)*(sqlite3_int64)p->szPage;
|
||||
rc = p->pDest->pMethods->xWrite(p->pDest, pData, p->szPage, iOff);
|
||||
if( rc!=SQLITE_OK ){
|
||||
scrubBackupErr(p, "write failed for page %d", pgno);
|
||||
p->rcErr = SQLITE_IOERR;
|
||||
}
|
||||
if( (u32)pgno>p->iLastPage ) p->iLastPage = pgno;
|
||||
}
|
||||
|
||||
/* Prepare a statement against the "db" database. */
|
||||
static sqlite3_stmt *scrubBackupPrepare(
|
||||
ScrubState *p, /* Backup context */
|
||||
sqlite3 *db, /* Database to prepare against */
|
||||
const char *zSql /* SQL statement */
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
if( p->rcErr ) return 0;
|
||||
p->rcErr = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p, "SQL error \"%s\" on \"%s\"",
|
||||
sqlite3_errmsg(db), zSql);
|
||||
sqlite3_finalize(pStmt);
|
||||
return 0;
|
||||
}
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
|
||||
/* Open the source database file */
|
||||
static void scrubBackupOpenSrc(ScrubState *p){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
/* Open the source database file */
|
||||
p->rcErr = sqlite3_open_v2(p->zSrcFile, &p->dbSrc,
|
||||
SQLITE_OPEN_READWRITE |
|
||||
SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p, "cannot open source database: %s",
|
||||
sqlite3_errmsg(p->dbSrc));
|
||||
return;
|
||||
}
|
||||
p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_schema; BEGIN;",
|
||||
0, 0, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p,
|
||||
"cannot start a read transaction on the source database: %s",
|
||||
sqlite3_errmsg(p->dbSrc));
|
||||
return;
|
||||
}
|
||||
rc = sqlite3_wal_checkpoint_v2(p->dbSrc, "main", SQLITE_CHECKPOINT_FULL,
|
||||
0, 0);
|
||||
if( rc ){
|
||||
scrubBackupErr(p, "cannot checkpoint the source database");
|
||||
return;
|
||||
}
|
||||
pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_size");
|
||||
if( pStmt==0 ) return;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
p->szPage = sqlite3_column_int(pStmt, 0);
|
||||
}else{
|
||||
scrubBackupErr(p, "unable to determine the page size");
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( p->rcErr ) return;
|
||||
pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_count");
|
||||
if( pStmt==0 ) return;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
p->nPage = sqlite3_column_int(pStmt, 0);
|
||||
}else{
|
||||
scrubBackupErr(p, "unable to determine the size of the source database");
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_file_control(p->dbSrc, "main", SQLITE_FCNTL_FILE_POINTER, &p->pSrc);
|
||||
if( p->pSrc==0 || p->pSrc->pMethods==0 ){
|
||||
scrubBackupErr(p, "cannot get the source file handle");
|
||||
p->rcErr = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and open the destination file */
|
||||
static void scrubBackupOpenDest(ScrubState *p){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
char *zSql;
|
||||
if( p->rcErr ) return;
|
||||
p->rcErr = sqlite3_open_v2(p->zDestFile, &p->dbDest,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
|
||||
SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p, "cannot open destination database: %s",
|
||||
sqlite3_errmsg(p->dbDest));
|
||||
return;
|
||||
}
|
||||
zSql = sqlite3_mprintf("PRAGMA page_size(%u);", p->szPage);
|
||||
if( zSql==0 ){
|
||||
p->rcErr = SQLITE_NOMEM;
|
||||
return;
|
||||
}
|
||||
p->rcErr = sqlite3_exec(p->dbDest, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p,
|
||||
"cannot set the page size on the destination database: %s",
|
||||
sqlite3_errmsg(p->dbDest));
|
||||
return;
|
||||
}
|
||||
sqlite3_exec(p->dbDest, "PRAGMA journal_mode=OFF;", 0, 0, 0);
|
||||
p->rcErr = sqlite3_exec(p->dbDest, "BEGIN EXCLUSIVE;", 0, 0, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p,
|
||||
"cannot start a write transaction on the destination database: %s",
|
||||
sqlite3_errmsg(p->dbDest));
|
||||
return;
|
||||
}
|
||||
pStmt = scrubBackupPrepare(p, p->dbDest, "PRAGMA page_count;");
|
||||
if( pStmt==0 ) return;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
scrubBackupErr(p, "cannot measure the size of the destination");
|
||||
}else if( sqlite3_column_int(pStmt, 0)>1 ){
|
||||
scrubBackupErr(p, "destination database is not empty - holds %d pages",
|
||||
sqlite3_column_int(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_file_control(p->dbDest, "main", SQLITE_FCNTL_FILE_POINTER, &p->pDest);
|
||||
if( p->pDest==0 || p->pDest->pMethods==0 ){
|
||||
scrubBackupErr(p, "cannot get the destination file handle");
|
||||
p->rcErr = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a 32-bit big-endian integer */
|
||||
static u32 scrubBackupInt32(const u8 *a){
|
||||
u32 v = a[3];
|
||||
v += ((u32)a[2])<<8;
|
||||
v += ((u32)a[1])<<16;
|
||||
v += ((u32)a[0])<<24;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read a 16-bit big-endian integer */
|
||||
static u32 scrubBackupInt16(const u8 *a){
|
||||
return (a[0]<<8) + a[1];
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a varint. Put the value in *pVal and return the number of bytes.
|
||||
*/
|
||||
static int scrubBackupVarint(const u8 *z, sqlite3_int64 *pVal){
|
||||
sqlite3_int64 v = 0;
|
||||
int i;
|
||||
for(i=0; i<8; i++){
|
||||
v = (v<<7) + (z[i]&0x7f);
|
||||
if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
|
||||
}
|
||||
v = (v<<8) + (z[i]&0xff);
|
||||
*pVal = v;
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes in a varint.
|
||||
*/
|
||||
static int scrubBackupVarintSize(const u8 *z){
|
||||
int i;
|
||||
for(i=0; i<8; i++){
|
||||
if( (z[i]&0x80)==0 ){ return i+1; }
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy the freelist trunk page given, and all its descendents,
|
||||
** zeroing out as much as possible in the process.
|
||||
*/
|
||||
static void scrubBackupFreelist(ScrubState *p, int pgno, u32 nFree){
|
||||
u8 *a, *aBuf;
|
||||
u32 n, mx;
|
||||
|
||||
if( p->rcErr ) return;
|
||||
aBuf = scrubBackupAllocPage(p);
|
||||
if( aBuf==0 ) return;
|
||||
|
||||
while( pgno && nFree){
|
||||
a = scrubBackupRead(p, pgno, aBuf);
|
||||
if( a==0 ) break;
|
||||
n = scrubBackupInt32(&a[4]);
|
||||
mx = p->szUsable/4 - 2;
|
||||
if( n<mx ){
|
||||
memset(&a[n*4+8], 0, 4*(mx-n));
|
||||
}
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
pgno = scrubBackupInt32(a);
|
||||
#if 0
|
||||
/* There is really no point in copying the freelist leaf pages.
|
||||
** Simply leave them uninitialized in the destination database. The
|
||||
** OS filesystem should zero those pages for us automatically.
|
||||
*/
|
||||
for(i=0; i<n && nFree; i++){
|
||||
u32 iLeaf = scrubBackupInt32(&a[i*4+8]);
|
||||
if( aZero==0 ){
|
||||
aZero = scrubBackupAllocPage(p);
|
||||
if( aZero==0 ){ pgno = 0; break; }
|
||||
memset(aZero, 0, p->szPage);
|
||||
}
|
||||
scrubBackupWrite(p, iLeaf, aZero);
|
||||
nFree--;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
sqlite3_free(aBuf);
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy an overflow chain from source to destination. Zero out any
|
||||
** unused tail at the end of the overflow chain.
|
||||
*/
|
||||
static void scrubBackupOverflow(ScrubState *p, int pgno, u32 nByte){
|
||||
u8 *a, *aBuf;
|
||||
|
||||
aBuf = scrubBackupAllocPage(p);
|
||||
if( aBuf==0 ) return;
|
||||
while( nByte>0 && pgno!=0 ){
|
||||
a = scrubBackupRead(p, pgno, aBuf);
|
||||
if( a==0 ) break;
|
||||
if( nByte >= (p->szUsable)-4 ){
|
||||
nByte -= (p->szUsable) - 4;
|
||||
}else{
|
||||
u32 x = (p->szUsable - 4) - nByte;
|
||||
u32 i = p->szUsable - x;
|
||||
memset(&a[i], 0, x);
|
||||
nByte = 0;
|
||||
}
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
pgno = scrubBackupInt32(a);
|
||||
}
|
||||
sqlite3_free(aBuf);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Copy B-Tree page pgno, and all of its children, from source to destination.
|
||||
** Zero out deleted content during the copy.
|
||||
*/
|
||||
static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){
|
||||
u8 *a;
|
||||
u32 i, n, pc;
|
||||
u32 nCell;
|
||||
u32 nPrefix;
|
||||
u32 szHdr;
|
||||
u32 iChild;
|
||||
u8 *aTop;
|
||||
u8 *aCell;
|
||||
u32 x, y;
|
||||
int ln = 0;
|
||||
|
||||
|
||||
if( p->rcErr ) return;
|
||||
if( iDepth>50 ){
|
||||
scrubBackupErr(p, "corrupt: b-tree too deep at page %d", pgno);
|
||||
return;
|
||||
}
|
||||
if( pgno==1 ){
|
||||
a = p->page1;
|
||||
}else{
|
||||
a = scrubBackupRead(p, pgno, 0);
|
||||
if( a==0 ) return;
|
||||
}
|
||||
nPrefix = pgno==1 ? 100 : 0;
|
||||
aTop = &a[nPrefix];
|
||||
szHdr = 8 + 4*(aTop[0]==0x02 || aTop[0]==0x05);
|
||||
aCell = aTop + szHdr;
|
||||
nCell = scrubBackupInt16(&aTop[3]);
|
||||
|
||||
/* Zero out the gap between the cell index and the start of the
|
||||
** cell content area */
|
||||
x = scrubBackupInt16(&aTop[5]); /* First byte of cell content area */
|
||||
if( x>p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
||||
y = szHdr + nPrefix + nCell*2;
|
||||
if( y>x ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( y<x ) memset(a+y, 0, x-y); /* Zero the gap */
|
||||
|
||||
/* Zero out all the free blocks */
|
||||
pc = scrubBackupInt16(&aTop[1]);
|
||||
if( pc>0 && pc<x ){ ln=__LINE__; goto btree_corrupt; }
|
||||
while( pc ){
|
||||
if( pc>(p->szUsable)-4 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
n = scrubBackupInt16(&a[pc+2]);
|
||||
if( pc+n>(p->szUsable) ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( n>4 ) memset(&a[pc+4], 0, n-4);
|
||||
x = scrubBackupInt16(&a[pc]);
|
||||
if( x<pc+4 && x>0 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
pc = x;
|
||||
}
|
||||
|
||||
/* Write this one page */
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
|
||||
/* Walk the tree and process child pages */
|
||||
for(i=0; i<nCell; i++){
|
||||
u32 X, M, K, nLocal;
|
||||
sqlite3_int64 P;
|
||||
pc = scrubBackupInt16(&aCell[i*2]);
|
||||
if( pc <= szHdr ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( pc > p->szUsable-3 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( aTop[0]==0x05 || aTop[0]==0x02 ){
|
||||
if( pc+4 > p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
||||
iChild = scrubBackupInt32(&a[pc]);
|
||||
pc += 4;
|
||||
scrubBackupBtree(p, iChild, iDepth+1);
|
||||
if( aTop[0]==0x05 ) continue;
|
||||
}
|
||||
pc += scrubBackupVarint(&a[pc], &P);
|
||||
if( pc >= p->szUsable ){ ln=__LINE__; goto btree_corrupt; }
|
||||
if( aTop[0]==0x0d ){
|
||||
X = p->szUsable - 35;
|
||||
}else{
|
||||
X = ((p->szUsable - 12)*64/255) - 23;
|
||||
}
|
||||
if( P<=X ){
|
||||
/* All content is local. No overflow */
|
||||
continue;
|
||||
}
|
||||
M = ((p->szUsable - 12)*32/255)-23;
|
||||
K = M + ((P-M)%(p->szUsable-4));
|
||||
if( aTop[0]==0x0d ){
|
||||
pc += scrubBackupVarintSize(&a[pc]);
|
||||
if( pc > (p->szUsable-4) ){ ln=__LINE__; goto btree_corrupt; }
|
||||
}
|
||||
nLocal = K<=X ? K : M;
|
||||
if( pc+nLocal > p->szUsable-4 ){ ln=__LINE__; goto btree_corrupt; }
|
||||
iChild = scrubBackupInt32(&a[pc+nLocal]);
|
||||
scrubBackupOverflow(p, iChild, (u32)(P-nLocal));
|
||||
}
|
||||
|
||||
/* Walk the right-most tree */
|
||||
if( aTop[0]==0x05 || aTop[0]==0x02 ){
|
||||
iChild = scrubBackupInt32(&aTop[8]);
|
||||
scrubBackupBtree(p, iChild, iDepth+1);
|
||||
}
|
||||
|
||||
/* All done */
|
||||
if( pgno>1 ) sqlite3_free(a);
|
||||
return;
|
||||
|
||||
btree_corrupt:
|
||||
scrubBackupErr(p, "corruption on page %d of source database (errid=%d)",
|
||||
pgno, ln);
|
||||
if( pgno>1 ) sqlite3_free(a);
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy all ptrmap pages from source to destination.
|
||||
** This routine is only called if the source database is in autovacuum
|
||||
** or incremental vacuum mode.
|
||||
*/
|
||||
static void scrubBackupPtrmap(ScrubState *p){
|
||||
u32 pgno = 2;
|
||||
u32 J = p->szUsable/5;
|
||||
u32 iLock = (1073742335/p->szPage)+1;
|
||||
u8 *a, *pBuf;
|
||||
if( p->rcErr ) return;
|
||||
pBuf = scrubBackupAllocPage(p);
|
||||
if( pBuf==0 ) return;
|
||||
while( pgno<=p->nPage ){
|
||||
a = scrubBackupRead(p, pgno, pBuf);
|
||||
if( a==0 ) break;
|
||||
scrubBackupWrite(p, pgno, a);
|
||||
pgno += J+1;
|
||||
if( pgno==iLock ) pgno++;
|
||||
}
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
|
||||
int sqlite3_scrub_backup(
|
||||
const char *zSrcFile, /* Source file */
|
||||
const char *zDestFile, /* Destination file */
|
||||
char **pzErr /* Write error here if non-NULL */
|
||||
){
|
||||
ScrubState s;
|
||||
u32 n, i;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.zSrcFile = zSrcFile;
|
||||
s.zDestFile = zDestFile;
|
||||
|
||||
/* Open both source and destination databases */
|
||||
scrubBackupOpenSrc(&s);
|
||||
scrubBackupOpenDest(&s);
|
||||
|
||||
/* Read in page 1 */
|
||||
s.page1 = scrubBackupRead(&s, 1, 0);
|
||||
if( s.page1==0 ) goto scrub_abort;
|
||||
s.szUsable = s.szPage - s.page1[20];
|
||||
|
||||
/* Copy the freelist */
|
||||
n = scrubBackupInt32(&s.page1[36]);
|
||||
i = scrubBackupInt32(&s.page1[32]);
|
||||
if( n ) scrubBackupFreelist(&s, i, n);
|
||||
|
||||
/* Copy ptrmap pages */
|
||||
n = scrubBackupInt32(&s.page1[52]);
|
||||
if( n ) scrubBackupPtrmap(&s);
|
||||
|
||||
/* Copy all of the btrees */
|
||||
scrubBackupBtree(&s, 1, 0);
|
||||
pStmt = scrubBackupPrepare(&s, s.dbSrc,
|
||||
"SELECT rootpage FROM sqlite_schema WHERE coalesce(rootpage,0)>0");
|
||||
if( pStmt==0 ) goto scrub_abort;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
i = (u32)sqlite3_column_int(pStmt, 0);
|
||||
scrubBackupBtree(&s, i, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
/* If the last page of the input db file is a free-list leaf, then the
|
||||
** backup file on disk is still smaller than the size indicated within
|
||||
** the database header. In this case, write a page of zeroes to the
|
||||
** last page of the backup database so that SQLite does not mistakenly
|
||||
** think the db is corrupt. */
|
||||
if( s.iLastPage<s.nPage ){
|
||||
u8 *aZero = scrubBackupAllocPage(&s);
|
||||
if( aZero ){
|
||||
memset(aZero, 0, s.szPage);
|
||||
scrubBackupWrite(&s, s.nPage, aZero);
|
||||
sqlite3_free(aZero);
|
||||
}
|
||||
}
|
||||
|
||||
scrub_abort:
|
||||
/* Close the destination database without closing the transaction. If we
|
||||
** commit, page zero will be overwritten. */
|
||||
sqlite3_close(s.dbDest);
|
||||
|
||||
/* But do close out the read-transaction on the source database */
|
||||
sqlite3_exec(s.dbSrc, "COMMIT;", 0, 0, 0);
|
||||
sqlite3_close(s.dbSrc);
|
||||
sqlite3_free(s.page1);
|
||||
if( pzErr ){
|
||||
*pzErr = s.zErr;
|
||||
}else{
|
||||
sqlite3_free(s.zErr);
|
||||
}
|
||||
return s.rcErr;
|
||||
}
|
||||
|
||||
#ifdef SCRUB_STANDALONE
|
||||
/* Error and warning log */
|
||||
static void errorLogCallback(void *pNotUsed, int iErr, const char *zMsg){
|
||||
const char *zType;
|
||||
switch( iErr&0xff ){
|
||||
case SQLITE_WARNING: zType = "WARNING"; break;
|
||||
case SQLITE_NOTICE: zType = "NOTICE"; break;
|
||||
default: zType = "ERROR"; break;
|
||||
}
|
||||
fprintf(stderr, "%s: %s\n", zType, zMsg);
|
||||
}
|
||||
|
||||
/* The main() routine when this utility is run as a stand-alone program */
|
||||
int main(int argc, char **argv){
|
||||
char *zErr = 0;
|
||||
int rc;
|
||||
if( argc!=3 ){
|
||||
fprintf(stderr,"Usage: %s SOURCE DESTINATION\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, 0);
|
||||
rc = sqlite3_scrub_backup(argv[1], argv[2], &zErr);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
fprintf(stderr, "%s: out of memory\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
if( zErr ){
|
||||
fprintf(stderr, "%s: %s\n", argv[0], zErr);
|
||||
sqlite3_free(zErr);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
+13
-31
@@ -239,7 +239,7 @@ static int seriesConnect(
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
@@ -261,7 +261,7 @@ static int seriesDisconnect(sqlite3_vtab *pVtab){
|
||||
static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
|
||||
series_cursor *pCur;
|
||||
(void)pUnused;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
@@ -400,16 +400,6 @@ static double seriesFloor(double r){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Convert a floating point value to its closest integer. Do so in
|
||||
** a way that avoids 'outside the range of representable values' warnings
|
||||
** from UBSAN.
|
||||
*/
|
||||
sqlite3_int64 seriesRealToI64(double r){
|
||||
if( r<-9223372036854774784.0 ) return SMALLEST_INT64;
|
||||
if( r>+9223372036854774784.0 ) return LARGEST_INT64;
|
||||
return (sqlite3_int64)r;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the series_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
@@ -532,7 +522,7 @@ static int seriesFilter(
|
||||
&& r>=(double)SMALLEST_INT64
|
||||
&& r<=(double)LARGEST_INT64
|
||||
){
|
||||
iMin = iMax = seriesRealToI64(r);
|
||||
iMin = iMax = (sqlite3_int64)r;
|
||||
}else{
|
||||
goto series_no_rows;
|
||||
}
|
||||
@@ -540,19 +530,15 @@ static int seriesFilter(
|
||||
iMin = iMax = sqlite3_value_int64(argv[iArg++]);
|
||||
}
|
||||
}else{
|
||||
if( idxNum & 0x0300 ){ /* value>X (0x200) or value>=X (0x100) */
|
||||
if( idxNum & 0x0300 ){ /* value>X or value>=X */
|
||||
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
|
||||
double r = sqlite3_value_double(argv[iArg++]);
|
||||
if( r<=(double)SMALLEST_INT64 ){
|
||||
if( r<(double)SMALLEST_INT64 ){
|
||||
iMin = SMALLEST_INT64;
|
||||
}else if( r>(double)LARGEST_INT64 ){
|
||||
goto series_no_rows;
|
||||
}else if( (idxNum & 0x0200)!=0 && r==seriesCeil(r) ){
|
||||
iMin = (sqlite3_int64)seriesCeil(r+1.0);
|
||||
}else{
|
||||
iMin = seriesRealToI64(seriesCeil(r));
|
||||
if( (idxNum & 0x0200)!=0 && r==seriesCeil(r) ){
|
||||
if( iMin==LARGEST_INT64 ) goto series_no_rows;
|
||||
iMin++;
|
||||
}
|
||||
iMin = (sqlite3_int64)seriesCeil(r);
|
||||
}
|
||||
}else{
|
||||
iMin = sqlite3_value_int64(argv[iArg++]);
|
||||
@@ -565,19 +551,15 @@ static int seriesFilter(
|
||||
}
|
||||
}
|
||||
}
|
||||
if( idxNum & 0x3000 ){ /* value<X (0x2000) or value<=X (0x1000) */
|
||||
if( idxNum & 0x3000 ){ /* value<X or value<=X */
|
||||
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
|
||||
double r = sqlite3_value_double(argv[iArg++]);
|
||||
if( r>=(double)LARGEST_INT64 ){
|
||||
if( r>(double)LARGEST_INT64 ){
|
||||
iMax = LARGEST_INT64;
|
||||
}else if( r<=(double)SMALLEST_INT64 ){
|
||||
goto series_no_rows;
|
||||
}else if( (idxNum & 0x2000)!=0 && r==seriesFloor(r) ){
|
||||
iMax = (sqlite3_int64)(r-1.0);
|
||||
}else{
|
||||
iMax = seriesRealToI64(seriesFloor(r));
|
||||
if( (idxNum & 0x2000)!=0 && r==seriesFloor(r) ){
|
||||
if( iMax==SMALLEST_INT64 ) goto series_no_rows;
|
||||
iMax--;
|
||||
}
|
||||
iMax = (sqlite3_int64)seriesFloor(r);
|
||||
}
|
||||
}else{
|
||||
iMax = sqlite3_value_int64(argv[iArg++]);
|
||||
|
||||
+8
-24
@@ -31,10 +31,6 @@ SQLITE_EXTENSION_INIT1
|
||||
/******************************************************************************
|
||||
** The Hash Engine
|
||||
*/
|
||||
#if defined(__GNUC__) && __GNUC__>=11
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wstringop-overread"
|
||||
#endif
|
||||
/* Context for the SHA1 hash */
|
||||
typedef struct SHA1Context SHA1Context;
|
||||
struct SHA1Context {
|
||||
@@ -175,7 +171,7 @@ static void hash_step(
|
||||
}else{
|
||||
i = 0;
|
||||
}
|
||||
if( len-i>0 ) (void)memcpy(&p->buffer[j], &data[i], len - i);
|
||||
(void)memcpy(&p->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
/* Compute a string using sqlite3_vsnprintf() and hash it */
|
||||
@@ -230,23 +226,17 @@ static void hash_finish(
|
||||
zOut[i*2]= 0;
|
||||
}
|
||||
}
|
||||
#if defined(__GNUC__) && __GNUC__>=11
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
/* End of the hashing logic
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
** Two SQL functions: sha1(X) and sha1b(X).
|
||||
** Implementation of the sha1(X) function.
|
||||
**
|
||||
** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash
|
||||
** of the argument X. If X is a BLOB, it is hashed as is. For all other
|
||||
** Return a lower-case hexadecimal rendering of the SHA1 hash of the
|
||||
** argument X. If X is a BLOB, it is hashed as is. For all other
|
||||
** types of input, X is converted into a UTF-8 string and the string
|
||||
** is hashed without the trailing 0x00 terminator. The hash of a NULL
|
||||
** is hash without the trailing 0x00 terminator. The hash of a NULL
|
||||
** value is NULL.
|
||||
**
|
||||
** sha1b(X) is the same except that it returns a 20-byte BLOB containing
|
||||
** the binary hash instead of a hexadecimal string.
|
||||
*/
|
||||
static void sha1Func(
|
||||
sqlite3_context *context,
|
||||
@@ -256,27 +246,22 @@ static void sha1Func(
|
||||
SHA1Context cx;
|
||||
int eType = sqlite3_value_type(argv[0]);
|
||||
int nByte = sqlite3_value_bytes(argv[0]);
|
||||
const unsigned char *pData;
|
||||
char zOut[44];
|
||||
|
||||
assert( argc==1 );
|
||||
if( eType==SQLITE_NULL ) return;
|
||||
hash_init(&cx);
|
||||
if( eType==SQLITE_BLOB ){
|
||||
pData = (const unsigned char*)sqlite3_value_blob(argv[0]);
|
||||
hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
|
||||
}else{
|
||||
pData = (const unsigned char*)sqlite3_value_text(argv[0]);
|
||||
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
|
||||
}
|
||||
if( pData==0 ) return;
|
||||
hash_step(&cx, pData, nByte);
|
||||
if( sqlite3_user_data(context)!=0 ){
|
||||
/* sha1b() - binary result */
|
||||
hash_finish(&cx, zOut, 1);
|
||||
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
/* sha1() - hexadecimal text result */
|
||||
hash_finish(&cx, zOut, 0);
|
||||
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +315,6 @@ static void sha1QueryFunc(
|
||||
}
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
z = sqlite3_sql(pStmt);
|
||||
if( z==0 ) z = "";
|
||||
n = (int)strlen(z);
|
||||
hash_step_vformat(&cx,"S%d:",n);
|
||||
hash_step(&cx,(unsigned char*)z,n);
|
||||
|
||||
+11
-21
@@ -32,7 +32,6 @@ SQLITE_EXTENSION_INIT1
|
||||
# define NEVER(X) 0
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef sqlite3_int64 i64;
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
@@ -193,7 +192,7 @@ static const unsigned char className[] = ".ABCDHLRMY9 ?";
|
||||
** Return NULL if memory allocation fails.
|
||||
*/
|
||||
static unsigned char *phoneticHash(const unsigned char *zIn, int nIn){
|
||||
unsigned char *zOut = sqlite3_malloc64( (i64)nIn + 1 );
|
||||
unsigned char *zOut = sqlite3_malloc64( nIn + 1 );
|
||||
int i;
|
||||
int nOut = 0;
|
||||
char cPrev = 0x77;
|
||||
@@ -353,7 +352,6 @@ static int substituteCost(char cPrev, char cFrom, char cTo){
|
||||
** -1 One of the inputs is NULL
|
||||
** -2 Non-ASCII characters on input
|
||||
** -3 Unable to allocate memory
|
||||
** -4 Inputs too large
|
||||
**
|
||||
** If pnMatch is not NULL, then *pnMatch is set to the number of bytes
|
||||
** of zB that matched the pattern in zA. If zA does not end with a '*',
|
||||
@@ -392,11 +390,9 @@ static int editdist1(const char *zA, const char *zB, int *pnMatch){
|
||||
for(nA=0; zA[nA]; nA++){
|
||||
if( zA[nA]&0x80 ) return -2;
|
||||
}
|
||||
if( nA>=100000 ) return -4;
|
||||
for(nB=0; zB[nB]; nB++){
|
||||
if( zB[nB]&0x80 ) return -2;
|
||||
}
|
||||
if( nB>=100000 ) return -4;
|
||||
|
||||
/* Special processing if either string is empty */
|
||||
if( nA==0 ){
|
||||
@@ -423,7 +419,7 @@ static int editdist1(const char *zA, const char *zB, int *pnMatch){
|
||||
if( nB<(sizeof(mStack)*4)/(sizeof(mStack[0])*5) ){
|
||||
m = mStack;
|
||||
}else{
|
||||
m = toFree = sqlite3_malloc64( ((i64)nB+1)*5LL*sizeof(m[0])/4 );
|
||||
m = toFree = sqlite3_malloc64( (nB+1)*5*sizeof(m[0])/4 );
|
||||
if( m==0 ) return -3;
|
||||
}
|
||||
cx = (char*)&m[nB+1];
|
||||
@@ -531,8 +527,6 @@ static void editdistSqlFunc(
|
||||
if( res<0 ){
|
||||
if( res==(-3) ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else if( res==(-4) ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else if( res==(-2) ){
|
||||
sqlite3_result_error(context, "non-ASCII input to editdist()", -1);
|
||||
}else{
|
||||
@@ -773,7 +767,7 @@ static int editDist3ConfigLoad(
|
||||
if( iCost>=10000 ) continue; /* Costs above 10K are considered infinite */
|
||||
if( pLang==0 || iLang!=iLangPrev ){
|
||||
EditDist3Lang *pNew;
|
||||
pNew = sqlite3_realloc64(p->a, ((i64)p->nLang+1)*sizeof(p->a[0]));
|
||||
pNew = sqlite3_realloc64(p->a, (p->nLang+1)*sizeof(p->a[0]));
|
||||
if( pNew==0 ){ rc = SQLITE_NOMEM; break; }
|
||||
p->a = pNew;
|
||||
pLang = &p->a[p->nLang];
|
||||
@@ -907,7 +901,7 @@ static EditDist3FromString *editDist3FromStringNew(
|
||||
|
||||
if( z==0 ) return 0;
|
||||
if( n<0 ) n = (int)strlen(z);
|
||||
pStr = sqlite3_malloc64( sizeof(*pStr) + sizeof(pStr->a[0])*n + (i64)n + 1 );
|
||||
pStr = sqlite3_malloc64( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 );
|
||||
if( pStr==0 ) return 0;
|
||||
pStr->a = (EditDist3From*)&pStr[1];
|
||||
memset(pStr->a, 0, sizeof(pStr->a[0])*n);
|
||||
@@ -933,13 +927,13 @@ static EditDist3FromString *editDist3FromStringNew(
|
||||
if( matchFrom(p, z+i, n-i)==0 ) continue;
|
||||
if( p->nTo==0 ){
|
||||
apNew = sqlite3_realloc64(pFrom->apDel,
|
||||
sizeof(*apNew)*((i64)pFrom->nDel+1));
|
||||
sizeof(*apNew)*(pFrom->nDel+1));
|
||||
if( apNew==0 ) break;
|
||||
pFrom->apDel = apNew;
|
||||
apNew[pFrom->nDel++] = p;
|
||||
}else{
|
||||
apNew = sqlite3_realloc64(pFrom->apSubst,
|
||||
sizeof(*apNew)*((i64)pFrom->nSubst+1));
|
||||
sizeof(*apNew)*(pFrom->nSubst+1));
|
||||
if( apNew==0 ) break;
|
||||
pFrom->apSubst = apNew;
|
||||
apNew[pFrom->nSubst++] = p;
|
||||
@@ -1014,8 +1008,6 @@ static int editDist3Core(
|
||||
unsigned int stackSpace[SQLITE_SPELLFIX_STACKALLOC_SZ/sizeof(unsigned int)];
|
||||
|
||||
/* allocate the Wagner matrix and the aTo[] array for the TO string */
|
||||
if( n2>10000 ) return -2;
|
||||
if( f.n>10000 ) return -2;
|
||||
n = (f.n+1)*(n2+1);
|
||||
n = (n+1)&~1;
|
||||
nByte = n*sizeof(m[0]) + sizeof(a2[0])*n2;
|
||||
@@ -1206,8 +1198,6 @@ static void editDist3SqlFunc(
|
||||
editDist3FromStringDelete(pFrom);
|
||||
if( dist==(-1) ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else if( dist==(-2) ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else{
|
||||
sqlite3_result_int(context, dist);
|
||||
}
|
||||
@@ -1722,9 +1712,9 @@ static const Transliteration *spellfixFindTranslit(int c, int *pxTop){
|
||||
*/
|
||||
static unsigned char *transliterate(const unsigned char *zIn, int nIn){
|
||||
#ifdef SQLITE_SPELLFIX_5BYTE_MAPPINGS
|
||||
unsigned char *zOut = sqlite3_malloc64( (i64)nIn*5 + 1 );
|
||||
unsigned char *zOut = sqlite3_malloc64( nIn*5 + 1 );
|
||||
#else
|
||||
unsigned char *zOut = sqlite3_malloc64( (i64)nIn*4 + 1 );
|
||||
unsigned char *zOut = sqlite3_malloc64( nIn*4 + 1 );
|
||||
#endif
|
||||
int c, sz, nOut;
|
||||
if( zOut==0 ) return 0;
|
||||
@@ -2067,7 +2057,7 @@ static int spellfix1Init(
|
||||
int i;
|
||||
|
||||
nDbName = (int)strlen(zDbName);
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) + (i64)nDbName + 1);
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) + nDbName + 1);
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -2471,7 +2461,7 @@ static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){
|
||||
iDist = editdist1(p->zPattern, zK1, 0);
|
||||
}
|
||||
if( iDist<0 ){
|
||||
p->rc = iDist==(-4) ? SQLITE_TOOBIG : SQLITE_NOMEM;
|
||||
p->rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
pCur->nSearch++;
|
||||
@@ -2783,7 +2773,7 @@ static int spellfix1Column(
|
||||
if( !zTranslit ) return SQLITE_NOMEM;
|
||||
res = editdist1(pCur->zPattern, zTranslit, &iMatchlen);
|
||||
sqlite3_free(zTranslit);
|
||||
if( res<0 ) return res==(-4) ? SQLITE_TOOBIG : SQLITE_NOMEM;
|
||||
if( res<0 ) return SQLITE_NOMEM;
|
||||
iMatchlen = translen_to_charlen(zWord, nWord, iMatchlen);
|
||||
}else{
|
||||
iMatchlen = utf8Charlen(zWord, nWord);
|
||||
|
||||
+3
-3
@@ -46,7 +46,7 @@ static void sqlarCompressFunc(
|
||||
uLongf nOut = compressBound(nData);
|
||||
Bytef *pOut;
|
||||
|
||||
pOut = (Bytef*)sqlite3_malloc64(nOut);
|
||||
pOut = (Bytef*)sqlite3_malloc(nOut);
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
@@ -84,14 +84,14 @@ static void sqlarUncompressFunc(
|
||||
sqlite3_int64 sz;
|
||||
|
||||
assert( argc==2 );
|
||||
sz = sqlite3_value_int64(argv[1]);
|
||||
sz = sqlite3_value_int(argv[1]);
|
||||
|
||||
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_malloc64(sz);
|
||||
Bytef *pOut = sqlite3_malloc(sz);
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){
|
||||
|
||||
@@ -101,8 +101,8 @@ FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
|
||||
|
||||
sz1 = (int)strlen(zFilename);
|
||||
sz2 = (int)strlen(zMode);
|
||||
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
|
||||
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
|
||||
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
|
||||
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
|
||||
if( b1 && b2 ){
|
||||
sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
|
||||
b1[sz1] = 0;
|
||||
@@ -127,8 +127,8 @@ FILE *sqlite3_popen(const char *zCommand, const char *zMode){
|
||||
|
||||
sz1 = (int)strlen(zCommand);
|
||||
sz2 = (int)strlen(zMode);
|
||||
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
|
||||
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
|
||||
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
|
||||
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
|
||||
if( b1 && b2 ){
|
||||
sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
|
||||
b1[sz1] = 0;
|
||||
@@ -151,7 +151,7 @@ char *sqlite3_fgets(char *buf, int sz, FILE *in){
|
||||
** that into UTF-8. Otherwise, non-ASCII characters all get translated
|
||||
** into '?'.
|
||||
*/
|
||||
wchar_t *b1 = sqlite3_malloc64( sz*sizeof(wchar_t) );
|
||||
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
|
||||
if( b1==0 ) return 0;
|
||||
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
|
||||
DWORD nRead = 0;
|
||||
@@ -226,7 +226,7 @@ int sqlite3_fputs(const char *z, FILE *out){
|
||||
** to the console on Windows.
|
||||
*/
|
||||
int sz = (int)strlen(z);
|
||||
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(wchar_t) );
|
||||
wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
|
||||
if( b1==0 ) return 0;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
@@ -258,7 +258,7 @@ int sqlite3_fputs(const char *z, FILE *out){
|
||||
|
||||
|
||||
/*
|
||||
** Work-alikes for fprintf() and vfprintf() from the standard C library.
|
||||
** Work-alike for fprintf() from the standard C library.
|
||||
*/
|
||||
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
|
||||
int rc;
|
||||
@@ -285,24 +285,6 @@ int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){
|
||||
int rc;
|
||||
if( UseWtextForOutput(out) ){
|
||||
/* When writing to the command-prompt in Windows, it is necessary
|
||||
** to use _O_WTEXT input mode and write UTF-16 characters.
|
||||
*/
|
||||
char *z;
|
||||
z = sqlite3_vmprintf(zFormat, ap);
|
||||
sqlite3_fputs(z, out);
|
||||
rc = (int)strlen(z);
|
||||
sqlite3_free(z);
|
||||
}else{
|
||||
/* Writing to a file or other destination, just write bytes without
|
||||
** any translation. */
|
||||
rc = vfprintf(out, zFormat, ap);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the mode for an output stream. mode argument is typically _O_BINARY or
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#ifdef _WIN32
|
||||
/**** Definitions For Windows ****/
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <windows.h>
|
||||
|
||||
FILE *sqlite3_fopen(const char *zFilename, const char *zMode);
|
||||
@@ -39,7 +38,6 @@ FILE *sqlite3_popen(const char *zCommand, const char *type);
|
||||
char *sqlite3_fgets(char *s, int size, FILE *stream);
|
||||
int sqlite3_fputs(const char *s, FILE *stream);
|
||||
int sqlite3_fprintf(FILE *stream, const char *format, ...);
|
||||
int sqlite3_vfprintf(FILE *stream, const char *format, va_list);
|
||||
void sqlite3_fsetmode(FILE *stream, int mode);
|
||||
|
||||
|
||||
@@ -51,7 +49,6 @@ void sqlite3_fsetmode(FILE *stream, int mode);
|
||||
#define sqlite3_fgets fgets
|
||||
#define sqlite3_fputs fputs
|
||||
#define sqlite3_fprintf fprintf
|
||||
#define sqlite3_vfprintf vfprintf
|
||||
#define sqlite3_fsetmode(F,X) /*no-op*/
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ static void stmtrandFunc(
|
||||
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
|
||||
if( p==0 ){
|
||||
unsigned int seed;
|
||||
p = sqlite3_malloc64( sizeof(*p) );
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
|
||||
@@ -101,7 +101,7 @@ static int templatevtabConnect(
|
||||
#define TEMPLATEVTAB_A 0
|
||||
#define TEMPLATEVTAB_B 1
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -123,7 +123,7 @@ static int templatevtabDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int templatevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
templatevtab_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -120,7 +120,6 @@ static int sqlite3UuidStrToBlob(
|
||||
unsigned char *aBlob /* Write results here */
|
||||
){
|
||||
int i;
|
||||
if( zStr==0 ) return 1;
|
||||
if( zStr[0]=='{' ) zStr++;
|
||||
for(i=0; i<16; i++){
|
||||
if( zStr[0]=='-' ) zStr++;
|
||||
|
||||
+2
-2
@@ -613,7 +613,7 @@ static int vstattabConnect(
|
||||
|
||||
rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
}
|
||||
@@ -633,7 +633,7 @@ static int vstattabDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
VfsStatCursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
|
||||
+1
-1
@@ -892,7 +892,7 @@ static int vfstraceOpen(
|
||||
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
|
||||
pInfo->zVfsName, p->zFName, flags);
|
||||
if( p->pReal->pMethods ){
|
||||
sqlite3_io_methods *pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
const sqlite3_io_methods *pSub = p->pReal->pMethods;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->iVersion = pSub->iVersion;
|
||||
|
||||
+8
-50
@@ -14,8 +14,6 @@
|
||||
** on stdout when its key interfaces are called. This is intended for
|
||||
** interactive analysis and debugging of virtual table interfaces.
|
||||
**
|
||||
** HOW TO COMPILE:
|
||||
**
|
||||
** To build this extension as a separately loaded shared library or
|
||||
** DLL, use compiler command-lines similar to the following:
|
||||
**
|
||||
@@ -23,7 +21,7 @@
|
||||
** (mac) clang -fPIC -dynamiclib vtablog.c -o vtablog.dylib
|
||||
** (windows) cl vtablog.c -link -dll -out:vtablog.dll
|
||||
**
|
||||
** USAGE EXAMPLE:
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./vtablog
|
||||
** CREATE VIRTUAL TABLE temp.log USING vtablog(
|
||||
@@ -31,23 +29,6 @@
|
||||
** rows=25
|
||||
** );
|
||||
** SELECT * FROM log;
|
||||
**
|
||||
** ARGUMENTS TO CREATE VIRTUAL TABLE:
|
||||
**
|
||||
** In "CREATE VIRTUAL TABLE temp.log AS vtablog(ARGS....)" statement, the
|
||||
** ARGS argument is a list of key-value pairs that can be any of the
|
||||
** following.
|
||||
**
|
||||
** schema=TEXT Text is a CREATE TABLE statement that defines
|
||||
** the schema of the new virtual table.
|
||||
**
|
||||
** rows=N The table as N rows.
|
||||
**
|
||||
** consume_order_by=N If the left-most ORDER BY terms is ASC and
|
||||
** against column N (where the leftmost column
|
||||
** is #1) then set the orderByConsumed=1 flag in
|
||||
** xBestIndex. Or if the left-most ORDER BY is
|
||||
** DESC and against column -N, do likewise.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
@@ -68,8 +49,6 @@ struct vtablog_vtab {
|
||||
char *zName; /* Table name. argv[2] of xConnect/xCreate */
|
||||
int nRow; /* Number of rows in the table */
|
||||
int nCursor; /* Number of cursors created */
|
||||
int iConsumeOB; /* Consume the ORDER BY clause if on column N-th
|
||||
** and consumeOB=N or consumeOB=(-N) and DESC */
|
||||
};
|
||||
|
||||
/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
|
||||
@@ -201,7 +180,6 @@ static int vtablogConnectCreate(
|
||||
int rc;
|
||||
char *zSchema = 0;
|
||||
char *zNRow = 0;
|
||||
char *zConsumeOB = 0;
|
||||
|
||||
printf("%s.%s.%s():\n", argv[1], argv[2],
|
||||
isCreate ? "xCreate" : "xConnect");
|
||||
@@ -225,10 +203,6 @@ static int vtablogConnectCreate(
|
||||
rc = SQLITE_ERROR;
|
||||
goto vtablog_end_connect;
|
||||
}
|
||||
if( vtablog_string_parameter(pzErr, "consume_order_by", z, &zConsumeOB) ){
|
||||
rc = SQLITE_ERROR;
|
||||
goto vtablog_end_connect;
|
||||
}
|
||||
}
|
||||
if( zSchema==0 ){
|
||||
zSchema = sqlite3_mprintf("%s","CREATE TABLE x(a,b);");
|
||||
@@ -240,17 +214,13 @@ static int vtablogConnectCreate(
|
||||
printf(" schema = '%s'\n", zSchema);
|
||||
rc = sqlite3_declare_vtab(db, zSchema);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->nRow = 10;
|
||||
if( zNRow ) pNew->nRow = atoi(zNRow);
|
||||
printf(" nrow = %d\n", pNew->nRow);
|
||||
if( zConsumeOB ) pNew->iConsumeOB = atoi(zConsumeOB);
|
||||
if( pNew->iConsumeOB ){
|
||||
printf(" consume_order_by = %d\n", pNew->iConsumeOB);
|
||||
}
|
||||
pNew->zDb = sqlite3_mprintf("%s", argv[1]);
|
||||
pNew->zName = sqlite3_mprintf("%s", argv[2]);
|
||||
}
|
||||
@@ -258,7 +228,6 @@ static int vtablogConnectCreate(
|
||||
vtablog_end_connect:
|
||||
sqlite3_free(zSchema);
|
||||
sqlite3_free(zNRow);
|
||||
sqlite3_free(zConsumeOB);
|
||||
return rc;
|
||||
}
|
||||
static int vtablogCreate(
|
||||
@@ -313,7 +282,7 @@ static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
vtablog_cursor *pCur;
|
||||
printf("%s.%s.xOpen(cursor=%d)\n", pTab->zDb, pTab->zName,
|
||||
++pTab->nCursor);
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->iCursor = pTab->nCursor;
|
||||
@@ -545,27 +514,16 @@ static int vtablogBestIndex(
|
||||
}
|
||||
}
|
||||
printf(" nOrderBy: %d\n", p->nOrderBy);
|
||||
if( 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);
|
||||
}
|
||||
if( pTab->iConsumeOB ){
|
||||
int N = p->aOrderBy[0].iColumn+1;
|
||||
if( (p->aOrderBy[0].desc && N==-pTab->iConsumeOB)
|
||||
|| (!p->aOrderBy[0].desc && N==pTab->iConsumeOB)
|
||||
){
|
||||
p->orderByConsumed = 1;
|
||||
}
|
||||
}
|
||||
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(" sqlite3_vtab_distinct()=%d\n", sqlite3_vtab_distinct(p));
|
||||
printf(" orderByConsumed=%d\n", p->orderByConsumed);
|
||||
printf(" estimatedCost=%g\n", p->estimatedCost);
|
||||
printf(" estimatedRows=%lld\n", p->estimatedRows);
|
||||
|
||||
+5
-5
@@ -86,7 +86,7 @@ static int vtshimCreate(
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -125,7 +125,7 @@ static int vtshimConnect(
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
@@ -192,7 +192,7 @@ static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
|
||||
int rc;
|
||||
*ppCursor = 0;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
|
||||
@@ -444,7 +444,7 @@ static int vtshimCopyModule(
|
||||
){
|
||||
sqlite3_module *p;
|
||||
if( !pMod || !ppMod ) return SQLITE_ERROR;
|
||||
p = sqlite3_malloc64( sizeof(*p) );
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
memcpy(p, pMod, sizeof(*p));
|
||||
*ppMod = p;
|
||||
@@ -464,7 +464,7 @@ void *sqlite3_create_disposable_module(
|
||||
vtshim_aux *pAux;
|
||||
sqlite3_module *pMod;
|
||||
int rc;
|
||||
pAux = sqlite3_malloc64( sizeof(*pAux) );
|
||||
pAux = sqlite3_malloc( sizeof(*pAux) );
|
||||
if( pAux==0 ){
|
||||
if( xDestroy ) xDestroy(pClientData);
|
||||
return 0;
|
||||
|
||||
+4
-11
@@ -13,11 +13,6 @@
|
||||
** This file implements a virtual table that returns the whole numbers
|
||||
** between 1 and 4294967295, inclusive.
|
||||
**
|
||||
** TESTING AND DEBUG USE ONLY -> This is not production code. This is
|
||||
** not a deliverable. Use the generate_series() virtual table for
|
||||
** real-world applications instead of this extension. THIS EXTENSION
|
||||
** MAY CONTAIN BUGS.
|
||||
**
|
||||
** Example:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE nums USING wholenumber;
|
||||
@@ -52,7 +47,7 @@ static int wholenumberConnect(
|
||||
char **pzErr
|
||||
){
|
||||
sqlite3_vtab *pNew;
|
||||
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
@@ -74,7 +69,7 @@ static int wholenumberDisconnect(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
wholenumber_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
@@ -159,14 +154,12 @@ static int wholenumberFilter(
|
||||
pCur->iValue = 1;
|
||||
pCur->mxValue = 0xffffffff; /* 4294967295 */
|
||||
if( idxNum & 3 ){
|
||||
v = sqlite3_value_int64(argv[0]);
|
||||
if( v<=pCur->mxValue && (idxNum&1)!=0 ) v++;
|
||||
v = sqlite3_value_int64(argv[0]) + (idxNum&1);
|
||||
if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v;
|
||||
i++;
|
||||
}
|
||||
if( idxNum & 12 ){
|
||||
v = sqlite3_value_int64(argv[i]);
|
||||
if( v>0 && ((idxNum>>2)&1)!=0 ) v--;
|
||||
v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1);
|
||||
if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
|
||||
+35
-48
@@ -393,7 +393,7 @@ static int zipfileConnect(
|
||||
|
||||
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile);
|
||||
pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, nByte+nFile);
|
||||
pNew->db = db;
|
||||
@@ -456,7 +456,7 @@ static int zipfileDisconnect(sqlite3_vtab *pVtab){
|
||||
static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
|
||||
ZipfileTab *pTab = (ZipfileTab*)p;
|
||||
ZipfileCsr *pCsr;
|
||||
pCsr = sqlite3_malloc64(sizeof(*pCsr));
|
||||
pCsr = sqlite3_malloc(sizeof(*pCsr));
|
||||
*ppCsr = (sqlite3_vtab_cursor*)pCsr;
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
@@ -488,7 +488,6 @@ static void zipfileResetCursor(ZipfileCsr *pCsr){
|
||||
pNext = p->pNext;
|
||||
zipfileEntryFree(p);
|
||||
}
|
||||
pCsr->pFreeEntry = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -540,15 +539,14 @@ static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
|
||||
static int zipfileReadData(
|
||||
FILE *pFile, /* Read from this file */
|
||||
u8 *aRead, /* Read into this buffer */
|
||||
i64 nRead, /* Number of bytes to read */
|
||||
int nRead, /* Number of bytes to read */
|
||||
i64 iOff, /* Offset to read from */
|
||||
char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */
|
||||
){
|
||||
size_t n;
|
||||
fseek(pFile, (long)iOff, SEEK_SET);
|
||||
n = fread(aRead, 1, (long)nRead, pFile);
|
||||
if( n!=(size_t)nRead ){
|
||||
sqlite3_free(*pzErrmsg);
|
||||
n = fread(aRead, 1, nRead, pFile);
|
||||
if( (int)n!=nRead ){
|
||||
*pzErrmsg = sqlite3_mprintf("error in fread()");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@@ -565,7 +563,7 @@ static int zipfileAppendData(
|
||||
fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
|
||||
n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
|
||||
if( (int)n!=nWrite ){
|
||||
zipfileTableErr(pTab,"error in fwrite()");
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pTab->szCurrent += nWrite;
|
||||
@@ -706,12 +704,7 @@ static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
|
||||
u8 *p = aExtra;
|
||||
u8 *pEnd = &aExtra[nExtra];
|
||||
|
||||
/* Stop when there are less than 9 bytes left to scan in the buffer. This
|
||||
** is because the timestamp field requires exactly 9 bytes - 4 bytes of
|
||||
** header fields and 5 bytes of data. If there are less than 9 bytes
|
||||
** remaining, either it is some other field or else the extra data
|
||||
** is corrupt. Either way, do not process it. */
|
||||
while( p+(2*sizeof(u16) + 1 + sizeof(u32))<=pEnd ){
|
||||
while( p<pEnd ){
|
||||
u16 id = zipfileRead16(p);
|
||||
u16 nByte = zipfileRead16(p);
|
||||
|
||||
@@ -817,7 +810,6 @@ static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
|
||||
** generic corruption message and return SQLITE_CORRUPT;
|
||||
*/
|
||||
static int zipfileCorrupt(char **pzErr){
|
||||
sqlite3_free(*pzErr);
|
||||
*pzErr = sqlite3_mprintf("zip archive is corrupt");
|
||||
return SQLITE_CORRUPT;
|
||||
}
|
||||
@@ -836,7 +828,7 @@ static int zipfileCorrupt(char **pzErr){
|
||||
static int zipfileGetEntry(
|
||||
ZipfileTab *pTab, /* Store any error message here */
|
||||
const u8 *aBlob, /* Pointer to in-memory file image */
|
||||
i64 nBlob, /* Size of aBlob[] in bytes */
|
||||
int nBlob, /* Size of aBlob[] in bytes */
|
||||
FILE *pFile, /* If aBlob==0, read from this file */
|
||||
i64 iOff, /* Offset of CDS record */
|
||||
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
|
||||
@@ -876,14 +868,14 @@ static int zipfileGetEntry(
|
||||
memset(pNew, 0, sizeof(ZipfileEntry));
|
||||
rc = zipfileReadCDS(aRead, &pNew->cds);
|
||||
if( rc!=SQLITE_OK ){
|
||||
zipfileTableErr(pTab, "failed to read CDS at offset %lld", iOff);
|
||||
*pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
|
||||
}else if( aBlob==0 ){
|
||||
rc = zipfileReadData(
|
||||
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
|
||||
);
|
||||
}else{
|
||||
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
|
||||
if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){
|
||||
if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){
|
||||
rc = zipfileCorrupt(pzErr);
|
||||
}
|
||||
}
|
||||
@@ -891,13 +883,7 @@ static int zipfileGetEntry(
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
u32 *pt = &pNew->mUnixTime;
|
||||
/* aRead[0..nFile-1] might contain embedded \000 characters
|
||||
** See Bug 2026-05-31T11:43:05Z */
|
||||
pNew->cds.zFile = sqlite3_malloc64(nFile+1);
|
||||
if( pNew->cds.zFile!=0 ){
|
||||
memcpy(pNew->cds.zFile, aRead, nFile);
|
||||
pNew->cds.zFile[nFile] = 0;
|
||||
}
|
||||
pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead);
|
||||
pNew->aExtra = (u8*)&pNew[1];
|
||||
memcpy(pNew->aExtra, &aRead[nFile], nExtra);
|
||||
if( pNew->cds.zFile==0 ){
|
||||
@@ -914,15 +900,14 @@ static int zipfileGetEntry(
|
||||
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
|
||||
}else{
|
||||
aRead = (u8*)&aBlob[pNew->cds.iOffset];
|
||||
if( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
|
||||
if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
|
||||
rc = zipfileCorrupt(pzErr);
|
||||
}
|
||||
}
|
||||
|
||||
memset(&lfh, 0, sizeof(lfh));
|
||||
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
|
||||
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
|
||||
pNew->iDataOff += lfh.nFile + lfh.nExtra;
|
||||
if( aBlob && pNew->cds.szCompressed ){
|
||||
if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
|
||||
@@ -933,7 +918,7 @@ static int zipfileGetEntry(
|
||||
}
|
||||
}
|
||||
}else{
|
||||
zipfileTableErr(pTab, "failed to read LFH at offset %d",
|
||||
*pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
|
||||
(int)pNew->cds.iOffset
|
||||
);
|
||||
}
|
||||
@@ -957,7 +942,7 @@ static int zipfileNext(sqlite3_vtab_cursor *cur){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( pCsr->pFile ){
|
||||
i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize;
|
||||
i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
|
||||
zipfileEntryFree(pCsr->pCurrent);
|
||||
pCsr->pCurrent = 0;
|
||||
if( pCsr->iNextOff>=iEof ){
|
||||
@@ -1002,7 +987,7 @@ static void zipfileInflate(
|
||||
int nIn, /* Size of buffer aIn[] in bytes */
|
||||
int nOut /* Expected output size */
|
||||
){
|
||||
u8 *aRes = sqlite3_malloc64(nOut);
|
||||
u8 *aRes = sqlite3_malloc(nOut);
|
||||
if( aRes==0 ){
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
}else{
|
||||
@@ -1023,7 +1008,7 @@ static void zipfileInflate(
|
||||
if( err!=Z_STREAM_END ){
|
||||
zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err);
|
||||
}else{
|
||||
sqlite3_result_blob(pCtx, aRes, (int)str.total_out, zipfileFree);
|
||||
sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree);
|
||||
aRes = 0;
|
||||
}
|
||||
}
|
||||
@@ -1195,12 +1180,12 @@ static int zipfileEof(sqlite3_vtab_cursor *cur){
|
||||
static int zipfileReadEOCD(
|
||||
ZipfileTab *pTab, /* Return errors here */
|
||||
const u8 *aBlob, /* Pointer to in-memory file image */
|
||||
i64 nBlob, /* Size of aBlob[] in bytes */
|
||||
int nBlob, /* Size of aBlob[] in bytes */
|
||||
FILE *pFile, /* Read from this file if aBlob==0 */
|
||||
ZipfileEOCD *pEOCD /* Object to populate */
|
||||
){
|
||||
u8 *aRead = pTab->aBuffer; /* Temporary buffer */
|
||||
i64 nRead; /* Bytes to read from file */
|
||||
int nRead; /* Bytes to read from file */
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
memset(pEOCD, 0, sizeof(ZipfileEOCD));
|
||||
@@ -1221,7 +1206,7 @@ static int zipfileReadEOCD(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
i64 i;
|
||||
int i;
|
||||
|
||||
/* Scan backwards looking for the signature bytes */
|
||||
for(i=nRead-20; i>=0; i--){
|
||||
@@ -1232,7 +1217,9 @@ static int zipfileReadEOCD(
|
||||
}
|
||||
}
|
||||
if( i<0 ){
|
||||
zipfileTableErr(pTab, "cannot find end of central directory record");
|
||||
pTab->base.zErrMsg = sqlite3_mprintf(
|
||||
"cannot find end of central directory record"
|
||||
);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@@ -1277,7 +1264,7 @@ static void zipfileAddEntry(
|
||||
}
|
||||
}
|
||||
|
||||
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){
|
||||
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){
|
||||
ZipfileEOCD eocd;
|
||||
int rc;
|
||||
int i;
|
||||
@@ -1325,7 +1312,7 @@ static int zipfileFilter(
|
||||
}else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
|
||||
static const u8 aEmptyBlob = 0;
|
||||
const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
|
||||
i64 nBlob = sqlite3_value_bytes(argv[0]);
|
||||
int nBlob = sqlite3_value_bytes(argv[0]);
|
||||
assert( pTab->pFirstEntry==0 );
|
||||
if( aBlob==0 ){
|
||||
aBlob = &aEmptyBlob;
|
||||
@@ -1399,7 +1386,7 @@ static int zipfileBestIndex(
|
||||
|
||||
static ZipfileEntry *zipfileNewEntry(const char *zPath){
|
||||
ZipfileEntry *pNew;
|
||||
pNew = sqlite3_malloc64(sizeof(ZipfileEntry));
|
||||
pNew = sqlite3_malloc(sizeof(ZipfileEntry));
|
||||
if( pNew ){
|
||||
memset(pNew, 0, sizeof(ZipfileEntry));
|
||||
pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
|
||||
@@ -1523,7 +1510,7 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
|
||||
|
||||
assert( pTab->pWriteFd==0 );
|
||||
if( pTab->zFile==0 || pTab->zFile[0]==0 ){
|
||||
zipfileTableErr(pTab, "zipfile: missing filename");
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@@ -1533,9 +1520,9 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
|
||||
** in main-memory until the transaction is committed. */
|
||||
pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+");
|
||||
if( pTab->pWriteFd==0 ){
|
||||
zipfileTableErr(pTab,
|
||||
"zipfile: failed to open file %s for writing", pTab->zFile
|
||||
);
|
||||
pTab->base.zErrMsg = sqlite3_mprintf(
|
||||
"zipfile: failed to open file %s for writing", pTab->zFile
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
fseek(pTab->pWriteFd, 0, SEEK_END);
|
||||
@@ -2000,11 +1987,11 @@ struct ZipfileCtx {
|
||||
ZipfileBuffer cds;
|
||||
};
|
||||
|
||||
static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){
|
||||
if( (pBuf->nAlloc-pBuf->n)<nByte ){
|
||||
static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
|
||||
if( pBuf->n+nByte>pBuf->nAlloc ){
|
||||
u8 *aNew;
|
||||
i64 nNew = pBuf->n ? (i64)pBuf->n*2 : 512;
|
||||
i64 nReq = pBuf->n + nByte;
|
||||
sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512;
|
||||
int nReq = pBuf->n + nByte;
|
||||
|
||||
while( nNew<nReq ) nNew = nNew*2;
|
||||
aNew = sqlite3_realloc64(pBuf->a, nNew);
|
||||
@@ -2049,7 +2036,7 @@ static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
|
||||
char *zName = 0; /* Path (name) of new entry */
|
||||
int nName = 0; /* Size of zName in bytes */
|
||||
char *zFree = 0; /* Free this before returning */
|
||||
i64 nByte;
|
||||
int nByte;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
|
||||
|
||||
@@ -1,775 +0,0 @@
|
||||
# SQLite Query Result Formatting Subsystem
|
||||
|
||||
The "Query Result Formatter" or "QRF" subsystem is a C-language
|
||||
subroutine that formats the output from an SQLite query for display using
|
||||
a fix-width font, for example on a terminal window over an SSH connection.
|
||||
The output format is configurable. The application can request various
|
||||
table formats, with flexible column widths and alignments, row-oriented
|
||||
formats, such as CSV and similar, as well as various special purpose formats
|
||||
like JSON.
|
||||
|
||||
For the first 25 years of SQLite's existance, the
|
||||
[command-line interface](https://sqlite.org/cli.html) (CLI)
|
||||
formatted query results using a hodge-podge of routines
|
||||
that had grown slowly by accretion. The QRF was created
|
||||
in fall of 2025 to refactor and reorganize this code into
|
||||
a more usable form. The idea behind QRF is to implement all the
|
||||
query result formatting capabilities of the CLI in a subroutine
|
||||
that can be incorporated and reused by other applications.
|
||||
|
||||
## 1.0 Overview Of Operation
|
||||
|
||||
Suppose variable `sqlite3_stmt *pStmt` is a pointer to an SQLite
|
||||
prepared statement that has been reset and bound and is ready to run.
|
||||
Then to format the output from this prepared statement, use code
|
||||
similar to the following:
|
||||
|
||||
> ~~~
|
||||
sqlite3_qrf_spec spec; /* Format specification */
|
||||
char *zErrMsg; /* Text error message (optional) */
|
||||
char *zResult = 0; /* Formatted output written here */
|
||||
int rc; /* Result code */
|
||||
|
||||
memset(&spec, 0, sizeof(spec)); /* Initialize the spec */
|
||||
spec.iVersion = 1; /* Version number must be 1 */
|
||||
spec.pzOutput = &zResult; /* Write results in variable zResult */
|
||||
/* Optionally fill in other settings in spec here, as needed */
|
||||
zErrMsg = 0; /* Not required; just being pedantic */
|
||||
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Format results */
|
||||
if( rc ){
|
||||
printf("Error (%d): %s\n", rc, zErrMsg); /* Report an error */
|
||||
sqlite3_free(zErrMsg); /* Free the error message text */
|
||||
}else{
|
||||
printf("%s", zResult); /* Report the results */
|
||||
}
|
||||
sqlite3_free(zResult); /* Free memory used to hold results */
|
||||
~~~
|
||||
|
||||
The `sqlite3_qrf_spec` object describes the desired output format
|
||||
and where to send the generated output. Most of the work in using
|
||||
the QRF involves filling out the sqlite3_qrf_spec.
|
||||
|
||||
### 1.1 Using QRF with SQL text
|
||||
|
||||
If you start with SQL text instead of an sqlite3_stmt pointer, and
|
||||
especially if the SQL text might comprise two or more statements, then
|
||||
the SQL text needs to be converted into sqlite3_stmt objects separately.
|
||||
If the original SQL text is in a variable `const char *zSql` and the
|
||||
database connection is in variable `sqlite3 *db`, then code
|
||||
similar to the following should work:
|
||||
|
||||
> ~~~
|
||||
sqlite3_qrf_spec spec; /* Format specification */
|
||||
char *zErrMsg; /* Text error message (optional) */
|
||||
char *zResult = 0; /* Formatted output written here */
|
||||
sqlite3_stmt *pStmt; /* Next prepared statement */
|
||||
int rc; /* Result code */
|
||||
|
||||
memset(&spec, 0, sizeof(spec)); /* Initialize the spec */
|
||||
spec.iVersion = 1; /* Version number must be 1 */
|
||||
spec.pzOutput = &zResult; /* Write results in variable zResult */
|
||||
/* Optionally fill in other settings in spec here, as needed */
|
||||
zErrMsg = 0; /* Not required; just being pedantic */
|
||||
while( zSql && zSql[0] ){
|
||||
pStmt = 0; /* Not required; just being pedantic */
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
|
||||
if( rc!=SQLITE_OK ){
|
||||
printf("Error: %s\n", sqlite3_errmsg(db));
|
||||
}else{
|
||||
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Get results */
|
||||
if( rc ){
|
||||
printf("Error (%d): %s\n", rc, zErrMsg); /* Report an error */
|
||||
sqlite3_free(zErrMsg); /* Free the error message text */
|
||||
}else{
|
||||
printf("%s", zResult); /* Report the results */
|
||||
sqlite3_free(zResult); /* Free memory used to hold results */
|
||||
zResult = 0;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
~~~
|
||||
|
||||
<a id="spec"></a>
|
||||
## 2.0 The `sqlite3_qrf_spec` object
|
||||
|
||||
The `sqlite3_qrf_spec` looks like this:
|
||||
|
||||
> ~~~
|
||||
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
|
||||
struct sqlite3_qrf_spec {
|
||||
unsigned char iVersion; /* Version number of this structure */
|
||||
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
|
||||
unsigned char eEsc; /* How to escape control characters in text */
|
||||
unsigned char eText; /* Quoting style for text */
|
||||
unsigned char eTitle; /* Quating style for the text of column names */
|
||||
unsigned char eBlob; /* Quoting style for BLOBs */
|
||||
unsigned char bTitles; /* True to show column names */
|
||||
unsigned char bWordWrap; /* Try to wrap on word boundaries */
|
||||
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
|
||||
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
|
||||
unsigned char eTitleAlign; /* Alignment for column headers */
|
||||
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
|
||||
unsigned char bBorder; /* Show outer border in Box and Table styles */
|
||||
short int nWrap; /* Wrap columns wider than this */
|
||||
short int nScreenWidth; /* Maximum overall table width */
|
||||
short int nLineLimit; /* Maximum number of lines for any row */
|
||||
short int nTitleLimit; /* Maximum number of characters in a title */
|
||||
unsigned int nMultiInsert; /* Add rows to one INSERT until size exceeds */
|
||||
int nCharLimit; /* Maximum number of characters in a cell */
|
||||
int nWidth; /* Number of entries in aWidth[] */
|
||||
int nAlign; /* Number of entries in aAlignment[] */
|
||||
short int *aWidth; /* Column widths */
|
||||
unsigned char *aAlign; /* Column alignments */
|
||||
char *zColumnSep; /* Alternative column separator */
|
||||
char *zRowSep; /* Alternative row separator */
|
||||
char *zTableName; /* Output table name */
|
||||
char *zNull; /* Rendering of NULL */
|
||||
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
|
||||
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
|
||||
void *pRenderArg; /* First argument to the xRender callback */
|
||||
void *pWriteArg; /* First argument to the xWrite callback */
|
||||
char **pzOutput; /* Storage location for output string */
|
||||
/* Additional fields may be added in the future */
|
||||
};
|
||||
~~~
|
||||
|
||||
Do not be alarmed by the complexity of this structure. Everything can
|
||||
be zeroed except for:
|
||||
|
||||
* `.iVersion`
|
||||
* One of `.pzOutput` or `.xWrite`.
|
||||
|
||||
You do not need to understand and configure every field of this object
|
||||
in order to use QRF effectively. Start by zeroing out the whole structure,
|
||||
then initializing iVersion and one of pzOutput or xWrite. Then maybe
|
||||
tweak one or two other settings to get the output you want.
|
||||
|
||||
Further detail on the meanings of each of the fields in the
|
||||
`sqlite3_qrf_spec` object is in the subsequent sections.
|
||||
|
||||
### 2.1 Structure Version Number
|
||||
|
||||
The sqlite3_qrf_spec.iVersion field must be 1. Future enhancements to
|
||||
the QRF might add new fields to the bottom of the sqlite3_qrf_spec
|
||||
object. Those new fields will only be accessible if the iVersion is greater
|
||||
than 1. Thus the iVersion field is used to support upgradability.
|
||||
|
||||
### 2.2 Output Deposition (xWrite and pzOutput)
|
||||
|
||||
The formatted output can either be sent to a callback function
|
||||
or accumulated into an output buffer in memory obtained
|
||||
from sqlite3_malloc(). If the sqlite3_qrf_spec.xWrite column is not NULL,
|
||||
then that function is invoked (using sqlite3_qrf_spec.xWriteArg as its
|
||||
first argument) to transmit the formatted output. Or, if
|
||||
sqlite3_qrf_spec.pzOutput points to a pointer to a character, then that
|
||||
pointer is made to point to memory obtained from sqlite3_malloc() that
|
||||
contains the complete text of the formatted output. If spec.pzOutput\[0\]
|
||||
is initially non-NULL, then it is assumed to already point to memory obtained
|
||||
from sqlite3_malloc(). In that case, the buffer is resized using
|
||||
sqlite3_realloc() and the new text is appended.
|
||||
|
||||
One of either sqlite3_qrf_spec.xWrite and sqlite3_qrf_spec.pzOutput must be
|
||||
non-NULL and the other must be NULL.
|
||||
|
||||
The return value from xWrite is an SQLITE result code. The usual return
|
||||
should be SQLITE_OK. But if for some reason the write fails, a different
|
||||
value might be returned.
|
||||
|
||||
### 2.3 Output Format
|
||||
|
||||
The sqlite3_qrf_spec.eStyle field is an integer code that defines the
|
||||
specific output format that will be generated. See [section 4.0](#style)
|
||||
below for details on the meaning of the various style options.
|
||||
|
||||
Other fields in sqlite3_qrf_spec might be used or might be
|
||||
ignored, depending on the value of eStyle.
|
||||
|
||||
### 2.4 Show Column Names (bTitles)
|
||||
|
||||
The sqlite3_qrf_spec.bTitles field can be either QRF_SW_Auto,
|
||||
QRF_SW_On, or QRF_SW_Off. Those three constants also have shorter
|
||||
alternative spellings: QRF_Auto, QRF_No, and
|
||||
QRF_Yes.
|
||||
|
||||
> ~~~
|
||||
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
|
||||
#define QRF_SW_Off 1 /* This setting is forced off */
|
||||
#define QRF_SW_On 2 /* This setting is forced on */
|
||||
#define QRF_Auto 0 /* Alternate spelling for QRF_SW_Auto and others */
|
||||
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
|
||||
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
|
||||
~~~
|
||||
|
||||
If the value is QRF_Yes, then column names appear in the output.
|
||||
If the value is QRF_No, column names are omitted. If the
|
||||
value is QRF_Auto, then an appropriate default is chosen.
|
||||
|
||||
### 2.5 Control Character Escapes (eEsc)
|
||||
|
||||
The sqlite3_qrf_spec.eEsc determines how ASCII control characters are
|
||||
formatted when displaying TEXT values in the result. These are the allowed
|
||||
values:
|
||||
|
||||
> ~~~
|
||||
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
|
||||
#define QRF_ESC_Off 1 /* Do not escape control characters */
|
||||
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
|
||||
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
|
||||
~~~
|
||||
|
||||
If the value of eEsc is QRF_ESC_Ascii, then the control character
|
||||
with value X is displayed as ^Y where Y is X+0x40. Hence, a
|
||||
backspace character (U+0008) is shown as "^H".
|
||||
|
||||
If eEsc is QRF_ESC_Symbol, then control characters in the range of U+0001
|
||||
through U+001f are mapped into U+2401 through U+241f, respectively.
|
||||
|
||||
If the value of eEsc is QRF_ESC_Off, then no translation occurs
|
||||
and control characters that appear in TEXT strings are transmitted
|
||||
to the formatted output as-is. This can be dangerous in applications,
|
||||
since an adversary who can control TEXT values might be able to
|
||||
inject ANSI cursor movement sequences to hide nefarious values.
|
||||
|
||||
The QRF_ESC_Auto value for eEsc means that the query result formatter
|
||||
gets to pick whichever control-character encoding it thinks is best for
|
||||
the situation. This will usually be QRF_ESC_Ascii.
|
||||
|
||||
The TAB (U+0009), LF (U+000a) and CR-LF (U+000d,U+000a) character
|
||||
sequence are always output literally and are not mapped to alternative
|
||||
display values, regardless of this setting.
|
||||
|
||||
### 2.6 Display of TEXT values (eText, eTitle)
|
||||
|
||||
The sqlite3_qrf_spec.eText controls how text values are rendered in the
|
||||
display. sqlite3_qrf_spec.eTitle controls how column names are rendered.
|
||||
Both fields can have one of the following values:
|
||||
|
||||
> ~~~
|
||||
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
|
||||
#define QRF_TEXT_Plain 1 /* Literal text */
|
||||
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
|
||||
#define QRF_TEXT_Html 4 /* HTML-style quoting */
|
||||
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
|
||||
#define QRF_TEXT_Json 6 /* JSON quoting */
|
||||
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
|
||||
~~~
|
||||
|
||||
A value of QRF_TEXT_Auto means that the query result formatter will choose
|
||||
what it thinks will be the best text encoding.
|
||||
|
||||
A value of QRF_TEXT_Plain means that text values appear in the output exactly
|
||||
as they are found in the database file, with no translation.
|
||||
|
||||
A value of QRF_TEXT_Sql means that text values are escaped so that they
|
||||
look like SQL literals. That means the value will be surrounded by
|
||||
single-quotes (U+0027) and any single-quotes contained within the text
|
||||
will be doubled.
|
||||
|
||||
QRF_TEXT_Relaxed is similar to QRF_TEXT_Sql, except that it automatically
|
||||
reverts to QRF_TEXT_Plain if the value to be displayed does not contain
|
||||
special characters and is not easily confused with a NULL or a numeric
|
||||
value. QRF_TEXT_Relaxed strives to minimize the amount of quoting syntax
|
||||
while keeping the result unambiguous and easy for humans to read. The
|
||||
precise rules for when quoting is omitted in QRF_TEXT_Relaxed, and when
|
||||
it is applied, might be adjusted in future releases.
|
||||
|
||||
A value of QRF_TEXT_Csv means that text values are escaped in accordance
|
||||
with RFC 4180, which defines Comma-Separated-Value or CSV files.
|
||||
Text strings that contain no special values appears as-is. Text strings
|
||||
that contain special values are contained in double-quotes (U+0022) and
|
||||
any double-quotes within the value are doubled.
|
||||
|
||||
A value of QRF_TEXT_Html means that text values are escaped for use in
|
||||
HTML. Special characters "<", "&", ">", """, and "'"
|
||||
are displayed as "&lt;", "&amp;", "&gt;", "&quot;",
|
||||
and "&#39;", respectively.
|
||||
|
||||
A value of QRF_TEXT_Tcl means that text values are displayed inside of
|
||||
double-quotes and special characters within the string are escaped using
|
||||
backslash escape, as in ANSI-C or TCL or Perl or other popular programming
|
||||
languages.
|
||||
|
||||
A value of QRF_TEXT_Json gives similar results as QRF_TEXT_Tcl except that the
|
||||
rules are adjusted so that the displayed string is strictly conforming
|
||||
the JSON specification.
|
||||
|
||||
### 2.7 How to display BLOB values (eBlob and bTextJsonb)
|
||||
|
||||
If the sqlite3_qrf_spec.bTextJsonb flag is QRF_SW_On and if the value to be
|
||||
displayed is JSONB, then the JSONB is translated into text JSON and the
|
||||
text is shown according to the sqlite3_qrf_spec.eText setting as
|
||||
described in the previous section.
|
||||
|
||||
If the bTextJsonb flag is QRF_SW_Off (the usual case) or if the BLOB value to
|
||||
be displayed is not JSONB, then the sqlite3_qrf_spec.eBlob field determines
|
||||
how the BLOB value is formatted. The following options are available;
|
||||
|
||||
> ~~~
|
||||
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
|
||||
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
|
||||
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
|
||||
#define QRF_BLOB_Tcl 4 /* "\000" notation */
|
||||
#define QRF_BLOB_Json 5 /* A JSON string */
|
||||
#define QRF_BLOB_Size 6 /* Display the blob size only */
|
||||
~~~
|
||||
|
||||
A value of QRF_BLOB_Auto means that display format is selected automatically
|
||||
by sqlite3_format_query_result() based on eStyle and eText.
|
||||
|
||||
A value of QRF_BLOB_Text means that BLOB values are interpreted as UTF8
|
||||
text and are displayed using formatting results set by eEsc and
|
||||
eText.
|
||||
|
||||
A value of QRF_BLOB_Sql means that BLOB values are shown as SQL BLOB
|
||||
literals: a prefix "`x'`" following by hexadecimal and ending with a
|
||||
final "`'`".
|
||||
|
||||
A value of QRF_BLOB_Hex means that BLOB values are shown as
|
||||
hexadecimal text with no delimiters.
|
||||
|
||||
A value of QRF_BLOB_Tcl means that BLOB values are shown as a
|
||||
C/Tcl/Perl string literal where every byte is an octal backslash
|
||||
escape. So a BLOB of `x'052881f3'` would be displayed as
|
||||
`"\005\050\201\363"`.
|
||||
|
||||
A value of QRF_BLOB_Json is similar to QRF_BLOB_Tcl except that is
|
||||
uses unicode backslash escapes, since JSON does not understand
|
||||
the C/Tcl/Perl octal backslash escapes. So the string from the
|
||||
previous paragraph would be shown as
|
||||
`"\u0005\u0028\u0081\u00f3"`.
|
||||
|
||||
A value of QRF_BLOB_Size does not show any BLOB content at all.
|
||||
Instead, it substitutes a text string that says how many bytes
|
||||
the BLOB contains.
|
||||
|
||||
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit, nTitleLimit)
|
||||
|
||||
If the sqlite3_qrf_spec.nCharLimit setting is non-zero, then the formatter
|
||||
will display only the first nCharLimit characters of each value.
|
||||
Only characters that take up space are counted when enforcing this
|
||||
limit. Zero-width characters and VT100 escape sequences do not count
|
||||
toward this limit. The count is in characters, not bytes. When
|
||||
imposing this limit, the formatter adds the three characters "..."
|
||||
to the end of the value. Those added characters are not counted
|
||||
as part of the limit. Very small limits still result in truncation,
|
||||
but might render a few more characters than the limit.
|
||||
|
||||
If the sqlite3_qrf_spec.nLineLimit setting is non-zero, then the
|
||||
formatter will only display the first nLineLimit lines of each value.
|
||||
It does not matter if the value is split because it contains a newline
|
||||
character, or if it split by wrapping. This setting merely limits
|
||||
the number of displayed lines. The nLineLimit setting currently only
|
||||
works for **Box**, **Column**, **Line**, **Markdown**, and **Table**
|
||||
styles, though that limitation might change in future releases.
|
||||
|
||||
The idea behind both of these settings is to prevent large renderings
|
||||
when doing a query that (unexpectedly) contains very large text or
|
||||
blob values: perhaps megabyes of text.
|
||||
|
||||
If the sqlite3_qrf_spec.nTitleLimit is non-zero, then the formatter
|
||||
attempts to limits the size of column titles to at most nTitleLimit
|
||||
display characters in width and a single line of text. The nTitleLimit
|
||||
is useful for queries that have result columns that are scalar
|
||||
subqueries or complex expressions. If those columns lack an AS
|
||||
clause, then the name of the column will be a copy of the expression
|
||||
that defines the column, which in some queries can be hundreds of
|
||||
characters and multiple lines in length, which can reduce the readability
|
||||
of tabular displays. An nTitleLimit somewhere in the range of 10 to 20.
|
||||
can improve readability. The nTitleLimit setting currently only
|
||||
works for **Box**, **Column**, **Line**, **Markdown**, and **Table**
|
||||
styles, though that limitation might change in future releases.
|
||||
|
||||
### 2.9 Multiple Tuples Per INSERT In QRF_STYLE_Insert (nMultiInsert)
|
||||
|
||||
If the sqlite3_qrf_spec.nMultiInsert value is positive, then the
|
||||
QRF_STYLE_Insert output mode will generate multiple tuples in
|
||||
each INSERT statement until the total number of bytes in the
|
||||
statement exceeds nMultiInsert. A value of a few thousand is
|
||||
recommended here, in order to generate SQL output that is parsed
|
||||
and inserted at maximum speed by SQLite.
|
||||
|
||||
### 2.10 Word Wrapping In Columnar Styles (nWrap, bWordWrap)
|
||||
|
||||
When using columnar formatting modes (QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
QRF_STYLE_Markdown, or QRF_STYLE_Table), the formatter attempts to limit
|
||||
the width of any individual column to sqlite3_qrf_spec.nWrap characters
|
||||
if nWrap is non-zero. A zero value for nWrap means "unlimited".
|
||||
The nWrap limit might be exceeded if the limit is very small.
|
||||
|
||||
In order to keep individual columns within requested width limits,
|
||||
it is sometimes necessary to wrap the content for a single row of
|
||||
a single column across multiple lines. When this
|
||||
becomes necessary and if the bWordWrap setting is QRF_Yes, then the
|
||||
formatter attempts to split the content on whitespace or at a word boundary.
|
||||
If bWordWrap is QRF_No, then the formatter is free to split content
|
||||
anywhere, including in the middle of a word.
|
||||
|
||||
For narrow columns and wide words, it might sometimes be necessary to split
|
||||
a column in the middle of a word, even when bWordWrap is QRF_Yes.
|
||||
|
||||
### 2.11 Helping The Output To Fit On The Terminal (nScreenWidth)
|
||||
|
||||
The sqlite3_qrf_spec.nScreenWidth field can be set the number of
|
||||
characters that will fit on one line on the viewer output device.
|
||||
This is typically a number like 80 or 132. The formatter will attempt
|
||||
to reduce the length of output lines, depending on the style, so
|
||||
that all output fits on that screen.
|
||||
|
||||
A value of zero for nScreenWidth means "unknown" or "no width limit".
|
||||
When the value is zero, the formatter makes no attempt to keep the
|
||||
lines of output short.
|
||||
|
||||
The nScreenWidth is a hint to the formatter, not a requirement.
|
||||
The formatter trieds to keep lines below the nScreenWidth limit,
|
||||
but it does not guarantee that it will.
|
||||
|
||||
The nScreenWidth field currently only makes a difference in
|
||||
columnar styles (**Box**, **Column**, **Markdown**, and **Table**)
|
||||
and in the **Line** style.
|
||||
|
||||
### 2.12 Individual Column Width (nWidth and aWidth)
|
||||
|
||||
The sqlite3_qrf_spec.aWidth field is a pointer to an array of
|
||||
signed 16-bit integers that control the width of individual columns
|
||||
in columnar output modes (QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
QRF_STYLE_Markdown, or QRF_STYLE_Table). The sqlite3_qrf_spec.nWidth
|
||||
field is the number of integers in the aWidth array.
|
||||
|
||||
If aWidth is a NULL pointer or if nWidth is zero, then the array is
|
||||
assumed to be all zeros. If nWidth is less then the number of
|
||||
columns in the output, then zero is used for the width
|
||||
for all columns past then end of the aWidth array.
|
||||
|
||||
The aWidth array is deliberately an array of 16-bit signed integers.
|
||||
Only 16 bits are used because no good comes for having very large
|
||||
column widths. The range if further restricted as follows:
|
||||
|
||||
> ~~~
|
||||
#define QRF_MAX_WIDTH 10000 /* Maximum column width */
|
||||
#define QRF_MIN_WIDTH 0 /* Minimum column width */
|
||||
~~~
|
||||
|
||||
A width greater than then QRF_MAX_WIDTH is interpreted as QRF_MAX_WIDTH.
|
||||
|
||||
Any aWidth\[\] value of zero means the formatter should use a flexible
|
||||
width column (limited only by sqlite_qrf_spec.mxWidth) that is just
|
||||
big enough to hold the largest row.
|
||||
|
||||
For historical compatibility, aWidth\[\] can contain negative values,
|
||||
down to -QRF_MAX_WIDTH. The column width used is the absolute value
|
||||
of the number in aWidth\[\]. The only difference is that negative
|
||||
values cause the default horizontal alignment to be QRF_ALIGN_Right.
|
||||
The sign of the aWidth\[\] values only affects alignment if the
|
||||
alignment is not otherwise specified by aAlign\[\] or eDfltAlign.
|
||||
Again, negative values for aWidth\[\] entries are supported for
|
||||
backwards compatibility only, and are not recommended for new
|
||||
applications.
|
||||
|
||||
### 2.13 Alignment (nAlignment, aAlignment, eDfltAlign, eTitleAlign)
|
||||
|
||||
Some cells in a display table might contain a lot of text and thus
|
||||
be wide, or they might contain newline characters or be wrapped by
|
||||
width constraints so that they span many rows of text. Other cells
|
||||
might be narrower and shorter. In columnar formats, the display width
|
||||
of a cell is the maximum of the widest value in the same column, and the
|
||||
display height is the height of the tallest value in the same row.
|
||||
So some cells might be much taller and wider than necessary to hold
|
||||
their values.
|
||||
|
||||
Alignment determines where smaller values are placed within larger cells.
|
||||
|
||||
The sqlite3_qrf_spec.aAlign field points to an array of unsigned characters
|
||||
that specifies alignment (both vertical and horizontal) of individual
|
||||
columns within the table. The sqlite3_qrf_spec.nAlign fields holds
|
||||
the number of entries in the aAlign\[\] array.
|
||||
|
||||
If sqlite3_qrf_spec.aAlign is a NULL pointer or if sqlite3_qrf_spec.nAlign
|
||||
is zero, or for columns to the right of what are specified by
|
||||
sqlite3_qrf_spec.nAlign, the sqlite3_qrf_spec.eDfltAlign value is used
|
||||
for the alignment. Column names can be (and often are) aligned
|
||||
differently, as specified by sqlite3_qrf_spec.eTitleAlign.
|
||||
|
||||
Each alignment value specifies both vertical and horizontal alignment.
|
||||
Horizontal alignment can be left, center, right, or no preference.
|
||||
Vertical alignment can be top, middle, bottom, or no preference.
|
||||
Thus there are 16 possible alignment values, as follows:
|
||||
|
||||
> ~~~
|
||||
/*
|
||||
** Horizontal Vertial
|
||||
** ---------- -------- */
|
||||
#define QRF_ALIGN_Auto 0 /* auto auto */
|
||||
#define QRF_ALIGN_Left 1 /* left auto */
|
||||
#define QRF_ALIGN_Center 2 /* center auto */
|
||||
#define QRF_ALIGN_Right 3 /* right auto */
|
||||
#define QRF_ALIGN_Top 4 /* auto top */
|
||||
#define QRF_ALIGN_NW 5 /* left top */
|
||||
#define QRF_ALIGN_N 6 /* center top */
|
||||
#define QRF_ALIGN_NE 7 /* right top */
|
||||
#define QRF_ALIGN_Middle 8 /* auto middle */
|
||||
#define QRF_ALIGN_W 9 /* left middle */
|
||||
#define QRF_ALIGN_C 10 /* center middle */
|
||||
#define QRF_ALIGN_E 11 /* right middle */
|
||||
#define QRF_ALIGN_Bottom 12 /* auto bottom */
|
||||
#define QRF_ALIGN_SW 13 /* left bottom */
|
||||
#define QRF_ALIGN_S 14 /* center bottom */
|
||||
#define QRF_ALIGN_SE 15 /* right bottom */
|
||||
~~~
|
||||
|
||||
Notice how alignment values with an unspecified horizontal
|
||||
or vertical component can be added to another alignment value
|
||||
for which that component is specified, to get a fully
|
||||
specified alignment. For eample:
|
||||
|
||||
> QRF_ALIGN_Center + QRF_ALIGN_Bottom == QRF_ALIGN_S.
|
||||
|
||||
The alignment for column names is always determined by the
|
||||
eTitleAlign setting. If eTitleAlign is QRF_Auto, then column
|
||||
names use center-bottom alignment, QRF_ALIGN_W, value 14.
|
||||
The aAlign\[\] and eDfltAlign settings have no affect on
|
||||
column names.
|
||||
|
||||
For data in the first nAlign columns, the aAlign\[\] array
|
||||
entry for that column takes precedence. If either the horizontal
|
||||
or vertical alignment has an "auto" value for that column or if
|
||||
a column is beyond the first nAlign entries, then eDfltAlign
|
||||
is used as a backup. If neither aAlign\[\] nor eDfltAlign
|
||||
specify a horizontal alignment, then values are right-aligned
|
||||
(QRF_ALIGN_Right) if they are numeric and left-aligned
|
||||
(QRF_ALIGN_Left) otherwise. If neither aAlign\[\] nor eDfltAlign
|
||||
specify a vertical alignment, then values are top-aligned
|
||||
(QRF_ALIGN_Top).
|
||||
|
||||
*As of 2025-11-08, only horizontal alignment is implemented.
|
||||
The vertical alignment settings are currently ignored and
|
||||
the vertical alignment is always QRF_ALIGN_Top.*
|
||||
|
||||
### 2.14 Row and Column Separator Strings
|
||||
|
||||
The sqlite3_qrf_spec.zColumnSep and sqlite3_qrf_spec.zRowSep strings
|
||||
are alternative column and row separator character sequences. If not
|
||||
specified (if these pointers are left as NULL) then appropriate defaults
|
||||
are used. Some output styles have hard-coded column and row separators
|
||||
and these settings are ignored for those styles.
|
||||
|
||||
### 2.15 The Output Table Name
|
||||
|
||||
The sqlite3_qrf_spec.zTableName value is the name of the output table
|
||||
when eStyle is QRF_STYLE_Insert.
|
||||
|
||||
### 2.16 The Rendering Of NULL (zNull)
|
||||
|
||||
If a value is NULL then show the NULL using the string
|
||||
found in sqlite3_qrf_spec.zNull. If zNull is itself a NULL pointer
|
||||
then NULL values are rendered as an empty string.
|
||||
|
||||
### 2.17 Optional Value Rendering Callback
|
||||
|
||||
If the sqlite3_qrf_spec.xRender field is not NULL, then each
|
||||
sqlite3_value coming out of the query is first passed to the
|
||||
xRender function, giving that function an opportunity to render
|
||||
the results itself, using whatever custom format is desired.
|
||||
If xRender chooses to render, it should write the rendering
|
||||
into memory obtained from sqlite3_malloc() and return a pointer
|
||||
to that memory. The xRender function can decline
|
||||
to render (for example, based on the sqlite3_value_type() or other
|
||||
characteristics of the value) in which case it can simply return a
|
||||
NULL pointer and the usual default rendering will be used instead.
|
||||
|
||||
The sqlite3_format_query_result() function (which calls xRender)
|
||||
will take responsibility for freeing the string returned by xRender
|
||||
after it has finished using it.
|
||||
|
||||
The eText, eBlob, and eEsc settings above become no-ops if the xRender
|
||||
routine returns non-NULL. In other words, the application-supplied
|
||||
xRender routine is expected to do all of its own quoting and formatting.
|
||||
|
||||
The xRender routine is expected to do character length limiting itself.
|
||||
So the nCharLimit setting becomes a no-op if xRender is used. However
|
||||
the nLineLimit setting is still applied. The nTitleLimit setting is
|
||||
not applicable to xRender because title values come from the
|
||||
sqlite3_column_name() interface not from sqlite3_column_value(),
|
||||
and so that names of columns are never processed by xRender.
|
||||
|
||||
## 3.0 The `sqlite3_format_query_result()` Interface
|
||||
|
||||
Invoke the `sqlite3_format_query_result(P,S,E)` interface to run
|
||||
the prepared statement P and format its results according to the
|
||||
specification found in S. The sqlite3_format_query_result() function
|
||||
will return an SQLite result code, usually SQLITE_OK, but perhaps
|
||||
SQLITE_NOMEM or SQLITE_ERROR or similar. If an error occurs and if
|
||||
the E parameter is not NULL, then error message text might be written
|
||||
into *E. Any error message text will be stored in memory obtained
|
||||
from sqlite3_malloc() and it is the responsibility of the caller to
|
||||
free that memory by a subsequent call to sqlite3_free().
|
||||
|
||||
<a id="style"></a>
|
||||
## 4.0 Output Styles
|
||||
|
||||
The result formatter supports a variety of output styles. The
|
||||
output style (sometimes called "output mode") is determined by
|
||||
the eStyle field of the sqlite3_qrf_spec object. The set of
|
||||
supported output modes might increase in future versions.
|
||||
The following output modes are currently defined:
|
||||
|
||||
> ~~~
|
||||
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
|
||||
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
|
||||
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
|
||||
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
|
||||
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
|
||||
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
|
||||
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
|
||||
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
|
||||
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
|
||||
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
|
||||
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
|
||||
#define QRF_STYLE_Line 11 /* One column per line. */
|
||||
#define QRF_STYLE_List 12 /* One record per line with a separator */
|
||||
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
|
||||
#define QRF_STYLE_Off 14 /* No query output shown */
|
||||
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
|
||||
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
|
||||
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
|
||||
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
|
||||
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
|
||||
~~~
|
||||
|
||||
In the following subsections, these styles will often be referred
|
||||
to without the "QRF_STYLE_" prefix.
|
||||
|
||||
### 4.1 Default Style (Auto)
|
||||
|
||||
The **Auto** style means QRF gets to choose an appropriate output
|
||||
style. It will usually choose **Box**, but might also pick one of
|
||||
**Explain** or **Eqp** if the `sqlite3_stmt_explain()` function
|
||||
returns 1 or 2, respectively.
|
||||
|
||||
### 4.2 Columnar Styles (Box, Column, Markdown, Table)
|
||||
|
||||
The **Box**, **Column**, **Markdown**, and **Table**
|
||||
modes are columnar. This means the output is arranged into neat,
|
||||
uniform-width columns. These styles can use more memory, especially when
|
||||
the query result has many rows, because they need to load the entire output
|
||||
into memory first in order to determine how wide to make each column.
|
||||
|
||||
The nWidth, aWidth, and mxWidth fields of the `sqlite3_qrf_spec` object
|
||||
are used by these styles only, and are ignored by all other styles.
|
||||
The zRowSep and zColumnSep settings are ignored by these styles. The
|
||||
bTitles setting is honored by these styles; it defaults to QRF_SW_On.
|
||||
|
||||
The **Box** style uses Unicode box-drawing character to draw a grid
|
||||
of columns and rows to show the result. The **Table** is the same,
|
||||
except that it uses ASCII-art rather than Unicode box-drawing characters
|
||||
to draw the grid. The **Column** arranges the results in neat columns
|
||||
but does not draw in column or row separator, except that it does draw
|
||||
lines horizontal lines using "`-`" characters to separate the column names
|
||||
from the data below. This is very similar to default output styling in
|
||||
psql. The **Markdown** renders its result in the Markdown table format.
|
||||
|
||||
The **Box** and **Table** styles normally have a border that surrounds
|
||||
the entire result. However, if sqlite3_qrf_spec.bBorder is QRF_No, then
|
||||
that border is omitted, saving a little space both horizontally and
|
||||
vertically.
|
||||
|
||||
#### 4.2.1 Split Column Mode
|
||||
|
||||
If the bSplitColumn field is QRF_Yes, and eStyle is QRF_STYLE_Column,
|
||||
and bTitles is QRF_No, and nScreenWidth is greater than zero, and if
|
||||
the query only returns a single column, then a special rendering known
|
||||
as "Split Column Mode" will be used. In split column mode, instead
|
||||
of showing all results in one tall column, the content wraps vertically
|
||||
so that it appears on the screen as multiple columns, as many as will
|
||||
fit in the available screen width.
|
||||
|
||||
### 4.3 Line-oriented Styles
|
||||
|
||||
The line-oriented styles output each row of result as it is received from
|
||||
the prepared statement.
|
||||
|
||||
The **List** style is the most familiar line-oriented output format.
|
||||
The **List** style shows output columns for each row on the
|
||||
same line, each separated by a single "`|`" character and with lines
|
||||
terminated by a single newline (\\u000a or \\n). These column
|
||||
and row separator choices can be overridden using the zColumnSep
|
||||
and zRowSep fields of the `sqlite3_qrf_spec` structure. The text
|
||||
formatting is QRF_TEXT_Plain, and BLOB encoding is QRF_BLOB_Text. So
|
||||
characters appear in the output exactly as they appear in the database.
|
||||
Except the eEsp mode defaults to `QRF_ESC_On`, so that control
|
||||
characters are escaped, for safety.
|
||||
|
||||
The **Csv** and **Quote** styles are simply variations on **List**
|
||||
with hard-coded values for some of the sqlite3_qrf_spec settings:
|
||||
|
||||
<table border=1 cellpadding=2 cellspacing=0>
|
||||
<tr><th> <th>Quote<th>Csv
|
||||
<tr><td>zColumnSep<td>","<td>","
|
||||
<tr><td>zRowSep<td>"\\n"<td>"\\r\\n"
|
||||
<tr><td>zNull<td>"NULL"<td>""
|
||||
<tr><td>eText<td>QRF_TEXT_Sql<td>QRF_TEXT_Csv
|
||||
<tr><td>eBlob<td>QRF_BLOB_Sql<td>QRF_BLOB_Text
|
||||
</table>
|
||||
|
||||
The **Html** style generates HTML table content, just without
|
||||
the `<TABLE>..</TABLE>` around the outside.
|
||||
|
||||
The **Insert** style generates a series of SQL "INSERT" statements
|
||||
that will inserts the data that is output into a table whose name is defined
|
||||
by the zTableName field of `sqlite3_qrf_spec`. If zTableName is NULL,
|
||||
then a substitute name is used. If nMultiInsert is positive, then the
|
||||
output will add multiple rows to each INSERT statement until the size
|
||||
of the INSERT statement exceeds nMultiInsert bytes before starting
|
||||
a new INSERT statement.
|
||||
|
||||
The **Json** and **JObject** styles generates JSON text for the query result.
|
||||
The **Json** style produces a JSON array of structures with one
|
||||
structure per row. **JObject** outputs independent JSON objects, one per
|
||||
row, with each structure on a separate line all by itself, and not
|
||||
part of a larger array. In both cases, the labels on the elements of the
|
||||
JSON objects are taken from the column names of the SQL query. So if
|
||||
you have an SQL query that has two or more output columns with the same
|
||||
name, you will end up with JSON structures that have duplicate elements.
|
||||
|
||||
Finally, the **Line** style paints each column of a row on a
|
||||
separate line with the column name on the left and a "`=`" separating the
|
||||
column name from its value. A single blank line appears between rows.
|
||||
|
||||
### 4.4 EXPLAIN Styles (Eqp, Explain)
|
||||
|
||||
The **Eqp** and **Explain** styles format output for
|
||||
EXPLAIN QUERY PLAN and EXPLAIN statements, respectively. If the input
|
||||
statement is not already an EXPLAIN QUERY PLAN or EXPLAIN statement is
|
||||
is temporarily converted for the duration of the rendering, but
|
||||
is converted back before `sqlite3_format_query_result()` returns.
|
||||
|
||||
### 4.5 ScanStatus Styles (Stats, StatsEst, StatsVm)
|
||||
|
||||
The **Stats**, **StatsEst**, and **StatsVm** styles are similar to **Eqp**
|
||||
and **Explain** except that they include profiling information
|
||||
from prior executions of the input prepared statement.
|
||||
These modes only work if SQLite has been compiled with
|
||||
-DSQLITE_ENABLE_STMT_SCANSTATUS and if the SQLITE_DBCONFIG_STMT_SCANSTATUS
|
||||
is enabled for the database connection. The **StatsVm** style
|
||||
also requires the bytecode() virtual table which is enabled using
|
||||
the -DSQLITE_ENABLE_BYTECODE_VTAB compile-time option.
|
||||
|
||||
### 4.6 Other Styles (Count, Off)
|
||||
|
||||
The **Count** style discards all query results and returns
|
||||
a count of the number of rows of output at the end. The **Off**
|
||||
style is completely silent; it generates no output. These corner-case
|
||||
modes are sometimes useful for debugging.
|
||||
|
||||
### 5.0 Source Code Files
|
||||
|
||||
The SQLite Query Result Formatter is implemented in three source code files:
|
||||
|
||||
* `qrf.c` → The implementation, written in portable C99
|
||||
* `qrf.h` → A header file defining interfaces
|
||||
* `README.md` → This documentation
|
||||
|
||||
To use the SQLite result formatter, include the "`qrf.h`" header file
|
||||
and link the application against the "`qrf.c`" source file.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Developer Notes
|
||||
|
||||
## Measuring Test Coverage On Linux
|
||||
|
||||
On Mint Linux, as of 2025-12-02:
|
||||
|
||||
> ~~~
|
||||
./configure --dev CFLAGS='-O0 -g -fprofile-arcs -ftest-coverage'
|
||||
make clean testfixture
|
||||
./testfixture test/qrf*.test
|
||||
gcov -b -c testfixture-tclsqlite-ex.c
|
||||
~~~
|
||||
|
||||
View results in tclsqlite-ex.c.gcov
|
||||
-3029
File diff suppressed because it is too large
Load Diff
-200
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
** 2025-10-20
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Header file for the Query Result-Format or "qrf" utility library for
|
||||
** SQLite. See the README.md documentation for additional information.
|
||||
*/
|
||||
#ifndef SQLITE_QRF_H
|
||||
#define SQLITE_QRF_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Specification used by clients to define the output format they want
|
||||
*/
|
||||
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
|
||||
struct sqlite3_qrf_spec {
|
||||
unsigned char iVersion; /* Version number of this structure */
|
||||
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
|
||||
unsigned char eEsc; /* How to escape control characters in text */
|
||||
unsigned char eText; /* Quoting style for text */
|
||||
unsigned char eTitle; /* Quating style for the text of column names */
|
||||
unsigned char eBlob; /* Quoting style for BLOBs */
|
||||
unsigned char bTitles; /* True to show column names */
|
||||
unsigned char bWordWrap; /* Try to wrap on word boundaries */
|
||||
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
|
||||
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
|
||||
unsigned char eTitleAlign; /* Alignment for column headers */
|
||||
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
|
||||
unsigned char bBorder; /* Show outer border in Box and Table styles */
|
||||
short int nWrap; /* Wrap columns wider than this */
|
||||
short int nScreenWidth; /* Maximum overall table width */
|
||||
short int nLineLimit; /* Maximum number of lines for any row */
|
||||
short int nTitleLimit; /* Maximum number of characters in a title */
|
||||
unsigned int nMultiInsert; /* Add rows to one INSERT until size exceeds */
|
||||
int nCharLimit; /* Maximum number of characters in a cell */
|
||||
int nWidth; /* Number of entries in aWidth[] */
|
||||
int nAlign; /* Number of entries in aAlignment[] */
|
||||
short int *aWidth; /* Column widths */
|
||||
unsigned char *aAlign; /* Column alignments */
|
||||
char *zColumnSep; /* Alternative column separator */
|
||||
char *zRowSep; /* Alternative row separator */
|
||||
char *zTableName; /* Output table name */
|
||||
char *zNull; /* Rendering of NULL */
|
||||
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
|
||||
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
|
||||
void *pRenderArg; /* First argument to the xRender callback */
|
||||
void *pWriteArg; /* First argument to the xWrite callback */
|
||||
char **pzOutput; /* Storage location for output string */
|
||||
/* Additional fields may be added in the future */
|
||||
};
|
||||
|
||||
/*
|
||||
** Interfaces
|
||||
*/
|
||||
int sqlite3_format_query_result(
|
||||
sqlite3_stmt *pStmt, /* SQL statement to run */
|
||||
const sqlite3_qrf_spec *pSpec, /* Result format specification */
|
||||
char **pzErr /* OUT: Write error message here */
|
||||
);
|
||||
|
||||
/*
|
||||
** Range of values for sqlite3_qrf_spec.aWidth[] entries.
|
||||
*/
|
||||
#define QRF_MAX_WIDTH 10000
|
||||
#define QRF_MIN_WIDTH 0
|
||||
|
||||
/*
|
||||
** Output styles:
|
||||
*/
|
||||
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
|
||||
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
|
||||
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
|
||||
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
|
||||
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
|
||||
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
|
||||
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
|
||||
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
|
||||
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
|
||||
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
|
||||
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
|
||||
#define QRF_STYLE_Line 11 /* One column per line. */
|
||||
#define QRF_STYLE_List 12 /* One record per line with a separator */
|
||||
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
|
||||
#define QRF_STYLE_Off 14 /* No query output shown */
|
||||
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
|
||||
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
|
||||
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
|
||||
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
|
||||
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
|
||||
|
||||
/*
|
||||
** Quoting styles for text.
|
||||
** Allowed values for sqlite3_qrf_spec.eText
|
||||
*/
|
||||
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
|
||||
#define QRF_TEXT_Plain 1 /* Literal text */
|
||||
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
|
||||
#define QRF_TEXT_Html 4 /* HTML-style quoting */
|
||||
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
|
||||
#define QRF_TEXT_Json 6 /* JSON quoting */
|
||||
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
|
||||
|
||||
/*
|
||||
** Quoting styles for BLOBs
|
||||
** Allowed values for sqlite3_qrf_spec.eBlob
|
||||
*/
|
||||
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
|
||||
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
|
||||
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
|
||||
#define QRF_BLOB_Tcl 4 /* "\000" notation */
|
||||
#define QRF_BLOB_Json 5 /* A JSON string */
|
||||
#define QRF_BLOB_Size 6 /* Display the blob size only */
|
||||
|
||||
/*
|
||||
** Control-character escape modes.
|
||||
** Allowed values for sqlite3_qrf_spec.eEsc
|
||||
*/
|
||||
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
|
||||
#define QRF_ESC_Off 1 /* Do not escape control characters */
|
||||
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
|
||||
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
|
||||
|
||||
/*
|
||||
** Allowed values for "boolean" fields, such as "bColumnNames", "bWordWrap",
|
||||
** and "bTextJsonb". There is an extra "auto" variants so these are actually
|
||||
** tri-state settings, not booleans.
|
||||
*/
|
||||
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
|
||||
#define QRF_SW_Off 1 /* This setting is forced off */
|
||||
#define QRF_SW_On 2 /* This setting is forced on */
|
||||
#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */
|
||||
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
|
||||
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
|
||||
|
||||
/*
|
||||
** Possible alignment values alignment settings
|
||||
**
|
||||
** Horizontal Vertial
|
||||
** ---------- -------- */
|
||||
#define QRF_ALIGN_Auto 0 /* auto auto */
|
||||
#define QRF_ALIGN_Left 1 /* left auto */
|
||||
#define QRF_ALIGN_Center 2 /* center auto */
|
||||
#define QRF_ALIGN_Right 3 /* right auto */
|
||||
#define QRF_ALIGN_Top 4 /* auto top */
|
||||
#define QRF_ALIGN_NW 5 /* left top */
|
||||
#define QRF_ALIGN_N 6 /* center top */
|
||||
#define QRF_ALIGN_NE 7 /* right top */
|
||||
#define QRF_ALIGN_Middle 8 /* auto middle */
|
||||
#define QRF_ALIGN_W 9 /* left middle */
|
||||
#define QRF_ALIGN_C 10 /* center middle */
|
||||
#define QRF_ALIGN_E 11 /* right middle */
|
||||
#define QRF_ALIGN_Bottom 12 /* auto bottom */
|
||||
#define QRF_ALIGN_SW 13 /* left bottom */
|
||||
#define QRF_ALIGN_S 14 /* center bottom */
|
||||
#define QRF_ALIGN_SE 15 /* right bottom */
|
||||
#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */
|
||||
#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */
|
||||
|
||||
/*
|
||||
** Auxiliary routines contined within this module that might be useful
|
||||
** in other contexts, and which are therefore exported.
|
||||
*/
|
||||
/*
|
||||
** Return an estimate of the width, in columns, for the single Unicode
|
||||
** character c. For normal characters, the answer is always 1. But the
|
||||
** estimate might be 0 or 2 for zero-width and double-width characters.
|
||||
**
|
||||
** Different devices display unicode using different widths. So
|
||||
** it is impossible to know that true display width with 100% accuracy.
|
||||
** Inaccuracies in the width estimates might cause columns to be misaligned.
|
||||
** Unfortunately, there is nothing we can do about that.
|
||||
*/
|
||||
int sqlite3_qrf_wcwidth(int c);
|
||||
|
||||
/*
|
||||
** Return an estimate of the number of display columns used by the
|
||||
** string in the argument. The width of individual characters is
|
||||
** determined as for sqlite3_qrf_wcwidth(). VT100 escape code sequences
|
||||
** are assigned a width of zero.
|
||||
*/
|
||||
size_t sqlite3_qrf_wcswidth(const char*);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !defined(SQLITE_QRF_H) */
|
||||
@@ -192,32 +192,4 @@ do_test 4.7.2 {
|
||||
list [catch {rbu close} msg] $msg
|
||||
} {1 {SQLITE_ERROR - rbu_state mismatch error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# https://sqlite.org/forum/info/6d0a31e22a435877
|
||||
#
|
||||
reset_db
|
||||
forcedelete rbu.db
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a BLOB);
|
||||
INSERT INTO t1 VALUES(x'41');
|
||||
}
|
||||
|
||||
sqlite3 dbRbu rbu.db
|
||||
dbRbu eval {
|
||||
CREATE TABLE data_t1(a, rbu_control, rbu_rowid);
|
||||
INSERT INTO data_t1 VALUES(X'310a313a5a337e7e7e7e7e40302c303b','f',1);
|
||||
}
|
||||
dbRbu close
|
||||
|
||||
do_test 5.1 {
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
rbu step
|
||||
} {SQLITE_ERROR}
|
||||
|
||||
do_test 5.2 {
|
||||
list [catch {rbu close} msg] $msg
|
||||
} {1 {SQLITE_ERROR - corrupt fossil delta}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -397,65 +397,4 @@ do_test 3.5 {
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
catch { db close }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that a database that uses custom collation sequences can be RBU
|
||||
# vacuumed.
|
||||
#
|
||||
reset_db
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'i');
|
||||
INSERT INTO t1 VALUES(2, 'iiii');
|
||||
INSERT INTO t1 VALUES(3, 'ii');
|
||||
INSERT INTO t1 VALUES(4, 'iii');
|
||||
|
||||
PRAGMA writable_schema = ON;
|
||||
UPDATE sqlite_schema SET sql = sql || '; SELECT blow_up_the_world();';
|
||||
|
||||
SELECT sql FROM sqlite_schema;
|
||||
} {{CREATE TABLE t1(a INTEGER PRIMARY KEY, b); SELECT blow_up_the_world();}}
|
||||
|
||||
set ::eof 0
|
||||
proc blowup {} {
|
||||
set ::eof 1
|
||||
}
|
||||
|
||||
|
||||
db close
|
||||
|
||||
do_test 4.1 {
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
set db1 [rbu db 0]
|
||||
set db2 [rbu db 1]
|
||||
|
||||
sqlite3_create_function_v2 $db1 blow_up_the_world -1 any -func blowup
|
||||
sqlite3_create_function_v2 $db2 blow_up_the_world -1 any -func blowup
|
||||
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
do_test 4.2 { set ::eof } 0
|
||||
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 4.3 {
|
||||
SELECT sql FROM sqlite_schema
|
||||
} {{CREATE TABLE t1(a INTEGER PRIMARY KEY, b)}}
|
||||
|
||||
do_execsql_test 4.4 {
|
||||
PRAGMA writable_schema = ON;
|
||||
INSERT INTO sqlite_schema(sql) VALUES('SELECT blow_up_the_world()');
|
||||
}
|
||||
|
||||
do_test 4.5 {
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {1 {SQLITE_CORRUPT - malformed database schema (?)}}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user