Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8d68a5b8e | |||
| abbfa7aa17 | |||
| c1ca183abf | |||
| ca9309cc56 | |||
| 217e77d240 | |||
| 743c9ec4a5 | |||
| 4245667e35 | |||
| 902ea83925 | |||
| 49d400b091 | |||
| ae5cfda656 | |||
| 23f61a4ba8 | |||
| bedbb1bfab | |||
| f5d0656f16 | |||
| 7960da0346 | |||
| 8683c09281 | |||
| 472de149ae | |||
| e644f7c24b | |||
| 594357f2e1 | |||
| eb84c81c53 | |||
| df5d06d03e | |||
| 6b86b33793 | |||
| 647b0dd12d | |||
| c25f5ea6e8 | |||
| b669bb5e2b | |||
| 9b9017def4 | |||
| f46091d73f | |||
| 4b1555a9fc | |||
| 9b80cb5f39 | |||
| f1ce4f40c7 | |||
| 875db41afc | |||
| 056a71562f | |||
| e79cb67c35 | |||
| 2e30d95fb6 | |||
| 590f013a87 | |||
| ad1285c5c0 | |||
| c32e16643d | |||
| 91a81316b9 | |||
| 3eaaec945c | |||
| c6138e970e | |||
| 809be56218 | |||
| 58c0652923 | |||
| b23f1572ab | |||
| c620522175 | |||
| c7c5b8a508 | |||
| cbef156d93 | |||
| 5d7aef16bf | |||
| 411929025b | |||
| bf82c95e08 | |||
| c9b7d943ad | |||
| 63b74e02a7 | |||
| 3c8e438583 | |||
| 2a072506ca | |||
| 89e5dfac01 | |||
| 42b78237cd | |||
| ff5e4ecced | |||
| c59b7a8053 | |||
| da217c958e | |||
| 03af6d7157 | |||
| 9f2be5db04 | |||
| b53d8fa9f2 | |||
| e7d5384418 | |||
| d01dee5e6a | |||
| a303392a2c | |||
| b38ac0986e | |||
| 36d5554c9a | |||
| 27c4cd183d | |||
| b5aaf0f285 | |||
| 423003dca9 | |||
| ae276719f0 | |||
| 6d97c1a844 | |||
| 59a9654715 | |||
| b2873a3014 | |||
| 8e1bd8264d | |||
| 0a94ef8b52 | |||
| ecba10730d | |||
| 5a1d659d79 | |||
| 16635fa777 | |||
| 47de1f9e19 | |||
| 8d0d409876 | |||
| 1e62057436 | |||
| bf9b994f52 | |||
| 27a67968af | |||
| c768ef7289 | |||
| f7d98ac764 | |||
| fb2e0bfa8b | |||
| ff4917e9a5 | |||
| 85728a21c6 | |||
| cf3107c7d4 | |||
| 26e0f2e916 | |||
| 76abcfbdc0 | |||
| ee026c5479 | |||
| d8e48fffdf | |||
| c7c15d1b83 | |||
| 3c1572ddb4 | |||
| 823872c6d6 | |||
| 841c98e14c | |||
| a872466c8e | |||
| 1d4f1cf426 | |||
| 08efa45113 | |||
| a07df967f5 | |||
| 2f2c5e2061 | |||
| a70a01dbcb | |||
| 1da477d3ab | |||
| cfce889cda | |||
| 8d7b41302f | |||
| 7e475e571f | |||
| a6303704a3 | |||
| 873a84054c | |||
| 2c4d62f013 | |||
| da2641597a | |||
| aafa022f5b | |||
| 838865c0bb | |||
| ea038d3d23 | |||
| 92c0881d9d | |||
| 50ba1a0213 | |||
| afe768193f | |||
| 64c40ec2c7 | |||
| 80fafc25ce | |||
| d9324a5226 | |||
| 40503750fa | |||
| 49507d2af6 | |||
| 54422235e9 | |||
| e38b6e0318 | |||
| 690d4c545d | |||
| da01757870 | |||
| c0a18d6a5a | |||
| 1407458c6f | |||
| b36f697b50 | |||
| b819202acf | |||
| 6e8a3341ea | |||
| f53c0a0d4e | |||
| a16edfa29b | |||
| 5f7e173abf | |||
| 80dbd818f2 | |||
| ffc9b1b046 | |||
| 881f0f4114 | |||
| 4868a86513 | |||
| 093677add4 | |||
| c75445087f | |||
| 7d24ff2971 | |||
| b0ab21dee4 | |||
| 4df2ab575f |
+10
@@ -446,6 +446,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/expert/test_expert.c \
|
||||
$(TOP)/ext/misc/amatch.c \
|
||||
$(TOP)/ext/misc/appendvfs.c \
|
||||
$(TOP)/ext/misc/basexx.c \
|
||||
$(TOP)/ext/misc/carray.c \
|
||||
$(TOP)/ext/misc/cksumvfs.c \
|
||||
$(TOP)/ext/misc/closure.c \
|
||||
@@ -778,6 +779,15 @@ sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
|
||||
cp tsrc/sqlite3ext.h .
|
||||
cp $(TOP)/ext/session/sqlite3session.h .
|
||||
|
||||
sqlite3r.h: sqlite3.h
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h
|
||||
|
||||
sqlite3r.c: sqlite3.c sqlite3r.h
|
||||
cp $(TOP)/ext/recover/sqlite3recover.c tsrc/
|
||||
cp $(TOP)/ext/recover/sqlite3recover.h tsrc/
|
||||
cp $(TOP)/ext/recover/dbdata.c tsrc/
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover $(AMALGAMATION_LINE_MACROS)
|
||||
|
||||
sqlite3ext.h: .target_source
|
||||
cp tsrc/sqlite3ext.h .
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.40.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.41.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.40.0'
|
||||
PACKAGE_STRING='sqlite 3.40.0'
|
||||
PACKAGE_VERSION='3.41.0'
|
||||
PACKAGE_STRING='sqlite 3.41.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1468,7 +1468,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.40.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.41.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1533,7 +1533,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.40.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.41.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1661,7 +1661,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.40.0
|
||||
sqlite configure 3.41.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2080,7 +2080,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.40.0, which was
|
||||
It was created by sqlite $as_me 3.41.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -12390,7 +12390,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.40.0, which was
|
||||
This file was extended by sqlite $as_me 3.41.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12456,7 +12456,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.40.0
|
||||
sqlite config.status 3.41.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -6278,6 +6278,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
|
||||
|
||||
/* If this is a new term, query for it. Update cksum3 with the results. */
|
||||
fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
|
||||
if( p->rc ) break;
|
||||
|
||||
if( eDetail==FTS5_DETAIL_NONE ){
|
||||
if( 0==fts5MultiIterIsEmpty(p, pIter) ){
|
||||
|
||||
Executable
+269
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
** 2022-11-18
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This is a SQLite extension for converting in either direction
|
||||
** between a (binary) blob and base64 text. Base64 can transit a
|
||||
** sane USASCII channel unmolested. It also plays nicely in CSV or
|
||||
** written as TCL brace-enclosed literals or SQL string literals,
|
||||
** and can be used unmodified in XML-like documents.
|
||||
**
|
||||
** This is an independent implementation of conversions specified in
|
||||
** RFC 4648, done on the above date by the author (Larry Brasfield)
|
||||
** who thereby has the right to put this into the public domain.
|
||||
**
|
||||
** The conversions meet RFC 4648 requirements, provided that this
|
||||
** C source specifies that line-feeds are included in the encoded
|
||||
** data to limit visible line lengths to 72 characters and to
|
||||
** terminate any encoded blob having non-zero length.
|
||||
**
|
||||
** Length limitations are not imposed except that the runtime
|
||||
** SQLite string or blob length limits are respected. Otherwise,
|
||||
** any length binary sequence can be represented and recovered.
|
||||
** Generated base64 sequences, with their line-feeds included,
|
||||
** can be concatenated; the result converted back to binary will
|
||||
** be the concatenation of the represented binary sequences.
|
||||
**
|
||||
** This SQLite3 extension creates a function, base64(x), which
|
||||
** either: converts text x containing base64 to a returned blob;
|
||||
** or converts a blob x to returned text containing base64. An
|
||||
** error will be thrown for other input argument types.
|
||||
**
|
||||
** This code relies on UTF-8 encoding only with respect to the
|
||||
** meaning of the first 128 (7-bit) codes matching that of USASCII.
|
||||
** It will fail miserably if somehow made to try to convert EBCDIC.
|
||||
** Because it is table-driven, it could be enhanced to handle that,
|
||||
** but the world and SQLite have moved on from that anachronism.
|
||||
**
|
||||
** To build the extension:
|
||||
** Set shell variable SQDIR=<your favorite SQLite checkout directory>
|
||||
** *Nix: gcc -O2 -shared -I$SQDIR -fPIC -o base64.so base64.c
|
||||
** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR -o base64.dylib base64.c
|
||||
** Win32: gcc -O2 -shared -I%SQDIR% -o base64.dll base64.c
|
||||
** Win32: cl /Os -I%SQDIR% base64.c -link -dll -out:base64.dll
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
|
||||
SQLITE_EXTENSION_INIT1;
|
||||
|
||||
#define PC 0x80 /* pad character */
|
||||
#define WS 0x81 /* whitespace */
|
||||
#define ND 0x82 /* Not above or digit-value */
|
||||
#define PAD_CHAR '='
|
||||
|
||||
#ifndef UBYTE_TYPEDEF
|
||||
typedef unsigned char ubyte;
|
||||
# define UBYTE_TYPEDEF
|
||||
#endif
|
||||
|
||||
static const ubyte b64DigitValues[128] = {
|
||||
/* HT LF VT FF CR */
|
||||
ND,ND,ND,ND, ND,ND,ND,ND, ND,WS,WS,WS, WS,WS,ND,ND,
|
||||
/* US */
|
||||
ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND,
|
||||
/*sp + / */
|
||||
WS,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,62, ND,ND,ND,63,
|
||||
/* 0 1 5 9 = */
|
||||
52,53,54,55, 56,57,58,59, 60,61,ND,ND, ND,PC,ND,ND,
|
||||
/* A O */
|
||||
ND, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
/* P Z */
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,ND, ND,ND,ND,ND,
|
||||
/* a o */
|
||||
ND,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
/* p z */
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,ND, ND,ND,ND,ND
|
||||
};
|
||||
|
||||
static const char b64Numerals[64+1]
|
||||
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
#define BX_DV_PROTO(c) ((((ubyte)(c))<0x80)? b64DigitValues[c] : 0x80)
|
||||
#define IS_BX_DIGIT(bdp) (((ubyte)(bdp))<0x80)
|
||||
#define IS_BX_WS(bdp) ((bdp)==WS)
|
||||
#define IS_BX_PAD(bdp) ((bdp)==PC)
|
||||
#define BX_NUMERAL(dv) (b64Numerals[(ubyte)(dv)])
|
||||
/* Width of base64 lines. Should be an integer multiple of 4. */
|
||||
#define B64_DARK_MAX 72
|
||||
|
||||
/* Encode a byte buffer into base64 text with linefeeds appended to limit
|
||||
** encoded group lengths to B64_DARK_MAX or to terminate the last group.
|
||||
*/
|
||||
static char* toBase64( ubyte *pIn, int nbIn, char *pOut ){
|
||||
int nCol = 0;
|
||||
while( nbIn >= 3 ){
|
||||
/* Do the bit-shuffle, exploiting unsigned input to avoid masking. */
|
||||
pOut[0] = BX_NUMERAL(pIn[0]>>2);
|
||||
pOut[1] = BX_NUMERAL(((pIn[0]<<4)|(pIn[1]>>4))&0x3f);
|
||||
pOut[2] = BX_NUMERAL(((pIn[1]&0xf)<<2)|(pIn[2]>>6));
|
||||
pOut[3] = BX_NUMERAL(pIn[2]&0x3f);
|
||||
pOut += 4;
|
||||
nbIn -= 3;
|
||||
pIn += 3;
|
||||
if( (nCol += 4)>=B64_DARK_MAX || nbIn<=0 ){
|
||||
*pOut++ = '\n';
|
||||
nCol = 0;
|
||||
}
|
||||
}
|
||||
if( nbIn > 0 ){
|
||||
signed char nco = nbIn+1;
|
||||
int nbe;
|
||||
unsigned long qv = *pIn++;
|
||||
for( nbe=1; nbe<3; ++nbe ){
|
||||
qv <<= 8;
|
||||
if( nbe<nbIn ) qv |= *pIn++;
|
||||
}
|
||||
for( nbe=3; nbe>=0; --nbe ){
|
||||
char ce = (nbe<nco)? BX_NUMERAL((ubyte)(qv & 0x3f)) : PAD_CHAR;
|
||||
qv >>= 6;
|
||||
pOut[nbe] = ce;
|
||||
}
|
||||
pOut += 4;
|
||||
*pOut++ = '\n';
|
||||
}
|
||||
*pOut = 0;
|
||||
return pOut;
|
||||
}
|
||||
|
||||
/* Skip over text which is not base64 numeral(s). */
|
||||
static char * skipNonB64( char *s ){
|
||||
char c;
|
||||
while( (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Decode base64 text into a byte buffer. */
|
||||
static ubyte* fromBase64( char *pIn, int ncIn, ubyte *pOut ){
|
||||
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
|
||||
while( ncIn>0 && *pIn!=PAD_CHAR ){
|
||||
static signed char nboi[] = { 0, 0, 1, 2, 3 };
|
||||
char *pUse = skipNonB64(pIn);
|
||||
unsigned long qv = 0L;
|
||||
int nti, nbo, nac;
|
||||
ncIn -= (pUse - pIn);
|
||||
pIn = pUse;
|
||||
nti = (ncIn>4)? 4 : ncIn;
|
||||
ncIn -= nti;
|
||||
nbo = nboi[nti];
|
||||
if( nbo==0 ) break;
|
||||
for( nac=0; nac<4; ++nac ){
|
||||
char c = (nac<nti)? *pIn++ : b64Numerals[0];
|
||||
ubyte bdp = BX_DV_PROTO(c);
|
||||
switch( bdp ){
|
||||
case ND:
|
||||
/* Treat dark non-digits as pad, but they terminate decode too. */
|
||||
ncIn = 0;
|
||||
/* fall thru */
|
||||
case WS:
|
||||
/* Treat whitespace as pad and terminate this group.*/
|
||||
nti = nac;
|
||||
/* fall thru */
|
||||
case PC:
|
||||
bdp = 0;
|
||||
--nbo;
|
||||
/* fall thru */
|
||||
default: /* bdp is the digit value. */
|
||||
qv = qv<<6 | bdp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch( nbo ){
|
||||
case 3:
|
||||
pOut[2] = (qv) & 0xff;
|
||||
case 2:
|
||||
pOut[1] = (qv>>8) & 0xff;
|
||||
case 1:
|
||||
pOut[0] = (qv>>16) & 0xff;
|
||||
}
|
||||
pOut += nbo;
|
||||
}
|
||||
return pOut;
|
||||
}
|
||||
|
||||
/* This function does the work for the SQLite base64(x) UDF. */
|
||||
static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
int nb, nc, nv = sqlite3_value_bytes(av[0]);
|
||||
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
|
||||
SQLITE_LIMIT_LENGTH, -1);
|
||||
char *cBuf;
|
||||
ubyte *bBuf;
|
||||
assert(na==1);
|
||||
switch( sqlite3_value_type(av[0]) ){
|
||||
case SQLITE_BLOB:
|
||||
nb = nv;
|
||||
nc = 4*(nv+2/3); /* quads needed */
|
||||
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
|
||||
if( nvMax < nc ){
|
||||
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
|
||||
return;
|
||||
}
|
||||
cBuf = sqlite3_malloc(nc);
|
||||
if( !cBuf ) goto memFail;
|
||||
bBuf = (ubyte*)sqlite3_value_blob(av[0]);
|
||||
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
|
||||
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
|
||||
break;
|
||||
case SQLITE_TEXT:
|
||||
nc = nv;
|
||||
nb = 3*((nv+3)/4); /* may overestimate due to LF and padding */
|
||||
if( nvMax < nb ){
|
||||
sqlite3_result_error(context, "blob from base64 may be too big", -1);
|
||||
return;
|
||||
}else if( nb<1 ){
|
||||
nb = 1;
|
||||
}
|
||||
bBuf = sqlite3_malloc(nb);
|
||||
if( !bBuf ) goto memFail;
|
||||
cBuf = (char *)sqlite3_value_text(av[0]);
|
||||
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
|
||||
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
|
||||
break;
|
||||
default:
|
||||
sqlite3_result_error(context, "base64 accepts only blob or text", -1);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
memFail:
|
||||
sqlite3_result_error(context, "base64 OOM", -1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Establish linkage to running SQLite library.
|
||||
*/
|
||||
#ifndef SQLITE_SHELL_EXTFUNCS
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_base_init
|
||||
#else
|
||||
static int sqlite3_base64_init
|
||||
#endif
|
||||
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErr;
|
||||
return sqlite3_create_function
|
||||
(db, "base64", 1,
|
||||
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
|
||||
0, base64, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Define some macros to allow this extension to be built into the shell
|
||||
** conveniently, in conjunction with use of SQLITE_SHELL_EXTFUNCS. This
|
||||
** allows shell.c, as distributed, to have this extension built in.
|
||||
*/
|
||||
#define BASE64_INIT(db) sqlite3_base64_init(db, 0, 0)
|
||||
#define BASE64_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
|
||||
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
** 2022-11-16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This is a utility for converting binary to base85 or vice-versa.
|
||||
** It can be built as a standalone program or an SQLite3 extension.
|
||||
**
|
||||
** Much like base64 representations, base85 can be sent through a
|
||||
** sane USASCII channel unmolested. It also plays nicely in CSV or
|
||||
** written as TCL brace-enclosed literals or SQL string literals.
|
||||
** It is not suited for unmodified use in XML-like documents.
|
||||
**
|
||||
** The encoding used resembles Ascii85, but was devised by the author
|
||||
** (Larry Brasfield) before Mozilla, Adobe, ZMODEM or other Ascii85
|
||||
** variant sources existed, in the 1984 timeframe on a VAX mainframe.
|
||||
** Further, this is an independent implementation of a base85 system.
|
||||
** Hence, the author has rightfully put this into the public domain.
|
||||
**
|
||||
** Base85 numerals are taken from the set of 7-bit USASCII codes,
|
||||
** excluding control characters and Space ! " ' ( ) { | } ~ Del
|
||||
** in code order representing digit values 0 to 84 (base 10.)
|
||||
**
|
||||
** Groups of 4 bytes, interpreted as big-endian 32-bit values,
|
||||
** are represented as 5-digit base85 numbers with MS to LS digit
|
||||
** order. Groups of 1-3 bytes are represented with 2-4 digits,
|
||||
** still big-endian but 8-24 bit values. (Using big-endian yields
|
||||
** the simplest transition to byte groups smaller than 4 bytes.
|
||||
** These byte groups can also be considered base-256 numbers.)
|
||||
** Groups of 0 bytes are represented with 0 digits and vice-versa.
|
||||
** No pad characters are used; Encoded base85 numeral sequence
|
||||
** (aka "group") length maps 1-to-1 to the decoded binary length.
|
||||
**
|
||||
** Any character not in the base85 numeral set delimits groups.
|
||||
** When base85 is streamed or stored in containers of indefinite
|
||||
** size, newline is used to separate it into sub-sequences of no
|
||||
** more than 80 digits so that fgets() can be used to read it.
|
||||
**
|
||||
** Length limitations are not imposed except that the runtime
|
||||
** SQLite string or blob length limits are respected. Otherwise,
|
||||
** any length binary sequence can be represented and recovered.
|
||||
** Base85 sequences can be concatenated by separating them with
|
||||
** a non-base85 character; the conversion to binary will then
|
||||
** be the concatenation of the represented binary sequences.
|
||||
|
||||
** The standalone program either converts base85 on stdin to create
|
||||
** a binary file or converts a binary file to base85 on stdout.
|
||||
** Read or make it blurt its help for invocation details.
|
||||
**
|
||||
** The SQLite3 extension creates a function, base85(x), which will
|
||||
** either convert text base85 to a blob or a blob to text base85
|
||||
** and return the result (or throw an error for other types.)
|
||||
** Unless built with OMIT_BASE85_CHECKER defined, it also creates a
|
||||
** function, is_base85(t), which returns 1 iff the text t contains
|
||||
** nothing other than base85 numerals and whitespace, or 0 otherwise.
|
||||
**
|
||||
** To build the extension:
|
||||
** Set shell variable SQDIR=<your favorite SQLite checkout directory>
|
||||
** and variable OPTS to -DOMIT_BASE85_CHECKER if is_base85() unwanted.
|
||||
** *Nix: gcc -O2 -shared -I$SQDIR $OPTS -fPIC -o base85.so base85.c
|
||||
** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR $OPTS -o base85.dylib base85.c
|
||||
** Win32: gcc -O2 -shared -I%SQDIR% %OPTS% -o base85.dll base85.c
|
||||
** Win32: cl /Os -I%SQDIR% %OPTS% base85.c -link -dll -out:base85.dll
|
||||
**
|
||||
** To build the standalone program, define PP symbol BASE85_STANDALONE. Eg.
|
||||
** *Nix or OSX: gcc -O2 -DBASE85_STANDALONE base85.c -o base85
|
||||
** Win32: gcc -O2 -DBASE85_STANDALONE -o base85.exe base85.c
|
||||
** Win32: cl /Os /MD -DBASE85_STANDALONE base85.c
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#ifndef OMIT_BASE85_CHECKER
|
||||
# include <ctype.h>
|
||||
#endif
|
||||
|
||||
#ifndef BASE85_STANDALONE
|
||||
|
||||
#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
|
||||
# include "sqlite3ext.h"
|
||||
#endif
|
||||
|
||||
SQLITE_EXTENSION_INIT1;
|
||||
|
||||
#else
|
||||
|
||||
# ifdef _WIN32
|
||||
# include <io.h>
|
||||
# include <fcntl.h>
|
||||
# else
|
||||
# define setmode(fd,m)
|
||||
# endif
|
||||
|
||||
static char *zHelp =
|
||||
"Usage: base85 <dirFlag> <binFile>\n"
|
||||
" <dirFlag> is either -r to read or -w to write <binFile>,\n"
|
||||
" content to be converted to/from base85 on stdout/stdin.\n"
|
||||
" <binFile> names a binary file to be rendered or created.\n"
|
||||
" Or, the name '-' refers to the stdin or stdout stream.\n"
|
||||
;
|
||||
|
||||
static void sayHelp(){
|
||||
printf("%s", zHelp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef UBYTE_TYPEDEF
|
||||
typedef unsigned char ubyte;
|
||||
# define UBYTE_TYPEDEF
|
||||
#endif
|
||||
|
||||
/* Classify c according to interval within USASCII set w.r.t. base85
|
||||
* Values of 1 and 3 are base85 numerals. Values of 0, 2, or 4 are not.
|
||||
*/
|
||||
#define B85_CLASS( c ) (((c)>='#')+((c)>'&')+((c)>='*')+((c)>'z'))
|
||||
|
||||
/* Provide digitValue to b85Numeral offset as a function of above class. */
|
||||
static ubyte b85_cOffset[] = { 0, '#', 0, '*'-4, 0 };
|
||||
#define B85_DNOS( c ) b85_cOffset[B85_CLASS(c)]
|
||||
|
||||
/* Say whether c is a base85 numeral. */
|
||||
#define IS_B85( c ) (B85_CLASS(c) & 1)
|
||||
|
||||
#if 0 /* Not used, */
|
||||
static ubyte base85DigitValue( char c ){
|
||||
ubyte dv = (ubyte)(c - '#');
|
||||
if( dv>87 ) return 0xff;
|
||||
return (dv > 3)? dv-3 : dv;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Width of base64 lines. Should be an integer multiple of 5. */
|
||||
#define B85_DARK_MAX 80
|
||||
|
||||
|
||||
static char * skipNonB85( char *s ){
|
||||
char c;
|
||||
while( (c = *s) && !IS_B85(c) ) ++s;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Convert small integer, known to be in 0..84 inclusive, to base85 numeral.
|
||||
* Do not use the macro form with argument expression having a side-effect.*/
|
||||
#if 0
|
||||
static char base85Numeral( ubyte b ){
|
||||
return (b < 4)? (char)(b + '#') : (char)(b - 4 + '*');
|
||||
}
|
||||
#else
|
||||
# define base85Numeral( dn )\
|
||||
((char)(((dn) < 4)? (char)((dn) + '#') : (char)((dn) - 4 + '*')))
|
||||
#endif
|
||||
|
||||
static char *putcs(char *pc, char *s){
|
||||
char c;
|
||||
while( (c = *s++)!=0 ) *pc++ = c;
|
||||
return pc;
|
||||
}
|
||||
|
||||
/* Encode a byte buffer into base85 text. If pSep!=0, it's a C string
|
||||
** to be appended to encoded groups to limit their length to B85_DARK_MAX
|
||||
** or to terminate the last group (to aid concatenation.)
|
||||
*/
|
||||
static char* toBase85( ubyte *pIn, int nbIn, char *pOut, char *pSep ){
|
||||
int nCol = 0;
|
||||
while( nbIn >= 4 ){
|
||||
int nco = 5;
|
||||
unsigned long qbv = (pIn[0]<<24)|(pIn[1]<<16)|(pIn[2]<<8)|pIn[3];
|
||||
while( nco > 0 ){
|
||||
unsigned nqv = (unsigned)(qbv/85UL);
|
||||
unsigned char dv = qbv - 85UL*nqv;
|
||||
qbv = nqv;
|
||||
pOut[--nco] = base85Numeral(dv);
|
||||
}
|
||||
nbIn -= 4;
|
||||
pIn += 4;
|
||||
pOut += 5;
|
||||
if( pSep && (nCol += 5)>=B85_DARK_MAX ){
|
||||
pOut = putcs(pOut, pSep);
|
||||
nCol = 0;
|
||||
}
|
||||
}
|
||||
if( nbIn > 0 ){
|
||||
int nco = nbIn + 1;
|
||||
unsigned long qv = *pIn++;
|
||||
int nbe = 1;
|
||||
while( nbe++ < nbIn ){
|
||||
qv = (qv<<8) | *pIn++;
|
||||
}
|
||||
nCol += nco;
|
||||
while( nco > 0 ){
|
||||
ubyte dv = (ubyte)(qv % 85);
|
||||
qv /= 85;
|
||||
pOut[--nco] = base85Numeral(dv);
|
||||
}
|
||||
pOut += (nbIn+1);
|
||||
}
|
||||
if( pSep && nCol>0 ) pOut = putcs(pOut, pSep);
|
||||
*pOut = 0;
|
||||
return pOut;
|
||||
}
|
||||
|
||||
/* Decode base85 text into a byte buffer. */
|
||||
static ubyte* fromBase85( char *pIn, int ncIn, ubyte *pOut ){
|
||||
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
|
||||
while( ncIn>0 ){
|
||||
static signed char nboi[] = { 0, 0, 1, 2, 3, 4 };
|
||||
char *pUse = skipNonB85(pIn);
|
||||
unsigned long qv = 0L;
|
||||
int nti, nbo;
|
||||
ncIn -= (pUse - pIn);
|
||||
pIn = pUse;
|
||||
nti = (ncIn>5)? 5 : ncIn;
|
||||
nbo = nboi[nti];
|
||||
if( nbo==0 ) break;
|
||||
while( nti>0 ){
|
||||
char c = *pIn++;
|
||||
ubyte cdo = B85_DNOS(c);
|
||||
--ncIn;
|
||||
if( cdo==0 ) break;
|
||||
qv = 85 * qv + (c - cdo);
|
||||
--nti;
|
||||
}
|
||||
nbo -= nti; /* Adjust for early (non-digit) end of group. */
|
||||
switch( nbo ){
|
||||
case 4:
|
||||
*pOut++ = (qv >> 24)&0xff;
|
||||
case 3:
|
||||
*pOut++ = (qv >> 16)&0xff;
|
||||
case 2:
|
||||
*pOut++ = (qv >> 8)&0xff;
|
||||
case 1:
|
||||
*pOut++ = qv&0xff;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pOut;
|
||||
}
|
||||
|
||||
#ifndef OMIT_BASE85_CHECKER
|
||||
/* Say whether input char sequence is all (base85 and/or whitespace).*/
|
||||
static int allBase85( char *p, int len ){
|
||||
char c;
|
||||
while( len-- > 0 && (c = *p++) != 0 ){
|
||||
if( !IS_B85(c) && !isspace(c) ) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef BASE85_STANDALONE
|
||||
|
||||
# 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);
|
||||
switch( sqlite3_value_type(av[0]) ){
|
||||
case SQLITE_TEXT:
|
||||
{
|
||||
int rv = allBase85( (char *)sqlite3_value_text(av[0]),
|
||||
sqlite3_value_bytes(av[0]) );
|
||||
sqlite3_result_int(context, rv);
|
||||
}
|
||||
break;
|
||||
case SQLITE_NULL:
|
||||
sqlite3_result_null(context);
|
||||
break;
|
||||
default:
|
||||
sqlite3_result_error(context, "is_base85 accepts only text or NULL", -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
/* This function does the work for the SQLite base85(x) UDF. */
|
||||
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
|
||||
int nb, nc, nv = sqlite3_value_bytes(av[0]);
|
||||
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
|
||||
SQLITE_LIMIT_LENGTH, -1);
|
||||
char *cBuf;
|
||||
ubyte *bBuf;
|
||||
assert(na==1);
|
||||
switch( sqlite3_value_type(av[0]) ){
|
||||
case SQLITE_BLOB:
|
||||
nb = nv;
|
||||
/* ulongs tail newlines tailenc+nul*/
|
||||
nc = 5*(nv/4) + nv%4 + nv/64+1 + 2;
|
||||
if( nvMax < nc ){
|
||||
sqlite3_result_error(context, "blob expanded to base85 too big", -1);
|
||||
return;
|
||||
}
|
||||
cBuf = sqlite3_malloc(nc);
|
||||
if( !cBuf ) goto memFail;
|
||||
bBuf = (ubyte*)sqlite3_value_blob(av[0]);
|
||||
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
|
||||
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
|
||||
break;
|
||||
case SQLITE_TEXT:
|
||||
nc = nv;
|
||||
nb = 4*(nv/5) + nv%5; /* may overestimate */
|
||||
if( nvMax < nb ){
|
||||
sqlite3_result_error(context, "blob from base85 may be too big", -1);
|
||||
return;
|
||||
}else if( nb<1 ){
|
||||
nb = 1;
|
||||
}
|
||||
bBuf = sqlite3_malloc(nb);
|
||||
if( !bBuf ) goto memFail;
|
||||
cBuf = (char *)sqlite3_value_text(av[0]);
|
||||
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
|
||||
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
|
||||
break;
|
||||
default:
|
||||
sqlite3_result_error(context, "base85 accepts only blob or text.", -1);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
memFail:
|
||||
sqlite3_result_error(context, "base85 OOM", -1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Establish linkage to running SQLite library.
|
||||
*/
|
||||
#ifndef SQLITE_SHELL_EXTFUNCS
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
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
|
||||
{
|
||||
int rc = sqlite3_create_function
|
||||
(db, "is_base85", 1,
|
||||
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_UTF8,
|
||||
0, is_base85, 0, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
# endif
|
||||
return sqlite3_create_function
|
||||
(db, "base85", 1,
|
||||
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
|
||||
0, base85, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Define some macros to allow this extension to be built into the shell
|
||||
** conveniently, in conjunction with use of SQLITE_SHELL_EXTFUNCS. This
|
||||
** allows shell.c, as distributed, to have this extension built in.
|
||||
*/
|
||||
# define BASE85_INIT(db) sqlite3_base85_init(db, 0, 0)
|
||||
# define BASE85_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
|
||||
|
||||
#else /* standalone program */
|
||||
|
||||
int main(int na, char *av[]){
|
||||
int cin;
|
||||
int rc = 0;
|
||||
ubyte bBuf[4*(B85_DARK_MAX/5)];
|
||||
char cBuf[5*(sizeof(bBuf)/4)+2];
|
||||
size_t nio;
|
||||
# ifndef OMIT_BASE85_CHECKER
|
||||
int b85Clean = 1;
|
||||
# endif
|
||||
char rw;
|
||||
FILE *fb = 0, *foc = 0;
|
||||
char fmode[3] = "xb";
|
||||
if( na < 3 || av[1][0]!='-' || (rw = av[1][1])==0 || (rw!='r' && rw!='w') ){
|
||||
sayHelp();
|
||||
return 0;
|
||||
}
|
||||
fmode[0] = rw;
|
||||
if( av[2][0]=='-' && av[2][1]==0 ){
|
||||
switch( rw ){
|
||||
case 'r':
|
||||
fb = stdin;
|
||||
setmode(fileno(stdin), O_BINARY);
|
||||
break;
|
||||
case 'w':
|
||||
fb = stdout;
|
||||
setmode(fileno(stdout), O_BINARY);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
fb = fopen(av[2], fmode);
|
||||
foc = fb;
|
||||
}
|
||||
if( !fb ){
|
||||
fprintf(stderr, "Cannot open %s for %c\n", av[2], rw);
|
||||
rc = 1;
|
||||
}else{
|
||||
switch( rw ){
|
||||
case 'r':
|
||||
while( (nio = fread( bBuf, 1, sizeof(bBuf), fb))>0 ){
|
||||
toBase85( bBuf, (int)nio, cBuf, 0 );
|
||||
fprintf(stdout, "%s\n", cBuf);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
while( 0 != fgets(cBuf, sizeof(cBuf), stdin) ){
|
||||
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
|
||||
b85Clean &= allBase85( cBuf, nc );
|
||||
# endif
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sayHelp();
|
||||
rc = 1;
|
||||
}
|
||||
if( foc ) fclose(foc);
|
||||
}
|
||||
# ifndef OMIT_BASE85_CHECKER
|
||||
if( !b85Clean ){
|
||||
fprintf(stderr, "Base85 input had non-base85 dark or control content.\n");
|
||||
}
|
||||
# endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
** 2022-11-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 source allows multiple SQLite extensions to be either: combined
|
||||
** into a single runtime-loadable library; or built into the SQLite shell
|
||||
** using a preprocessing convention set by src/shell.c.in (and shell.c).
|
||||
**
|
||||
** Presently, it combines the base64.c and base85.c extensions. However,
|
||||
** it can be used as a template for other combinations.
|
||||
**
|
||||
** Example usages:
|
||||
**
|
||||
** - Build a runtime-loadable extension from SQLite checkout directory:
|
||||
** *Nix, OSX: gcc -O2 -shared -I. -fPIC -o basexx.so ext/misc/basexx.c
|
||||
** Win32: cl /Os -I. ext/misc/basexx.c -link -dll -out:basexx.dll
|
||||
**
|
||||
** - Incorporate as built-in in sqlite3 shell:
|
||||
** *Nix, OSX with gcc on a like platform:
|
||||
** export mop1=-DSQLITE_SHELL_EXTSRC=ext/misc/basexx.c
|
||||
** export mop2=-DSQLITE_SHELL_EXTFUNCS=BASEXX
|
||||
** make sqlite3 "OPTS=$mop1 $mop2"
|
||||
** Win32 with Microsoft toolset on Windows:
|
||||
** set mop1=-DSQLITE_SHELL_EXTSRC=ext/misc/basexx.c
|
||||
** set mop2=-DSQLITE_SHELL_EXTFUNCS=BASEXX
|
||||
** set mops="OPTS=%mop1% %mop2%"
|
||||
** nmake -f Makefile.msc sqlite3.exe %mops%
|
||||
*/
|
||||
|
||||
#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
|
||||
# include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1;
|
||||
#endif
|
||||
|
||||
static void init_api_ptr(const sqlite3_api_routines *pApi){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
}
|
||||
|
||||
#undef SQLITE_EXTENSION_INIT1
|
||||
#define SQLITE_EXTENSION_INIT1 /* */
|
||||
#undef SQLITE_EXTENSION_INIT2
|
||||
#define SQLITE_EXTENSION_INIT2(v) (void)v
|
||||
|
||||
/* These next 2 undef's are only needed because the entry point names
|
||||
* collide when formulated per the rules stated for loadable extension
|
||||
* entry point names that will be deduced from the file basenames.
|
||||
*/
|
||||
#undef sqlite3_base_init
|
||||
#define sqlite3_base_init sqlite3_base64_init
|
||||
#include "base64.c"
|
||||
|
||||
#undef sqlite3_base_init
|
||||
#define sqlite3_base_init sqlite3_base85_init
|
||||
#include "base85.c"
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_basexx_init(sqlite3 *db, char **pzErr,
|
||||
const sqlite3_api_routines *pApi){
|
||||
init_api_ptr(pApi);
|
||||
int rc1 = BASE64_INIT(db);
|
||||
int rc2 = BASE85_INIT(db);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( rc1==SQLITE_OK && rc2==SQLITE_OK ){
|
||||
BASE64_EXPOSE(db, pzErr);
|
||||
BASE64_EXPOSE(db, pzErr);
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
# define BASEXX_INIT(db) sqlite3_basexx_init(db, 0, 0)
|
||||
# define BASEXX_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
|
||||
+4
-4
@@ -185,7 +185,7 @@ static unsigned re_next_char(ReInput *p){
|
||||
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
|
||||
p->i += 2;
|
||||
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
||||
}else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
}else if( (c&0xf8)==0xf0 && p->i+2<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
|
||||
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
|
||||
| (p->z[p->i+2]&0x3f);
|
||||
@@ -712,15 +712,15 @@ static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
|
||||
** one or more matching characters, enter those matching characters into
|
||||
** zInit[]. The re_match() routine can then search ahead in the input
|
||||
** string looking for the initial match without having to run the whole
|
||||
** regex engine over the string. Do not worry able trying to match
|
||||
** regex engine over the string. Do not worry about trying to match
|
||||
** unicode characters beyond plane 0 - those are very rare and this is
|
||||
** just an optimization. */
|
||||
if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){
|
||||
for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
|
||||
unsigned x = pRe->aArg[i];
|
||||
if( x<=127 ){
|
||||
if( x<=0x7f ){
|
||||
pRe->zInit[j++] = (unsigned char)x;
|
||||
}else if( x<=0xfff ){
|
||||
}else if( x<=0x7ff ){
|
||||
pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
|
||||
pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||||
}else if( x<=0xffff ){
|
||||
|
||||
+4
-4
@@ -531,7 +531,7 @@ static void sha3Func(
|
||||
/* Compute a string using sqlite3_vsnprintf() with a maximum length
|
||||
** of 50 bytes and add it to the hash.
|
||||
*/
|
||||
static void hash_step_vformat(
|
||||
static void sha3_step_vformat(
|
||||
SHA3Context *p, /* Add content to this context */
|
||||
const char *zFormat,
|
||||
...
|
||||
@@ -627,7 +627,7 @@ static void sha3QueryFunc(
|
||||
z = sqlite3_sql(pStmt);
|
||||
if( z ){
|
||||
n = (int)strlen(z);
|
||||
hash_step_vformat(&cx,"S%d:",n);
|
||||
sha3_step_vformat(&cx,"S%d:",n);
|
||||
SHA3Update(&cx,(unsigned char*)z,n);
|
||||
}
|
||||
|
||||
@@ -671,14 +671,14 @@ static void sha3QueryFunc(
|
||||
case SQLITE_TEXT: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_text(pStmt, i);
|
||||
hash_step_vformat(&cx,"T%d:",n2);
|
||||
sha3_step_vformat(&cx,"T%d:",n2);
|
||||
SHA3Update(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
|
||||
hash_step_vformat(&cx,"B%d:",n2);
|
||||
sha3_step_vformat(&cx,"B%d:",n2);
|
||||
SHA3Update(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# 2022 November 07
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set ::testprefix rburename
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
}
|
||||
|
||||
forcedelete test.db-vacuum
|
||||
|
||||
proc my_rename {old new} {
|
||||
lappend ::my_rename_calls [list [file tail $old] [file tail $new]]
|
||||
file rename $old $new
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
sqlite3rbu_vacuum rbu test.db
|
||||
rbu rename_handler my_rename
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
rbu close
|
||||
} SQLITE_DONE
|
||||
|
||||
do_test 1.2 {
|
||||
set ::my_rename_calls
|
||||
} {{test.db-oal test.db-wal}}
|
||||
|
||||
proc my_rename {old new} {
|
||||
error "something went wrong"
|
||||
}
|
||||
|
||||
do_test 1.3 {
|
||||
sqlite3rbu_vacuum rbu test.db
|
||||
rbu rename_handler my_rename
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {1 SQLITE_IOERR}
|
||||
|
||||
finish_test
|
||||
@@ -227,10 +227,11 @@ do_test 6.1 {
|
||||
rbu close
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
SELECT 1 FROM sqlite_master LIMIT 1;
|
||||
PRAGMA wal_checkpoint;
|
||||
} {1 0 4 4}
|
||||
do_test 6.2 {
|
||||
execsql { SELECT 1 FROM sqlite_master LIMIT 1 }
|
||||
execsql { PRAGMA wal_checkpoint }
|
||||
execsql { SELECT 1 FROM sqlite_master LIMIT 1 }
|
||||
} {1}
|
||||
|
||||
do_test 6.3 {
|
||||
sqlite3rbu_vacuum rbu test.db test.db2
|
||||
|
||||
+53
-27
@@ -393,6 +393,8 @@ struct sqlite3rbu {
|
||||
int nPagePerSector; /* Pages per sector for pTargetFd */
|
||||
i64 iOalSz;
|
||||
i64 nPhaseOneStep;
|
||||
void *pRenameArg;
|
||||
int (*xRename)(void*, const char*, const char*);
|
||||
|
||||
/* The following state variables are used as part of the incremental
|
||||
** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding
|
||||
@@ -2781,7 +2783,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, sqlite3 *dbMain, int *pbRetry){
|
||||
sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
|
||||
if( p->zState==0 ){
|
||||
const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
|
||||
p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
|
||||
p->zState = rbuMPrintf(p, "file:///%s-vacuum?modeof=%s", zFile, zFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3241,32 +3243,7 @@ static void rbuMoveOalFile(sqlite3rbu *p){
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
#if defined(_WIN32_WCE)
|
||||
{
|
||||
LPWSTR zWideOal;
|
||||
LPWSTR zWideWal;
|
||||
|
||||
zWideOal = rbuWinUtf8ToUnicode(zOal);
|
||||
if( zWideOal ){
|
||||
zWideWal = rbuWinUtf8ToUnicode(zWal);
|
||||
if( zWideWal ){
|
||||
if( MoveFileW(zWideOal, zWideWal) ){
|
||||
p->rc = SQLITE_OK;
|
||||
}else{
|
||||
p->rc = SQLITE_IOERR;
|
||||
}
|
||||
sqlite3_free(zWideWal);
|
||||
}else{
|
||||
p->rc = SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
sqlite3_free(zWideOal);
|
||||
}else{
|
||||
p->rc = SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
}
|
||||
#else
|
||||
p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
|
||||
#endif
|
||||
p->rc = p->xRename(p->pRenameArg, zOal, zWal);
|
||||
}
|
||||
|
||||
if( p->rc!=SQLITE_OK
|
||||
@@ -4005,6 +3982,7 @@ static sqlite3rbu *openRbuHandle(
|
||||
|
||||
/* Create the custom VFS. */
|
||||
memset(p, 0, sizeof(sqlite3rbu));
|
||||
sqlite3rbu_rename_handler(p, 0, 0);
|
||||
rbuCreateVfs(p);
|
||||
|
||||
/* Open the target, RBU and state databases */
|
||||
@@ -4396,6 +4374,54 @@ int sqlite3rbu_savestate(sqlite3rbu *p){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Default xRename callback for RBU.
|
||||
*/
|
||||
static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){
|
||||
int rc = SQLITE_OK;
|
||||
#if defined(_WIN32_WCE)
|
||||
{
|
||||
LPWSTR zWideOld;
|
||||
LPWSTR zWideNew;
|
||||
|
||||
zWideOld = rbuWinUtf8ToUnicode(zOld);
|
||||
if( zWideOld ){
|
||||
zWideNew = rbuWinUtf8ToUnicode(zNew);
|
||||
if( zWideNew ){
|
||||
if( MoveFileW(zWideOld, zWideNew) ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = SQLITE_IOERR;
|
||||
}
|
||||
sqlite3_free(zWideNew);
|
||||
}else{
|
||||
rc = SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
sqlite3_free(zWideOld);
|
||||
}else{
|
||||
rc = SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
}
|
||||
#else
|
||||
rc = rename(zOld, zNew) ? SQLITE_IOERR : SQLITE_OK;
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sqlite3rbu_rename_handler(
|
||||
sqlite3rbu *pRbu,
|
||||
void *pArg,
|
||||
int (*xRename)(void *pArg, const char *zOld, const char *zNew)
|
||||
){
|
||||
if( xRename ){
|
||||
pRbu->xRename = xRename;
|
||||
pRbu->pRenameArg = pArg;
|
||||
}else{
|
||||
pRbu->xRename = xDefaultRename;
|
||||
pRbu->pRenameArg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour
|
||||
** of a standard VFS in the following ways:
|
||||
|
||||
@@ -544,6 +544,34 @@ SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
|
||||
|
||||
SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
|
||||
|
||||
/*
|
||||
** As part of applying an RBU update or performing an RBU vacuum operation,
|
||||
** the system must at one point move the *-oal file to the equivalent *-wal
|
||||
** path. Normally, it does this by invoking POSIX function rename(2) directly.
|
||||
** Except on WINCE platforms, where it uses win32 API MoveFileW(). This
|
||||
** function may be used to register a callback that the RBU module will invoke
|
||||
** instead of one of these APIs.
|
||||
**
|
||||
** If a callback is registered with an RBU handle, it invokes it instead
|
||||
** of rename(2) when it needs to move a file within the file-system. The
|
||||
** first argument passed to the xRename() callback is a copy of the second
|
||||
** argument (pArg) passed to this function. The second is the full path
|
||||
** to the file to move and the third the full path to which it should be
|
||||
** moved. The callback function should return SQLITE_OK to indicate
|
||||
** success. If an error occurs, it should return an SQLite error code.
|
||||
** In this case the RBU operation will be abandoned and the error returned
|
||||
** to the RBU user.
|
||||
**
|
||||
** Passing a NULL pointer in place of the xRename argument to this function
|
||||
** restores the default behaviour.
|
||||
*/
|
||||
SQLITE_API void sqlite3rbu_rename_handler(
|
||||
sqlite3rbu *pRbu,
|
||||
void *pArg,
|
||||
int (*xRename)(void *pArg, const char *zOld, const char *zNew)
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** Create an RBU VFS named zName that accesses the underlying file-system
|
||||
** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
|
||||
|
||||
+54
-3
@@ -26,6 +26,14 @@
|
||||
# endif
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct TestRbu TestRbu;
|
||||
struct TestRbu {
|
||||
sqlite3rbu *pRbu;
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *xRename;
|
||||
};
|
||||
|
||||
/* From main.c */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
@@ -55,6 +63,20 @@ void test_rbu_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){
|
||||
Tcl_DecrRefCount(pScript);
|
||||
}
|
||||
|
||||
static int xRenameCallback(void *pArg, const char *zOld, const char *zNew){
|
||||
int rc = SQLITE_OK;
|
||||
TestRbu *pTest = (TestRbu*)pArg;
|
||||
Tcl_Obj *pEval = Tcl_DuplicateObj(pTest->xRename);
|
||||
|
||||
Tcl_IncrRefCount(pEval);
|
||||
Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zOld, -1));
|
||||
Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zNew, -1));
|
||||
|
||||
rc = Tcl_EvalObjEx(pTest->interp, pEval, TCL_GLOBAL_ONLY);
|
||||
Tcl_DecrRefCount(pEval);
|
||||
|
||||
return rc ? SQLITE_IOERR : SQLITE_OK;
|
||||
}
|
||||
|
||||
static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
|
||||
ClientData clientData,
|
||||
@@ -63,7 +85,8 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int ret = TCL_OK;
|
||||
sqlite3rbu *pRbu = (sqlite3rbu*)clientData;
|
||||
TestRbu *pTest = (TestRbu*)clientData;
|
||||
sqlite3rbu *pRbu = pTest->pRbu;
|
||||
struct RbuCmd {
|
||||
const char *zName;
|
||||
int nArg;
|
||||
@@ -82,6 +105,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
|
||||
{"temp_size_limit", 3, "LIMIT"}, /* 10 */
|
||||
{"temp_size", 2, ""}, /* 11 */
|
||||
{"dbRbu_eval", 3, "SQL"}, /* 12 */
|
||||
{"rename_handler", 3, "SCRIPT"},/* 13 */
|
||||
{0,0,0}
|
||||
};
|
||||
int iCmd;
|
||||
@@ -127,6 +151,8 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
|
||||
}
|
||||
ret = TCL_ERROR;
|
||||
}
|
||||
if( pTest->xRename ) Tcl_DecrRefCount(pTest->xRename);
|
||||
ckfree(pTest);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -214,6 +240,19 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
|
||||
break;
|
||||
}
|
||||
|
||||
case 13: /* rename_handler */ {
|
||||
Tcl_Obj *pScript = objv[2];
|
||||
assert( !sqlite3_stricmp(aCmd[13].zName, "rename_handler") );
|
||||
if( Tcl_GetCharLength(pScript)==0 ){
|
||||
sqlite3rbu_rename_handler(pRbu, 0, 0);
|
||||
}else{
|
||||
pTest->xRename = Tcl_DuplicateObj(pScript);
|
||||
Tcl_IncrRefCount(pTest->xRename);
|
||||
sqlite3rbu_rename_handler(pRbu, pTest, xRenameCallback);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: /* seems unlikely */
|
||||
assert( !"cannot happen" );
|
||||
break;
|
||||
@@ -222,6 +261,18 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void createRbuWrapper(
|
||||
Tcl_Interp *interp,
|
||||
const char *zCmd,
|
||||
sqlite3rbu *pRbu
|
||||
){
|
||||
TestRbu *pTest = (TestRbu*)ckalloc(sizeof(TestRbu));
|
||||
memset(pTest, 0, sizeof(TestRbu));
|
||||
pTest->pRbu = pRbu;
|
||||
pTest->interp = interp;
|
||||
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pTest, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: sqlite3rbu CMD <target-db> <rbu-db> ?<state-db>?
|
||||
*/
|
||||
@@ -247,7 +298,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu(
|
||||
if( objc==5 ) zStateDb = Tcl_GetString(objv[4]);
|
||||
|
||||
pRbu = sqlite3rbu_open(zTarget, zRbu, zStateDb);
|
||||
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
|
||||
createRbuWrapper(interp, zCmd, pRbu);
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -276,7 +327,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu_vacuum(
|
||||
if( zStateDb && zStateDb[0]=='\0' ) zStateDb = 0;
|
||||
|
||||
pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
|
||||
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
|
||||
createRbuWrapper(interp, zCmd, pRbu);
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
** It contains one entry for each b-tree pointer between a parent and
|
||||
** child page in the database.
|
||||
*/
|
||||
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
|
||||
@@ -82,6 +83,8 @@ SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
#define DBDATA_PADDING_BYTES 100
|
||||
|
||||
typedef struct DbdataTable DbdataTable;
|
||||
@@ -425,7 +428,7 @@ static void dbdataValue(
|
||||
u32 enc,
|
||||
int eType,
|
||||
u8 *pData,
|
||||
int nData
|
||||
sqlite3_int64 nData
|
||||
){
|
||||
if( eType>=0 && dbdataValueBytes(eType)<=nData ){
|
||||
switch( eType ){
|
||||
@@ -861,7 +864,7 @@ static int dbdataColumn(
|
||||
case DBDATA_COLUMN_VALUE: {
|
||||
if( pCsr->iField<0 ){
|
||||
sqlite3_result_int64(ctx, pCsr->iIntkey);
|
||||
}else{
|
||||
}else if( &pCsr->pRec[pCsr->nRec] >= pCsr->pPtr ){
|
||||
sqlite3_int64 iType;
|
||||
dbdataGetVarintU32(pCsr->pHdrPtr, &iType);
|
||||
dbdataValue(
|
||||
@@ -935,3 +938,5 @@ int sqlite3_dbdata_init(
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
return sqlite3DbdataRegister(db);
|
||||
}
|
||||
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
@@ -10,16 +10,9 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set testprefix recover1
|
||||
|
||||
|
||||
|
||||
proc compare_result {db1 db2 sql} {
|
||||
set r1 [$db1 eval $sql]
|
||||
set r2 [$db2 eval $sql]
|
||||
@@ -271,11 +264,13 @@ do_recover_test 14
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 15.1 {
|
||||
execsql {
|
||||
PRAGMA journal_mode=OFF;
|
||||
PRAGMA mmap_size=10;
|
||||
}
|
||||
do_execsql_test 15.1 {
|
||||
CREATE TABLE t1(x);
|
||||
} {off 10}
|
||||
} {}
|
||||
do_recover_test 15
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
|
||||
|
||||
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
|
||||
if {[info commands sqlite3_recover_init]==""} {
|
||||
finish_test
|
||||
return -code return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12,17 +12,9 @@
|
||||
# Tests for the SQLITE_RECOVER_ROWIDS option.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix recoverclobber
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test; return
|
||||
}
|
||||
|
||||
proc recover {db output} {
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
$R run
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set testprefix recovercorrupt
|
||||
|
||||
database_may_be_corrupt
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set testprefix recovercorrupt2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set testprefix recoverfault
|
||||
|
||||
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set testprefix recoverfault2
|
||||
|
||||
|
||||
|
||||
+16
-20
@@ -11,17 +11,9 @@
|
||||
#
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix recoverold
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test; return
|
||||
}
|
||||
|
||||
proc compare_result {db1 db2 sql} {
|
||||
set r1 [$db1 eval $sql]
|
||||
set r2 [$db2 eval $sql]
|
||||
@@ -71,7 +63,10 @@ proc do_recover_test {tn {tsql {}} {res {}}} {
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval [join $::sqlhook ";"]
|
||||
|
||||
|
||||
db cache flush
|
||||
if {$tsql==""} {
|
||||
compare_dbs db db2
|
||||
uplevel [list do_test $tn.sql [list compare_dbs db db2] {}]
|
||||
} else {
|
||||
uplevel [list do_execsql_test -db db2 $tn.sql $tsql $res]
|
||||
@@ -155,7 +150,6 @@ do_recover_test 2.4.1 {
|
||||
2 2 3 {} 8 9 7
|
||||
}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 2.5 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
WITH s(i) AS (
|
||||
@@ -173,17 +167,19 @@ do_recover_test 2.5.1 {
|
||||
2 2 3 {} 8 9 7
|
||||
}
|
||||
|
||||
do_test 2.6 {
|
||||
forcedelete test.db2
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
$R config lostandfound lost_and_found
|
||||
$R config freelistcorrupt 1
|
||||
$R run
|
||||
$R finish
|
||||
sqlite3 db2 test.db2
|
||||
execsql { SELECT count(*) FROM lost_and_found_1; } db2
|
||||
} {103}
|
||||
db2 close
|
||||
ifcapable !secure_delete {
|
||||
do_test 2.6 {
|
||||
forcedelete test.db2
|
||||
set R [sqlite3_recover_init db main test.db2]
|
||||
$R config lostandfound lost_and_found
|
||||
$R config freelistcorrupt 1
|
||||
$R run
|
||||
$R finish
|
||||
sqlite3 db2 test.db2
|
||||
execsql { SELECT count(*) FROM lost_and_found_1; } db2
|
||||
} {103}
|
||||
db2 close
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
breakpoint
|
||||
|
||||
@@ -10,11 +10,7 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
db close
|
||||
sqlite3_test_control_pending_byte 0x1000000
|
||||
@@ -27,6 +23,7 @@ foreach {pgsz bOverflow} {
|
||||
} {
|
||||
reset_db
|
||||
execsql "PRAGMA page_size = $pgsz"
|
||||
execsql "PRAGMA auto_vacuum = 0"
|
||||
do_execsql_test 1.$pgsz.$bOverflow.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(b, a, c);
|
||||
|
||||
@@ -12,17 +12,9 @@
|
||||
# Tests for the SQLITE_RECOVER_ROWIDS option.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix recoverrowid
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test; return
|
||||
}
|
||||
|
||||
proc recover {db bRowids output} {
|
||||
forcedelete $output
|
||||
|
||||
|
||||
@@ -12,18 +12,11 @@
|
||||
# Tests for the SQLITE_RECOVER_SLOWINDEXES option.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix recoverslowidx
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test; return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 3), (4, 4);
|
||||
|
||||
@@ -11,17 +11,9 @@
|
||||
#
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] recover_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix recoversql
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test; return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE "x.1" (x, y);
|
||||
INSERT INTO "x.1" VALUES(1, 1), (2, 2), (3, 3);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Declaration for public API function in file dbdata.c. This may be called
|
||||
** with NULL as the final two arguments to register the sqlite_dbptr and
|
||||
@@ -280,10 +282,16 @@ static RecoverGlobal recover_g;
|
||||
*/
|
||||
#define RECOVER_ROWID_DEFAULT 1
|
||||
|
||||
/*
|
||||
** Mutex handling:
|
||||
**
|
||||
** recoverEnterMutex() - Enter the recovery mutex
|
||||
** recoverLeaveMutex() - Leave the recovery mutex
|
||||
** recoverAssertMutexHeld() - Assert that the recovery mutex is held
|
||||
*/
|
||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
|
||||
# define recoverEnterMutex()
|
||||
# define recoverLeaveMutex()
|
||||
# define recoverAssertMutexHeld()
|
||||
#else
|
||||
static void recoverEnterMutex(void){
|
||||
sqlite3_mutex_enter(sqlite3_mutex_alloc(RECOVER_MUTEX_ID));
|
||||
@@ -291,9 +299,13 @@ static void recoverEnterMutex(void){
|
||||
static void recoverLeaveMutex(void){
|
||||
sqlite3_mutex_leave(sqlite3_mutex_alloc(RECOVER_MUTEX_ID));
|
||||
}
|
||||
#endif
|
||||
#if SQLITE_THREADSAFE+0>=1 && defined(SQLITE_DEBUG)
|
||||
static void recoverAssertMutexHeld(void){
|
||||
assert( sqlite3_mutex_held(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)) );
|
||||
}
|
||||
#else
|
||||
# define recoverAssertMutexHeld()
|
||||
#endif
|
||||
|
||||
|
||||
@@ -301,11 +313,8 @@ static void recoverAssertMutexHeld(void){
|
||||
** Like strlen(). But handles NULL pointer arguments.
|
||||
*/
|
||||
static int recoverStrlen(const char *zStr){
|
||||
int nRet = 0;
|
||||
if( zStr ){
|
||||
while( zStr[nRet] ) nRet++;
|
||||
}
|
||||
return nRet;
|
||||
if( zStr==0 ) return 0;
|
||||
return (int)(strlen(zStr)&0x7fffffff);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2844,3 +2853,5 @@ int sqlite3_recover_finish(sqlite3_recover *p){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <tcl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
typedef struct TestRecover TestRecover;
|
||||
struct TestRecover {
|
||||
sqlite3_recover *p;
|
||||
@@ -284,9 +286,10 @@ static int test_sqlite3_dbdata_init(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
int TestRecover_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
struct Cmd {
|
||||
const char *zCmd;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
@@ -302,7 +305,7 @@ int TestRecover_Init(Tcl_Interp *interp){
|
||||
struct Cmd *p = &aCmd[i];
|
||||
Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, p->pArg, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
+368
-155
@@ -20,6 +20,14 @@
|
||||
# above-listed o? target names.
|
||||
#
|
||||
# clean = clean up
|
||||
#
|
||||
# Required tools beyond those needed for the canonical builds:
|
||||
#
|
||||
# - Emscripten SDK: https://emscripten.org/docs/getting_started/downloads.html
|
||||
# - The bash shell
|
||||
# - GNU make, GNU sed, GNU awk, GNU grep (all in the $PATH)
|
||||
# - wasm-strip for release builds: https://github.com/WebAssembly/wabt
|
||||
# - InfoZip for 'dist' zip file
|
||||
########################################################################
|
||||
SHELL := $(shell which bash 2>/dev/null)
|
||||
MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
@@ -71,17 +79,19 @@ dir.jacc := jaccwabyt
|
||||
dir.common := common
|
||||
dir.fiddle := fiddle
|
||||
dir.tool := $(dir.top)/tool
|
||||
CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ $(dir.fiddle)/*~
|
||||
|
||||
########################################################################
|
||||
# dir.dout = output dir for deliverables.
|
||||
#
|
||||
# MAINTENANCE REMINDER: the output .js and .wasm files of emcc must be
|
||||
# in _this_ dir, rather than a subdir, or else parts of the generated
|
||||
# code get confused and cannot load property. Specifically, when X.js
|
||||
# loads X.wasm, whether or not X.js uses the correct path for X.wasm
|
||||
# depends on how it's loaded: an HTML script tag will resolve it
|
||||
# intuitively, whereas a Worker's call to importScripts() will not.
|
||||
# That's a fundamental incompatibility with how URL resolution in
|
||||
# JS happens between those two contexts. See:
|
||||
# MAINTENANCE REMINDER: the output .js and .wasm files of certain emcc
|
||||
# buildables must be in _this_ dir, rather than a subdir, or else
|
||||
# parts of the generated code get confused and cannot load
|
||||
# property. Specifically, when X.js loads X.wasm, whether or not X.js
|
||||
# uses the correct path for X.wasm depends on how it's loaded: an HTML
|
||||
# script tag will resolve it intuitively, whereas a Worker's call to
|
||||
# importScripts() will not. That's a fundamental incompatibility with
|
||||
# how URL resolution in JS happens between those two contexts. See:
|
||||
#
|
||||
# https://zzz.buzz/2017/03/14/relative-uris-in-web-development/
|
||||
#
|
||||
@@ -96,7 +106,7 @@ dir.dout := $(dir.wasm)/jswasm
|
||||
# dir.tmp = output dir for intermediary build files, as opposed to
|
||||
# end-user deliverables.
|
||||
dir.tmp := $(dir.wasm)/bld
|
||||
#CLEAN_FILES += $(wildcard $(dir.dout)/*) $(wildcard $(dir.tmp)/*)
|
||||
CLEAN_FILES += $(dir.tmp)/* $(dir.dout)/*
|
||||
ifeq (,$(wildcard $(dir.dout)))
|
||||
dir._tmp := $(shell mkdir -p $(dir.dout))
|
||||
endif
|
||||
@@ -104,13 +114,12 @@ ifeq (,$(wildcard $(dir.tmp)))
|
||||
dir._tmp := $(shell mkdir -p $(dir.tmp))
|
||||
endif
|
||||
|
||||
cflags.common := -I. -I.. -I$(dir.top)
|
||||
CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~
|
||||
emcc.WASM_BIGINT ?= 1
|
||||
sqlite3.c := $(dir.top)/sqlite3.c
|
||||
sqlite3.h := $(dir.top)/sqlite3.h
|
||||
# Most SQLITE_OPT flags are set in sqlite3-wasm.c but we need them
|
||||
# made explicit here for building speedtest1.c.
|
||||
SQLITE_OPT = \
|
||||
-DSQLITE_ENABLE_FTS4 \
|
||||
-DSQLITE_ENABLE_FTS5 \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
|
||||
@@ -130,16 +139,100 @@ SQLITE_OPT = \
|
||||
'-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_WASM_ENABLE_C_TESTS
|
||||
# ^^^ most flags are set in sqlite3-wasm.c but we need them
|
||||
# made explicit here for building speedtest1.c.
|
||||
|
||||
ifneq (,$(filter release,$(MAKECMDGOALS)))
|
||||
emcc_opt ?= -Oz -flto
|
||||
$(sqlite3.c) $(sqlite3.h):
|
||||
$(MAKE) -C $(dir.top) sqlite3.c
|
||||
|
||||
.PHONY: clean distclean
|
||||
clean:
|
||||
-rm -f $(CLEAN_FILES)
|
||||
distclean: clean
|
||||
-rm -f $(DISTCLEAN_FILES)
|
||||
|
||||
ifeq (release,$(filter release,$(MAKECMDGOALS)))
|
||||
ifeq (,$(wasm-strip))
|
||||
$(error Cannot make release-quality binary because wasm-strip is not available. \
|
||||
See notes in the warning above)
|
||||
endif
|
||||
else
|
||||
emcc_opt ?= -O0
|
||||
# ^^^^ build times for -O levels higher than 0 are painful at
|
||||
# dev-time.
|
||||
$(info Development build. Use '$(MAKE) release' for a smaller release build.)
|
||||
endif
|
||||
|
||||
# bin.version-info = binary to output various sqlite3 version info for
|
||||
# embedding in the JS files and in building the distribution zip file.
|
||||
# It must NOT be in $(dir.tmp) because we need it to survive the
|
||||
# cleanup process for the dist build to work properly.
|
||||
bin.version-info := $(dir.wasm)/version-info
|
||||
$(bin.version-info): $(dir.wasm)/version-info.c $(sqlite3.h) $(MAKEFILE)
|
||||
$(CC) -O0 -I$(dir.top) -o $@ $<
|
||||
DISTCLEAN_FILES += $(bin.version-info)
|
||||
|
||||
# bin.stripcomments is used for stripping C/C++-style comments from JS
|
||||
# files. The JS files contain large chunks of documentation which we
|
||||
# don't need for all builds. That app's -k flag is of particular
|
||||
# importance here, as it allows us to retain the opening comment
|
||||
# blocks, which contain the license header and version info.
|
||||
bin.stripccomments := $(dir.tool)/stripccomments
|
||||
$(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE)
|
||||
$(CC) -o $@ $<
|
||||
DISTCLEAN_FILES += $(bin.stripccomments)
|
||||
|
||||
|
||||
########################################################################
|
||||
# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via ./c-pp -f
|
||||
# $(1) ...
|
||||
#
|
||||
# Historical notes:
|
||||
#
|
||||
# - We first attempted to use gcc and/or clang to preprocess JS files
|
||||
# in the same way we would normally do C files, but C-specific quirks
|
||||
# of each makes that untennable.
|
||||
#
|
||||
# - We implemented c-pp.c (the C-Minus Pre-processor) as a custom
|
||||
# generic/file-format-agnostic preprocessor to enable us to pack
|
||||
# code for different target builds into the same JS files. Most
|
||||
# notably, some ES6 module (a.k.a. ESM) features cannot legally be
|
||||
# referenced at all in non-ESM code, e.g. the "import" and "export"
|
||||
# keywords. This preprocessing step permits us to swap out sections
|
||||
# of code where necessary for ESM and non-ESM (a.k.a. vanilla JS)
|
||||
# require different implementations. The alternative to such
|
||||
# preprocessing, would be to have separate source files for ES6
|
||||
# builds, which would have a higher maintenance burden than c-pp.c
|
||||
# seems likely to.
|
||||
#
|
||||
# c-pp.c was written specifically for the sqlite project's JavaScript
|
||||
# builds but is maintained as a standalone project:
|
||||
# https://fossil.wanderinghorse.net/r/c-pp
|
||||
bin.c-pp := ./c-pp
|
||||
$(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE)
|
||||
$(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top)
|
||||
define C-PP.FILTER
|
||||
# Create $2 from $1 using $(bin.c-pp)
|
||||
# $1 = Input file: c-pp -f $(1).js
|
||||
# $2 = Output file: c-pp -o $(2).js
|
||||
# $3 = optional c-pp -D... flags
|
||||
$(2): $(1) $$(MAKEFILE) $$(bin.c-pp)
|
||||
$$(bin.c-pp) -f $(1) -o $$@ $(3)
|
||||
CLEAN_FILES += $(2)
|
||||
endef
|
||||
c-pp.D.vanilla ?=
|
||||
c-pp.D.esm ?= -Dtarget=es6-module
|
||||
# /end C-PP.FILTER
|
||||
########################################################################
|
||||
|
||||
|
||||
# cflags.common = C compiler flags for all builds
|
||||
cflags.common := -I. -I.. -I$(dir.top)
|
||||
# emcc.WASM_BIGINT = 1 for BigInt (C int64) support, else 0. The API
|
||||
# disables certain features if BigInt is not enabled and such builds
|
||||
# _are not tested_ on any regular basis.
|
||||
emcc.WASM_BIGINT ?= 1
|
||||
|
||||
# emcc_opt = optimization-related flags. These are primarily used by
|
||||
# the various oX targets. build times for -O levels higher than 0 are
|
||||
# painful at dev-time.
|
||||
emcc_opt ?= -O0
|
||||
|
||||
# When passing emcc_opt from the CLI, += and re-assignment have no
|
||||
# effect, so emcc_opt+=-g3 doesn't work. So...
|
||||
emcc_opt_full := $(emcc_opt) -g3
|
||||
@@ -165,47 +258,26 @@ emcc_opt_full := $(emcc_opt) -g3
|
||||
# speed for the unit tests is all over the place either way so it's
|
||||
# difficult to say whether -Os gives any speed benefit over -Oz.
|
||||
#
|
||||
# (Much later: -O2 consistently gives the best speeds.)
|
||||
# Much practice has demonstrated that -O2 consistently gives the best
|
||||
# runtime speeds, but not by a large enough factor to rule out use of
|
||||
# -Oz when small deliverable size is a priority.
|
||||
########################################################################
|
||||
|
||||
|
||||
$(sqlite3.c) $(sqlite3.h):
|
||||
$(MAKE) -C $(dir.top) sqlite3.c
|
||||
|
||||
.PHONY: clean distclean
|
||||
clean:
|
||||
-rm -f $(CLEAN_FILES)
|
||||
distclean: clean
|
||||
-rm -f $(DISTCLEAN_FILES)
|
||||
|
||||
ifeq (release,$(filter release,$(MAKECMDGOALS)))
|
||||
ifeq (,$(wasm-strip))
|
||||
$(error Cannot make release-quality binary because wasm-strip is not available. \
|
||||
See notes in the warning above)
|
||||
endif
|
||||
else
|
||||
$(info Development build. Use '$(MAKE) release' for a smaller release build.)
|
||||
endif
|
||||
|
||||
bin.version-info := $(dir.wasm)/version-info
|
||||
# ^^^^ NOT in $(dir.tmp) because we need it to survive the cleanup
|
||||
# process for the dist build to work properly.
|
||||
$(bin.version-info): $(dir.wasm)/version-info.c $(sqlite3.h) $(MAKEFILE)
|
||||
$(CC) -O0 -I$(dir.top) -o $@ $<
|
||||
DISTCLEAN_FILES += $(bin.version-info)
|
||||
|
||||
bin.stripccomments := $(dir.tool)/stripccomments
|
||||
$(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE)
|
||||
$(CC) -o $@ $<
|
||||
DISTCLEAN_FILES += $(bin.stripccomments)
|
||||
|
||||
# EXPORTED_FUNCTIONS.* = files for use with Emscripten's
|
||||
# -sEXPORTED_FUNCTION flag.
|
||||
EXPORTED_FUNCTIONS.api.in := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api)
|
||||
EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
|
||||
$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE)
|
||||
cat $(EXPORTED_FUNCTIONS.api.in) > $@
|
||||
cp $(EXPORTED_FUNCTIONS.api.in) $@
|
||||
|
||||
# sqlite3-license-version.js = generated JS file with the license
|
||||
# header and version info.
|
||||
sqlite3-license-version.js := $(dir.tmp)/sqlite3-license-version.js
|
||||
# sqlite3-license-version-header.js = JS file containing only the
|
||||
# license header.
|
||||
sqlite3-license-version-header.js := $(dir.api)/sqlite3-license-version-header.js
|
||||
# sqlite3-api-build-version.js = generated JS file which populates the
|
||||
# sqlite3.version object using $(bin.version-info).
|
||||
sqlite3-api-build-version.js := $(dir.tmp)/sqlite3-api-build-version.js
|
||||
# sqlite3-api.jses = the list of JS files which make up $(sqlite3-api.js), in
|
||||
# the order they need to be assembled.
|
||||
@@ -225,17 +297,22 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
|
||||
sqlite3-worker1.js := $(dir.api)/sqlite3-worker1.js
|
||||
sqlite3-worker1-promiser.js := $(dir.api)/sqlite3-worker1-promiser.js
|
||||
define CP_XAPI
|
||||
# COPY_XAPI = a $(call)able function to copy $1 to $(dir.dout), where
|
||||
# $1 must be one of the "external" JS API files.
|
||||
define COPY_XAPI
|
||||
sqlite3-api.ext.jses += $$(dir.dout)/$$(notdir $(1))
|
||||
$$(dir.dout)/$$(notdir $(1)): $(1) $$(MAKEFILE)
|
||||
cp $$< $$@
|
||||
endef
|
||||
$(foreach X,$(SOAP.js) $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js),\
|
||||
$(eval $(call CP_XAPI,$(X))))
|
||||
$(eval $(call COPY_XAPI,$(X))))
|
||||
all: $(sqlite3-api.ext.jses)
|
||||
|
||||
sqlite3-api.js := $(dir.tmp)/sqlite3-api.js
|
||||
$(sqlite3-api.js): $(sqlite3-api.jses) $(MAKEFILE)
|
||||
# sqlite3-api.js.in = the generated sqlite3-api.js before it gets
|
||||
# preprocessed. It contains all of $(sqlite3-api.jses) but none of the
|
||||
# Emscripten-specific headers and footers.
|
||||
sqlite3-api.js.in := $(dir.tmp)/sqlite3-api.c-pp.js
|
||||
$(sqlite3-api.js.in): $(sqlite3-api.jses) $(MAKEFILE)
|
||||
@echo "Making $@..."
|
||||
@for i in $(sqlite3-api.jses); do \
|
||||
echo "/* BEGIN FILE: $$i */"; \
|
||||
@@ -252,32 +329,8 @@ $(sqlite3-api-build-version.js): $(bin.version-info) $(MAKEFILE)
|
||||
echo ';'; \
|
||||
echo '});'; \
|
||||
} > $@
|
||||
|
||||
########################################################################
|
||||
# --post-js and --pre-js are emcc flags we use to append/prepend JS to
|
||||
# the generated emscripten module file.
|
||||
pre-js.js := $(dir.api)/pre-js.js
|
||||
post-js.js := $(dir.tmp)/post-js.js
|
||||
post-jses := \
|
||||
$(dir.api)/post-js-header.js \
|
||||
$(sqlite3-api.js) \
|
||||
$(dir.api)/post-js-footer.js
|
||||
$(post-js.js): $(post-jses) $(MAKEFILE)
|
||||
@echo "Making $@..."
|
||||
@for i in $(post-jses); do \
|
||||
echo "/* BEGIN FILE: $$i */"; \
|
||||
cat $$i; \
|
||||
echo "/* END FILE: $$i */"; \
|
||||
done > $@
|
||||
extern-post-js.js := $(dir.api)/extern-post-js.js
|
||||
extern-pre-js.js := $(dir.api)/extern-pre-js.js
|
||||
pre-post-common.flags := \
|
||||
--post-js=$(post-js.js) \
|
||||
--extern-post-js=$(extern-post-js.js) \
|
||||
--extern-pre-js=$(sqlite3-license-version.js)
|
||||
pre-post-jses.deps := $(post-js.js) \
|
||||
$(extern-post-js.js) $(extern-pre-js.js) $(sqlite3-license-version.js)
|
||||
$(sqlite3-license-version.js): $(sqlite3.h) $(sqlite3-license-version-header.js) $(MAKEFILE)
|
||||
$(sqlite3-license-version.js): $(sqlite3.h) $(sqlite3-license-version-header.js) \
|
||||
$(MAKEFILE)
|
||||
@echo "Making $@..."; { \
|
||||
cat $(sqlite3-license-version-header.js); \
|
||||
echo '/*'; \
|
||||
@@ -289,43 +342,109 @@ $(sqlite3-license-version.js): $(sqlite3.h) $(sqlite3-license-version-header.js)
|
||||
} > $@
|
||||
|
||||
########################################################################
|
||||
# call-make-pre-js creates rules for pre-js-$(1).js. $1 = the base
|
||||
# name of the JS file on whose behalf this pre-js is for.
|
||||
# --post-js and --pre-js are emcc flags we use to append/prepend JS to
|
||||
# the generated emscripten module file. The following rules generate
|
||||
# various versions of those files for the vanilla and ESM builds.
|
||||
pre-js.js.in := $(dir.api)/pre-js.js
|
||||
pre-js.js.esm := $(dir.tmp)/pre-js.esm.js
|
||||
pre-js.js.vanilla := $(dir.tmp)/pre-js.vanilla.js
|
||||
$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.vanilla),$(c-pp.D.vanilla)))
|
||||
$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.esm),$(c-pp.D.esm)))
|
||||
post-js.js.in := $(dir.tmp)/post-js.js
|
||||
post-js.js.vanilla := $(dir.tmp)/post-js.vanilla.js
|
||||
post-js.js.esm := $(dir.tmp)/post-js.esm.js
|
||||
post-jses.js := \
|
||||
$(dir.api)/post-js-header.js \
|
||||
$(sqlite3-api.js.in) \
|
||||
$(dir.api)/post-js-footer.js
|
||||
$(post-js.js.in): $(post-jses.js) $(MAKEFILE)
|
||||
@echo "Making $@..."
|
||||
@for i in $(post-jses.js); do \
|
||||
echo "/* BEGIN FILE: $$i */"; \
|
||||
cat $$i; \
|
||||
echo "/* END FILE: $$i */"; \
|
||||
done > $@
|
||||
$(eval $(call C-PP.FILTER,$(post-js.js.in),$(post-js.js.vanilla),$(c-pp.D.vanilla)))
|
||||
$(eval $(call C-PP.FILTER,$(post-js.js.in),$(post-js.js.esm),$(c-pp.D.esm)))
|
||||
|
||||
# extern-post-js* and extern-pre-js* are files for use with
|
||||
# Emscripten's --extern-pre-js and --extern-post-js flags. These
|
||||
# rules make different copies for the vanilla and ESM builds.
|
||||
extern-post-js.js.in := $(dir.api)/extern-post-js.js
|
||||
extern-post-js.js.vanilla := $(dir.tmp)/extern-post-js.vanilla.js
|
||||
extern-post-js.js.esm := $(dir.tmp)/extern-post-js.esm.js
|
||||
$(eval $(call C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.vanilla),$(c-pp.D.vanilla)))
|
||||
$(eval $(call C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.esm),$(c-pp.D.esm)))
|
||||
extern-pre-js.js := $(dir.api)/extern-pre-js.js
|
||||
|
||||
# Emscripten flags for --[extern-][pre|post]-js=... for the
|
||||
# various builds.
|
||||
pre-post-common.flags := \
|
||||
--extern-pre-js=$(sqlite3-license-version.js)
|
||||
pre-post-common.flags.vanilla := \
|
||||
$(pre-post-common.flags) \
|
||||
--post-js=$(post-js.js.vanilla) \
|
||||
--extern-post-js=$(extern-post-js.js.vanilla)
|
||||
pre-post-common.flags.esm := \
|
||||
$(pre-post-common.flags) \
|
||||
--post-js=$(post-js.js.esm) \
|
||||
--extern-post-js=$(extern-post-js.js.esm)
|
||||
|
||||
# pre-post-jses.deps.* = a list of dependencies for the
|
||||
# --[extern-][pre/post]-js files.
|
||||
pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)
|
||||
pre-post-jses.deps.vanilla := $(pre-post-jses.deps.common) \
|
||||
$(post-js.js.vanilla) $(extern-post-js.js.vanilla)
|
||||
pre-post-jses.deps.esm := $(pre-post-jses.deps.common) \
|
||||
$(post-js.js.esm) $(extern-post-js.js.esm)
|
||||
|
||||
########################################################################
|
||||
# call-make-pre-js is a $(call)able which creates rules for
|
||||
# pre-js-$(1).js. $1 = the base name of the JS file on whose behalf
|
||||
# this pre-js is for. $2 is the build mode: one of (vanilla, esm).
|
||||
# This sets up --[extern-][pre/post]-js flags in
|
||||
# $(pre-post-$(1).flags.$(2)) and dependencies in
|
||||
# $(pre-post-$(1).deps.$(2)).
|
||||
define call-make-pre-js
|
||||
pre-post-$(1).flags ?=
|
||||
$$(dir.tmp)/pre-js-$(1).js: $$(pre-js.js) $$(MAKEFILE)
|
||||
cp $$(pre-js.js) $$@
|
||||
pre-post-$(1).flags.$(2) ?=
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(2)) $$(MAKEFILE)
|
||||
cp $$(pre-js.js.$(2)) $$@
|
||||
@if [ sqlite3-wasmfs = $(1) ]; then \
|
||||
echo "delete Module[xNameOfInstantiateWasm] /*for WASMFS build*/;"; \
|
||||
elif [ sqlite3 != $(1) ]; then \
|
||||
echo "Module[xNameOfInstantiateWasm].uri = '$(1).wasm';"; \
|
||||
fi >> $$@
|
||||
pre-post-$(1).deps := $$(pre-post-jses.deps) $$(dir.tmp)/pre-js-$(1).js
|
||||
pre-post-$(1).flags += --pre-js=$$(dir.tmp)/pre-js-$(1).js
|
||||
pre-post-$(1).deps.$(2) := \
|
||||
$$(pre-post-jses.deps.$(2)) \
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
pre-post-$(1).flags.$(2) += \
|
||||
$$(pre-post-common.flags.$(2)) \
|
||||
--pre-js=$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
endef
|
||||
#$(error $(call call-make-pre-js,sqlite3-wasmfs))
|
||||
# /post-js and pre-js
|
||||
########################################################################
|
||||
|
||||
########################################################################
|
||||
# emcc flags for .c/.o/.wasm/.js.
|
||||
emcc.flags :=
|
||||
#emcc.flags += -v # _very_ loud but also informative about what it's doing
|
||||
# -g3 is needed to keep -O2 and higher from creating broken JS via
|
||||
# minification.
|
||||
ifeq (1,$(emcc.verbose))
|
||||
emcc.flags += -v
|
||||
# -v is _very_ loud but also informative about what it's doing
|
||||
endif
|
||||
|
||||
########################################################################
|
||||
# emcc flags for .c/.o.
|
||||
emcc.cflags :=
|
||||
emcc.cflags += -std=c99 -fPIC
|
||||
# -------------^^^^^^^^ we currently need c99 for WASM-specific sqlite3 APIs.
|
||||
# -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c).
|
||||
emcc.cflags += -I. -I$(dir.top)
|
||||
|
||||
########################################################################
|
||||
# emcc flags specific to building the final .js/.wasm file...
|
||||
# emcc flags specific to building .js/.wasm files...
|
||||
emcc.jsflags := -fPIC
|
||||
emcc.jsflags += --minify 0
|
||||
emcc.jsflags += --no-entry
|
||||
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
|
||||
emcc.jsflags += -sMODULARIZE
|
||||
emcc.jsflags += -sSTRICT_JS
|
||||
emcc.jsflags += -sDYNAMIC_EXECUTION=0
|
||||
@@ -333,7 +452,9 @@ emcc.jsflags += -sNO_POLYFILL
|
||||
emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api)
|
||||
emcc.exportedRuntimeMethods := \
|
||||
-sEXPORTED_RUNTIME_METHODS=FS,wasmMemory
|
||||
# FS ==> stdio/POSIX I/O proxies
|
||||
# FS ==> stdio/POSIX I/O proxies. Currently used explicitly only
|
||||
# by the fiddle app, and it must never be exposed to client code
|
||||
# via our APIs.
|
||||
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
|
||||
emcc.jsflags += $(emcc.exportedRuntimeMethods)
|
||||
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
|
||||
@@ -367,32 +488,44 @@ emcc.jsflags += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
|
||||
|
||||
emcc.jsflags += $(emcc.environment)
|
||||
#emcc.jsflags += -sTOTAL_STACK=4194304
|
||||
|
||||
########################################################################
|
||||
# $(sqlite3.js.init-func) is the name Emscripten assigns our exported
|
||||
# module init/load function. This symbol name is hard-coded in
|
||||
# $(extern-post-js.js) as well as in numerous docs.
|
||||
#
|
||||
# "sqlite3InitModule" is the symbol we document for client use, so
|
||||
# that's the symbol name which must be exported, whether it comes from
|
||||
# Emscripten or our own code in extern-post-js.js.
|
||||
#
|
||||
# That said... we can change $(sqlite3.js.init-func) as long as the
|
||||
# name "sqlite3InitModule" is the one which gets exposed via the
|
||||
# resulting JS files. That can be accomplished via
|
||||
# extern-post-js.js. However... using a temporary symbol name here
|
||||
# and then adding sqlite3InitModule() ourselves results in 2 global
|
||||
# symbols: we cannot "delete" the Emscripten-defined
|
||||
# $(sqlite3.js.init-func) because it's declared with "var".
|
||||
sqlite3.js.init-func := sqlite3InitModule
|
||||
# ^^^^ $(sqlite3.js.init-func) symbol name is hard-coded in
|
||||
# $(extern-post-js.js) as well as in numerous docs. If changed, it
|
||||
# needs to be globally modified in *.js and all related documentation.
|
||||
|
||||
emcc.jsflags += -sEXPORT_NAME=$(sqlite3.js.init-func)
|
||||
emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr.
|
||||
#emcc.jsflags += -sSTRICT # fails due to missing __syscall_...()
|
||||
#emcc.jsflags += -sALLOW_UNIMPLEMENTED_SYSCALLS
|
||||
#emcc.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API
|
||||
#emcc.jsflags += -sABORTING_MALLOC
|
||||
#emcc.jsflags += -sABORTING_MALLOC # only for experimentation
|
||||
emcc.jsflags += -sALLOW_TABLE_GROWTH
|
||||
# -sALLOW_TABLE_GROWTH is required for installing new SQL UDFs
|
||||
# ^^^^ -sALLOW_TABLE_GROWTH is required for installing new SQL UDFs
|
||||
emcc.jsflags += -Wno-limited-postlink-optimizations
|
||||
# ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag.
|
||||
#emcc.jsflags += -sSTANDALONE_WASM # causes OOM errors, not sure why
|
||||
# https://lld.llvm.org/WebAssembly.html
|
||||
emcc.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0
|
||||
# ^^^^ emcc likes to warn when we have "limited optimizations" via the
|
||||
# -g3 flag.
|
||||
# emcc.jsflags += -sSTANDALONE_WASM # causes OOM errors, not sure why.
|
||||
|
||||
# Re. undefined symbol handling, see: https://lld.llvm.org/WebAssembly.html
|
||||
emcc.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=1
|
||||
emcc.jsflags += -sLLD_REPORT_UNDEFINED
|
||||
#emcc.jsflags += --allow-undefined
|
||||
#emcc.jsflags += --import-undefined
|
||||
#emcc.jsflags += --unresolved-symbols=import-dynamic --experimental-pic
|
||||
#emcc.jsflags += --experimental-pic --unresolved-symbols=ingore-all --import-undefined
|
||||
#emcc.jsflags += --unresolved-symbols=ignore-all
|
||||
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
|
||||
|
||||
########################################################################
|
||||
# -sMEMORY64=1 fails to load, erroring with:
|
||||
@@ -404,21 +537,28 @@ emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
|
||||
# new Uint8Array(wasm.heap8u().buffer, ptr, n)
|
||||
#
|
||||
# because ptr is now a BigInt, so is invalid for passing to arguments
|
||||
# which have strict must-be-a-Number requirements.
|
||||
# which have strict must-be-a-Number requirements. That aspect will
|
||||
# make any eventual port to 64-bit address space extremely painful, as
|
||||
# such constructs are found all over the place in the source code.
|
||||
########################################################################
|
||||
|
||||
|
||||
########################################################################
|
||||
# -sSINGLE_FILE:
|
||||
# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js#L1704
|
||||
# -sSINGLE_FILE=1 would be really nice but we have to build with -g3
|
||||
# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js
|
||||
#
|
||||
# -sSINGLE_FILE=1 would be _really_ nice but we have to build with -g3
|
||||
# for -O2 and higher to work (else minification breaks the code) and
|
||||
# cannot wasm-strip the binary before it gets encoded into the JS
|
||||
# file. The result is that the generated JS file is, because of the -g3
|
||||
# debugging info, _huge_.
|
||||
# file. The result is that the generated JS file is, because of the
|
||||
# -g3 debugging info, _huge_.
|
||||
########################################################################
|
||||
|
||||
sqlite3.js := $(dir.dout)/sqlite3.js
|
||||
sqlite3.mjs := $(dir.dout)/sqlite3.mjs
|
||||
# Undocumented Emscripten feature: if the target file extension is
|
||||
# "mjs", it defaults to ES6 module builds:
|
||||
# https://github.com/emscripten-core/emscripten/issues/14383
|
||||
sqlite3.wasm := $(dir.dout)/sqlite3.wasm
|
||||
sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
|
||||
# sqlite3-wasm.o vs sqlite3-wasm.c: building against the latter
|
||||
@@ -426,27 +566,71 @@ sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
|
||||
# enough to the target speed requirements that the 500ms makes a
|
||||
# difference. Thus we build all binaries against sqlite3-wasm.c
|
||||
# instead of building a shared copy of sqlite3-wasm.o.
|
||||
$(eval $(call call-make-pre-js,sqlite3))
|
||||
$(sqlite3.js):
|
||||
$(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \
|
||||
$(EXPORTED_FUNCTIONS.api) \
|
||||
$(pre-post-sqlite3.deps)
|
||||
$(eval $(call call-make-pre-js,sqlite3,vanilla))
|
||||
$(eval $(call call-make-pre-js,sqlite3,esm))
|
||||
$(sqlite3.js) $(sqlite3.mjs): $(MAKEFILE) $(sqlite3.wasm.obj) \
|
||||
$(EXPORTED_FUNCTIONS.api)
|
||||
$(sqlite3.js): $(pre-post-sqlite3.deps.vanilla)
|
||||
$(sqlite3.mjs): $(pre-post-sqlite3.deps.esm)
|
||||
########################################################################
|
||||
# SQLITE3.xJS.RECIPE = the $(call)able recipe body for $(sqlite3.js)
|
||||
# and $(sqlite3.mjs). $1 = one of (vanilla, esm).
|
||||
#
|
||||
# Reminder for ESM builds: even if we use -sEXPORT_ES6=0, emcc _still_
|
||||
# adds:
|
||||
#
|
||||
# export default $(sqlite3.js.init-func);
|
||||
#
|
||||
# when building *.mjs, which is bad because we need to export an
|
||||
# overwritten version of that function and cannot "export default"
|
||||
# twice. Because of this, we have to sed $(sqlite3.mjs) to remove the
|
||||
# _first_ instance (only) of /^export default/.
|
||||
#
|
||||
# Upstream RFE:
|
||||
# https://github.com/emscripten-core/emscripten/issues/18237
|
||||
########################################################################
|
||||
define SQLITE3.xJS.RECIPE
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) -o $@ $(emcc_opt_full) $(emcc.flags) \
|
||||
$(emcc.jsflags) $(pre-post-common.flags) $(pre-post-sqlite3.flags) \
|
||||
$(emcc.jsflags) \
|
||||
$(pre-post-sqlite3.flags.$(1)) $(emcc.flags.sqlite3.$(1)) \
|
||||
$(cflags.common) $(SQLITE_OPT) $(sqlite3-wasm.c)
|
||||
@if [ esm = $(1) ]; then \
|
||||
echo "Fragile workaround for an Emscripten annoyance. See emcc.flags.sqlite3.esm."; \
|
||||
sed -i -e '0,/^export default/{/^export default/d}' $@ || exit $$?; \
|
||||
if ! grep -q '^export default' $@; then \
|
||||
echo "Cannot find export default." 1>&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi
|
||||
chmod -x $(sqlite3.wasm)
|
||||
$(maybe-wasm-strip) $(sqlite3.wasm)
|
||||
@ls -la $@ $(sqlite3.wasm)
|
||||
endef
|
||||
emcc.flags.sqlite3.vanilla :=
|
||||
emcc.flags.sqlite3.esm := -sEXPORT_ES6 -sUSE_ES6_IMPORT_META
|
||||
$(sqlite3.js):
|
||||
$(call SQLITE3.xJS.RECIPE,vanilla)
|
||||
$(sqlite3.mjs):
|
||||
$(call SQLITE3.xJS.RECIPE,esm)
|
||||
########################################################################
|
||||
# We have to ensure that we do not build both $(sqlite3.js) and
|
||||
# $(sqlite3.mjs) in parallel because both result in the creation of
|
||||
# $(sqlite3.wasm). We have no(?) way to build just the .mjs file
|
||||
# without also building the .wasm file. i.e. we're building
|
||||
# $(sqlite3.wasm) twice, but that's apparently unavoidable (and
|
||||
# harmless, just a waste of build time).
|
||||
$(sqlite3.wasm): $(sqlite3.js)
|
||||
CLEAN_FILES += $(sqlite3.js) $(sqlite3.wasm)
|
||||
all: $(sqlite3.js)
|
||||
wasm: $(sqlite3.js)
|
||||
# End main Emscripten-based module build
|
||||
$(sqlite3.mjs): $(sqlite3.js)
|
||||
CLEAN_FILES += $(sqlite3.js) $(sqlite3.mjs) $(sqlite3.wasm)
|
||||
all: $(sqlite3.mjs)
|
||||
wasm: $(sqlite3.mjs)
|
||||
# End main $(sqlite3.js) build
|
||||
########################################################################
|
||||
|
||||
########################################################################
|
||||
# batch-runner.js...
|
||||
# batch-runner.js is part of one of the test apps which reads in SQL
|
||||
# dumps generated by $(speedtest1) and executes them.
|
||||
dir.sql := sql
|
||||
speedtest1 := ../../speedtest1
|
||||
speedtest1.c := ../../test/speedtest1.c
|
||||
@@ -468,30 +652,30 @@ batch: batch-runner.list
|
||||
all: batch
|
||||
# end batch-runner.js
|
||||
########################################################################
|
||||
# speedtest1.js...
|
||||
# speedtest1-common.eflags = emcc flags used by multiple builds of speedtest1
|
||||
# Wasmified speedtest1 is our primary benchmarking tool.
|
||||
#
|
||||
# speedtest1.eflags.common = emcc flags used by multiple builds of speedtest1
|
||||
# speedtest1.eflags = emcc flags used by main build of speedtest1
|
||||
speedtest1-common.eflags := $(emcc_opt_full)
|
||||
speedtest1.eflags.common := $(emcc_opt_full)
|
||||
speedtest1.eflags :=
|
||||
speedtest1.eflags += -sENVIRONMENT=web
|
||||
speedtest1.eflags += -sALLOW_MEMORY_GROWTH
|
||||
speedtest1.eflags += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
|
||||
speedtest1-common.eflags += -sINVOKE_RUN=0
|
||||
speedtest1-common.eflags += --no-entry
|
||||
#speedtest1-common.eflags += -flto
|
||||
speedtest1-common.eflags += -sABORTING_MALLOC
|
||||
speedtest1-common.eflags += -sSTRICT_JS
|
||||
speedtest1-common.eflags += -sMODULARIZE
|
||||
speedtest1-common.eflags += -Wno-limited-postlink-optimizations
|
||||
speedtest1.eflags.common += -sINVOKE_RUN=0
|
||||
speedtest1.eflags.common += --no-entry
|
||||
#speedtest1.eflags.common += -flto
|
||||
speedtest1.eflags.common += -sABORTING_MALLOC
|
||||
speedtest1.eflags.common += -sSTRICT_JS
|
||||
speedtest1.eflags.common += -sMODULARIZE
|
||||
speedtest1.eflags.common += -Wno-limited-postlink-optimizations
|
||||
EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1)
|
||||
speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
speedtest1-common.eflags += $(emcc.exportedRuntimeMethods)
|
||||
speedtest1-common.eflags += -sALLOW_TABLE_GROWTH
|
||||
speedtest1-common.eflags += -sDYNAMIC_EXECUTION=0
|
||||
speedtest1-common.eflags += --minify 0
|
||||
speedtest1-common.eflags += -sEXPORT_NAME=$(sqlite3.js.init-func)
|
||||
speedtest1-common.eflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
|
||||
speedtest1-common.eflags += $(pre-post-common.flags)
|
||||
speedtest1.eflags.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
speedtest1.eflags.common += $(emcc.exportedRuntimeMethods)
|
||||
speedtest1.eflags.common += -sALLOW_TABLE_GROWTH
|
||||
speedtest1.eflags.common += -sDYNAMIC_EXECUTION=0
|
||||
speedtest1.eflags.common += --minify 0
|
||||
speedtest1.eflags.common += -sEXPORT_NAME=$(sqlite3.js.init-func)
|
||||
speedtest1.eflags.common += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
|
||||
speedtest1.exit-runtime0 := -sEXIT_RUNTIME=0
|
||||
speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1
|
||||
# Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get
|
||||
@@ -515,17 +699,17 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api)
|
||||
@echo "Making $@ ..."
|
||||
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api); } > $@
|
||||
speedtest1.js := $(dir.dout)/speedtest1.js
|
||||
speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js))
|
||||
speedtest1.wasm := $(dir.dout)/speedtest1.wasm
|
||||
speedtest1.cflags := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
speedtest1.cses := $(speedtest1.c) $(sqlite3-wasm.c)
|
||||
$(eval $(call call-make-pre-js,speedtest1))
|
||||
$(eval $(call call-make-pre-js,speedtest1,vanilla))
|
||||
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cses) \
|
||||
$(pre-post-speedtest1.deps) \
|
||||
$(pre-post-speedtest1.deps.vanilla) \
|
||||
$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) \
|
||||
$(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \
|
||||
$(pre-post-speedtest1.flags) \
|
||||
$(speedtest1.eflags) $(speedtest1.eflags.common) \
|
||||
$(speedtest1.cflags) $(pre-post-speedtest1.flags.vanilla) \
|
||||
$(SQLITE_OPT) \
|
||||
$(speedtest1.exit-runtime0) \
|
||||
-o $@ $(speedtest1.cses) -lm
|
||||
@@ -538,6 +722,29 @@ CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
|
||||
# end speedtest1.js
|
||||
########################################################################
|
||||
|
||||
########################################################################
|
||||
# tester1 is the main unit and regression test application and needs
|
||||
# to be able to run in 4 separate modes to cover the primary
|
||||
# client-side use cases:
|
||||
#
|
||||
# 1) Load sqlite3 in the main UI thread of a conventional script.
|
||||
# 2) Load sqlite3 in a conventional Worker thread.
|
||||
# 3) Load sqlite3 as an ES6 module (ESM) in the main thread.
|
||||
# 4) Load sqlite3 as an ESM worker. (Not all browsers support this.)
|
||||
#
|
||||
# To that end, we require two separate builds of tester1.js:
|
||||
#
|
||||
# tester1.js: cases 1 and 2
|
||||
# tester1.mjs: cases 3 and 4
|
||||
#
|
||||
# To create those, we filter tester1.c-pp.js with $(bin.c-pp)...
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.js))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.esm)))
|
||||
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
|
||||
all: tester1
|
||||
|
||||
########################################################################
|
||||
# Convenience rules to rebuild with various -Ox levels. Much
|
||||
# experimentation shows -O2 to be the clear winner in terms of speed.
|
||||
@@ -547,8 +754,8 @@ CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
|
||||
.PHONY: o0 o1 o2 o3 os oz
|
||||
o-xtra := -flto
|
||||
# ^^^^ -flto can have a considerably performance boost at -O0 but
|
||||
# doubles the build time and seems to have negligible effect on
|
||||
# higher optimization levels.
|
||||
# doubles the build time and seems to have negligible, if any, effect
|
||||
# on higher optimization levels.
|
||||
o0: clean
|
||||
$(MAKE) -e "emcc_opt=-O0"
|
||||
o1: clean
|
||||
@@ -566,16 +773,21 @@ oz: clean
|
||||
########################################################################
|
||||
# Sub-makes...
|
||||
|
||||
# sqlite.org/fiddle application...
|
||||
include fiddle.make
|
||||
|
||||
# Only add wasmfs if wasmfs.enable=1 or we're running (dist)clean
|
||||
ifneq (,$(filter wasmfs,$(MAKECMDGOALS)))
|
||||
wasmfs.enable ?= 1
|
||||
else
|
||||
wasmfs.enable ?= $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
|
||||
endif
|
||||
ifeq (1,$(wasmfs.enable))
|
||||
# wasmfs build disabled 2022-10-19 per /chat discussion.
|
||||
# OPFS-over-wasmfs was initially a stopgap measure and a convenient
|
||||
# point of comparison for the OPFS sqlite3_vfs's performance, but it
|
||||
# currently doubles our deliverables and build maintenance burden for
|
||||
# little, if any, benefit.
|
||||
# little benefit.
|
||||
#
|
||||
########################################################################
|
||||
# Some platforms do not support the WASMFS build. Raspberry Pi OS is one
|
||||
@@ -595,15 +807,16 @@ endif
|
||||
########################################################################
|
||||
|
||||
########################################################################
|
||||
# Create deliverables:
|
||||
ifneq (,$(filter dist,$(MAKECMDGOALS)))
|
||||
# Create main client downloadable zip file:
|
||||
ifneq (,$(filter dist snapshot,$(MAKECMDGOALS)))
|
||||
include dist.make
|
||||
endif
|
||||
|
||||
########################################################################
|
||||
# Push files to public wasm-testing.sqlite.org server
|
||||
wasm-testing.include = $(dir.dout) *.js *.html \
|
||||
batch-runner.list $(dir.sql) $(dir.common) $(dir.fiddle) $(dir.jacc)
|
||||
wasm-testing.include = *.js *.html batch-runner.list \
|
||||
./tests \
|
||||
$(dir.dout) $(dir.sql) $(dir.common) $(dir.fiddle) $(dir.jacc)
|
||||
wasm-testing.exclude = sql/speedtest1.sql
|
||||
wasm-testing.dir = /jail/sites/wasm-testing
|
||||
wasm-testing.dest ?= wasm-testing:$(wasm-testing.dir)
|
||||
|
||||
@@ -7,6 +7,7 @@ _sqlite3_bind_null
|
||||
_sqlite3_bind_parameter_count
|
||||
_sqlite3_bind_parameter_index
|
||||
_sqlite3_bind_text
|
||||
_sqlite3_busy_timeout
|
||||
_sqlite3_changes
|
||||
_sqlite3_changes64
|
||||
_sqlite3_clear_bindings
|
||||
|
||||
@@ -131,3 +131,11 @@ into the build-generated `sqlite3.js` along with `sqlite3-api.js`.
|
||||
`sqlite3InitModule()` function with one which, after the module is
|
||||
loaded, also initializes the asynchronous parts of the sqlite3
|
||||
module. For example, the OPFS VFS support.
|
||||
|
||||
## Preprocessing of Source Files
|
||||
|
||||
Certain files in the build require preprocessing to filter in/out
|
||||
parts which differ between vanilla JS builds and ES6 Module
|
||||
(a.k.a. esm) builds. The preprocessor itself is in
|
||||
[](/file/ext/wasm/c-pp.c) and the associates flags and rules are in
|
||||
[](/file/ext/wasm/GNUmakefile).
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
|
||||
/* ^^^^ ACHTUNG: blank line at the start is necessary because
|
||||
Emscripten will not add a newline in some cases and we need
|
||||
a blank line for a sed-based kludge for the ES6 build. */
|
||||
/* extern-post-js.js must be appended to the resulting sqlite3.js
|
||||
file. It gets its name from being used as the value for the
|
||||
--extern-post-js=... Emscripten flag. Note that this code, unlike
|
||||
most of the associated JS code, runs outside of the
|
||||
Emscripten-generated module init scope, in the current
|
||||
global scope. */
|
||||
//#if target=es6-module
|
||||
const toExportForES6 =
|
||||
//#endif
|
||||
(function(){
|
||||
/**
|
||||
In order to hide the sqlite3InitModule()'s resulting Emscripten
|
||||
module from downstream clients (and simplify our documentation by
|
||||
being able to elide those details), we rewrite
|
||||
sqlite3InitModule() to return the sqlite3 object.
|
||||
In order to hide the sqlite3InitModule()'s resulting
|
||||
Emscripten module from downstream clients (and simplify our
|
||||
documentation by being able to elide those details), we hide that
|
||||
function and expose a hand-written sqlite3InitModule() to return
|
||||
the sqlite3 object (most of the time).
|
||||
|
||||
Unfortunately, we cannot modify the module-loader/exporter-based
|
||||
impls which Emscripten installs at some point in the file above
|
||||
this.
|
||||
*/
|
||||
const originalInit = self.sqlite3InitModule;
|
||||
const originalInit =
|
||||
/* Maintenance reminder: DO NOT use `self.` here. It's correct
|
||||
for non-ES6 Module cases but wrong for ES6 modules because those
|
||||
resolve this symbol differently. */ sqlite3InitModule;
|
||||
if(!originalInit){
|
||||
throw new Error("Expecting self.sqlite3InitModule to be defined by the Emscripten build.");
|
||||
}
|
||||
@@ -94,10 +105,15 @@
|
||||
}
|
||||
/* Replace the various module exports performed by the Emscripten
|
||||
glue... */
|
||||
if (typeof exports === 'object' && typeof module === 'object')
|
||||
if (typeof exports === 'object' && typeof module === 'object'){
|
||||
module.exports = sqlite3InitModule;
|
||||
else if (typeof exports === 'object')
|
||||
}else if (typeof exports === 'object'){
|
||||
exports["sqlite3InitModule"] = sqlite3InitModule;
|
||||
}
|
||||
/* AMD modules get injected in a way we cannot override,
|
||||
so we can't handle those here. */
|
||||
return self.sqlite3InitModule /* required for ESM */;
|
||||
})();
|
||||
//#if target=es6-module
|
||||
export default toExportForES6;
|
||||
//#endif
|
||||
|
||||
@@ -29,6 +29,10 @@ sqlite3InitModuleState.debugModule('self.location =',self.location);
|
||||
4) If none of the above apply, (prefix+path) is returned.
|
||||
*/
|
||||
Module['locateFile'] = function(path, prefix) {
|
||||
//#if target=es6-module
|
||||
return new URL(path, import.meta.url).href;
|
||||
//#else
|
||||
'use strict';
|
||||
let theFile;
|
||||
const up = this.urlParams;
|
||||
if(up.has(path)){
|
||||
@@ -47,6 +51,7 @@ Module['locateFile'] = function(path, prefix) {
|
||||
"result =", theFile
|
||||
);
|
||||
return theFile;
|
||||
//#endif /* SQLITE_JS_EMS */
|
||||
}.bind(sqlite3InitModuleState);
|
||||
|
||||
/**
|
||||
@@ -65,7 +70,7 @@ Module[xNameOfInstantiateWasm] = function callee(imports,onSuccess){
|
||||
const uri = Module.locateFile(
|
||||
callee.uri, (
|
||||
('undefined'===typeof scriptDirectory/*var defined by Emscripten glue*/)
|
||||
? '' : scriptDirectory)
|
||||
? "" : scriptDirectory)
|
||||
);
|
||||
sqlite3InitModuleState.debugModule(
|
||||
"instantiateWasm() uri =", uri
|
||||
|
||||
@@ -141,7 +141,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const __dbArgcMismatch = (pDb,f,n)=>{
|
||||
return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
|
||||
f+"() requires "+n+" argument"+
|
||||
(1===n?'':'s')+".");
|
||||
(1===n?"":'s')+".");
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -158,7 +158,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
n = v.byteLength;
|
||||
v = util.typedArrayToString(v);
|
||||
}else if(Array.isArray(v)){
|
||||
v = v.join('');
|
||||
v = v.join("");
|
||||
n = -1;
|
||||
}
|
||||
return [v, n];
|
||||
|
||||
@@ -55,12 +55,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
if(sqliteResultCode){
|
||||
if(dbPtr instanceof DB) dbPtr = dbPtr.pointer;
|
||||
toss3(
|
||||
"sqlite result code",sqliteResultCode+":",
|
||||
"sqlite3 result code",sqliteResultCode+":",
|
||||
(dbPtr
|
||||
? capi.sqlite3_errmsg(dbPtr)
|
||||
: capi.sqlite3_errstr(sqliteResultCode))
|
||||
);
|
||||
}
|
||||
return arguments[0];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -200,9 +201,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
*/
|
||||
dbCtorHelper.normalizeArgs = function(filename=':memory:',flags = 'c',vfs = null){
|
||||
const arg = {};
|
||||
if(1===arguments.length && 'object'===typeof arguments[0]){
|
||||
const x = arguments[0];
|
||||
Object.keys(x).forEach((k)=>arg[k] = x[k]);
|
||||
if(1===arguments.length && arguments[0] && 'object'===typeof arguments[0]){
|
||||
Object.assign(arg, arguments[0]);
|
||||
if(undefined===arg.flags) arg.flags = 'c';
|
||||
if(undefined===arg.vfs) arg.vfs = null;
|
||||
if(undefined===arg.filename) arg.filename = ':memory:';
|
||||
@@ -462,14 +462,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Expects to be given a DB instance or an `sqlite3*` pointer (may
|
||||
be null) and an sqlite3 API result code. If the result code is
|
||||
not falsy, this function throws an SQLite3Error with an error
|
||||
message from sqlite3_errmsg(), using dbPtr as the db handle, or
|
||||
sqlite3_errstr() if dbPtr is falsy. Note that if it's passed a
|
||||
non-error code like SQLITE_ROW or SQLITE_DONE, it will still
|
||||
throw but the error string might be "Not an error." The various
|
||||
non-0 non-error codes need to be checked for in
|
||||
client code where they are expected.
|
||||
message from sqlite3_errmsg(), using db (or, if db is-a DB,
|
||||
db.pointer) as the db handle, or sqlite3_errstr() if db is
|
||||
falsy. Note that if it's passed a non-error code like SQLITE_ROW
|
||||
or SQLITE_DONE, it will still throw but the error string might be
|
||||
"Not an error." The various non-0 non-error codes need to be
|
||||
checked for in client code where they are expected.
|
||||
|
||||
If it does not throw, it returns its first argument.
|
||||
*/
|
||||
DB.checkRc = checkSqlite3Rc;
|
||||
DB.checkRc = (db,resultCode)=>checkSqlite3Rc(db,resultCode);
|
||||
|
||||
DB.prototype = {
|
||||
/** Returns true if this db handle is open, else false. */
|
||||
@@ -1130,6 +1132,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1");
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
A convenience form of DB.checkRc(this,resultCode). If it does
|
||||
not throw, it returns this object.
|
||||
*/
|
||||
checkRc: function(resultCode){
|
||||
return DB.checkRc(this, resultCode);
|
||||
}
|
||||
}/*DB.prototype*/;
|
||||
|
||||
|
||||
@@ -92,7 +92,8 @@ const installOpfsVfs = function callee(options){
|
||||
}
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
if(undefined===options.verbose){
|
||||
options.verbose = urlParams.has('opfs-verbose') ? 3 : 2;
|
||||
options.verbose = urlParams.has('opfs-verbose')
|
||||
? (+urlParams.get('opfs-verbose') || 2) : 1;
|
||||
}
|
||||
if(undefined===options.sanityChecks){
|
||||
options.sanityChecks = urlParams.has('opfs-sanity-check');
|
||||
@@ -101,6 +102,8 @@ const installOpfsVfs = function callee(options){
|
||||
options.proxyUri = callee.defaultProxyUri;
|
||||
}
|
||||
|
||||
//console.warn("OPFS options =",options,self.location);
|
||||
|
||||
if('function' === typeof options.proxyUri){
|
||||
options.proxyUri = options.proxyUri();
|
||||
}
|
||||
@@ -166,7 +169,12 @@ const installOpfsVfs = function callee(options){
|
||||
opfsVfs.dispose();
|
||||
return promiseReject_(err);
|
||||
};
|
||||
const W = new Worker(options.proxyUri);
|
||||
const W =
|
||||
//#if target=es6-module
|
||||
new Worker(new URL(options.proxyUri, import.meta.url));
|
||||
//#else
|
||||
new Worker(options.proxyUri);
|
||||
//#endif
|
||||
W._originalOnError = W.onerror /* will be restored later */;
|
||||
W.onerror = function(err){
|
||||
// The error object doesn't contain any useful info when the
|
||||
@@ -335,16 +343,37 @@ const installOpfsVfs = function callee(options){
|
||||
'SQLITE_LOCK_PENDING',
|
||||
'SQLITE_LOCK_RESERVED',
|
||||
'SQLITE_LOCK_SHARED',
|
||||
'SQLITE_LOCKED',
|
||||
'SQLITE_MISUSE',
|
||||
'SQLITE_NOTFOUND',
|
||||
'SQLITE_OPEN_CREATE',
|
||||
'SQLITE_OPEN_DELETEONCLOSE',
|
||||
'SQLITE_OPEN_MAIN_DB',
|
||||
'SQLITE_OPEN_READONLY'
|
||||
].forEach((k)=>{
|
||||
if(undefined === (state.sq3Codes[k] = capi[k])){
|
||||
toss("Maintenance required: not found:",k);
|
||||
}
|
||||
});
|
||||
state.opfsFlags = Object.assign(Object.create(null),{
|
||||
/**
|
||||
Flag for use with xOpen(). "opfs-unlock-asap=1" enables
|
||||
this. See defaultUnlockAsap, below.
|
||||
*/
|
||||
OPFS_UNLOCK_ASAP: 0x01,
|
||||
/**
|
||||
If true, any async routine which implicitly acquires a sync
|
||||
access handle (i.e. an OPFS lock) will release that locks at
|
||||
the end of the call which acquires it. If false, such
|
||||
"autolocks" are not released until the VFS is idle for some
|
||||
brief amount of time.
|
||||
|
||||
The benefit of enabling this is much higher concurrency. The
|
||||
down-side is much-reduced performance (as much as a 4x decrease
|
||||
in speedtest1).
|
||||
*/
|
||||
defaultUnlockAsap: false
|
||||
});
|
||||
|
||||
/**
|
||||
Runs the given operation (by name) in the async worker
|
||||
@@ -467,9 +496,11 @@ const installOpfsVfs = function callee(options){
|
||||
/**
|
||||
Returns an array of the deserialized state stored by the most
|
||||
recent serialize() operation (from from this thread or the
|
||||
counterpart thread), or null if the serialization buffer is empty.
|
||||
counterpart thread), or null if the serialization buffer is
|
||||
empty. If passed a truthy argument, the serialization buffer
|
||||
is cleared after deserialization.
|
||||
*/
|
||||
state.s11n.deserialize = function(){
|
||||
state.s11n.deserialize = function(clear=false){
|
||||
++metrics.s11n.deserialize.count;
|
||||
const t = performance.now();
|
||||
const argc = viewU8[0];
|
||||
@@ -494,6 +525,7 @@ const installOpfsVfs = function callee(options){
|
||||
rc.push(v);
|
||||
}
|
||||
}
|
||||
if(clear) viewU8[0] = 0;
|
||||
//log("deserialize:",argc, rc);
|
||||
metrics.s11n.deserialize.time += performance.now() - t;
|
||||
return rc;
|
||||
@@ -566,7 +598,7 @@ const installOpfsVfs = function callee(options){
|
||||
const ndx = Math.random() * (f._n * 64) % f._n | 0;
|
||||
a[i] = f._chars[ndx];
|
||||
}
|
||||
return a.join('');
|
||||
return a.join("");
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -832,9 +864,15 @@ const installOpfsVfs = function callee(options){
|
||||
//xSleep is optionally defined below
|
||||
xOpen: function f(pVfs, zName, pFile, flags, pOutFlags){
|
||||
mTimeStart('xOpen');
|
||||
let opfsFlags = 0;
|
||||
if(0===zName){
|
||||
zName = randomFilename();
|
||||
}else if('number'===typeof zName){
|
||||
if(capi.sqlite3_uri_boolean(zName, "opfs-unlock-asap", 0)){
|
||||
/* -----------------------^^^^^ MUST pass the untranslated
|
||||
C-string here. */
|
||||
opfsFlags |= state.opfsFlags.OPFS_UNLOCK_ASAP;
|
||||
}
|
||||
zName = wasm.cstringToJs(zName);
|
||||
}
|
||||
const fh = Object.create(null);
|
||||
@@ -842,7 +880,7 @@ const installOpfsVfs = function callee(options){
|
||||
fh.filename = zName;
|
||||
fh.sab = new SharedArrayBuffer(state.fileBufferSize);
|
||||
fh.flags = flags;
|
||||
const rc = opRun('xOpen', pFile, zName, flags);
|
||||
const rc = opRun('xOpen', pFile, zName, flags, opfsFlags);
|
||||
if(!rc){
|
||||
/* Recall that sqlite3_vfs::xClose() will be called, even on
|
||||
error, unless pFile->pMethods is NULL. */
|
||||
@@ -1133,6 +1171,9 @@ const installOpfsVfs = function callee(options){
|
||||
|
||||
//TODO to support fiddle and worker1 db upload:
|
||||
//opfsUtil.createFile = function(absName, content=undefined){...}
|
||||
//We have sqlite3.wasm.sqlite3_wasm_vfs_create_file() for this
|
||||
//purpose but its interface and name are still under
|
||||
//consideration.
|
||||
|
||||
if(sqlite3.oo1){
|
||||
opfsUtil.OpfsDb = function(...args){
|
||||
@@ -1146,7 +1187,10 @@ const installOpfsVfs = function callee(options){
|
||||
[
|
||||
/* Truncate journal mode is faster than delete or wal for
|
||||
this vfs, per speedtest1. */
|
||||
"pragma journal_mode=truncate;"
|
||||
"pragma journal_mode=truncate;",
|
||||
/* Set a default busy-timeout handler to help OPFS dbs
|
||||
deal with multi-tab/multi-worker contention. */
|
||||
"pragma busy_timeout=2000;",
|
||||
/*
|
||||
This vfs benefits hugely from cache on moderate/large
|
||||
speedtest1 --size 50 and --size 100 workloads. We currently
|
||||
@@ -1154,8 +1198,8 @@ const installOpfsVfs = function callee(options){
|
||||
sqlite3.wasm. If that policy changes, the cache can
|
||||
be set here.
|
||||
*/
|
||||
//"pragma cache_size=-8388608;"
|
||||
].join('')
|
||||
//"pragma cache_size=-16384;"
|
||||
].join("")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,69 +11,18 @@
|
||||
***********************************************************************
|
||||
|
||||
This file is intended to be combined at build-time with other
|
||||
related code, most notably a header and footer which wraps this whole
|
||||
file into an Emscripten Module.postRun() handler which has a parameter
|
||||
named "Module" (the Emscripten Module object). The exact requirements,
|
||||
conventions, and build process are very much under construction and
|
||||
will be (re)documented once they've stopped fluctuating so much.
|
||||
related code, most notably a header and footer which wraps this
|
||||
whole file into an Emscripten Module.postRun() handler which has a
|
||||
parameter named "Module" (the Emscripten Module object). The sqlite3
|
||||
JS API has no hard requirements on Emscripten, and does not expose
|
||||
any Emscripten APIs to clients. It is structured such that its build
|
||||
can be tweaked to include it in arbitrary WASM environments which
|
||||
supply the necessary underlying features (e.g. a POSIX file I/O
|
||||
layer).
|
||||
|
||||
Specific goals of this project:
|
||||
Main project home page: https://sqlite.org
|
||||
|
||||
- Except where noted in the non-goals, provide a more-or-less
|
||||
feature-complete wrapper to the sqlite3 C API, insofar as WASM
|
||||
feature parity with C allows for. In fact, provide at least 3
|
||||
APIs...
|
||||
|
||||
1) Bind a low-level sqlite3 API which is as close to the native
|
||||
one as feasible in terms of usage.
|
||||
|
||||
2) A higher-level API, more akin to sql.js and node.js-style
|
||||
implementations. This one speaks directly to the low-level
|
||||
API. This API must be used from the same thread as the
|
||||
low-level API.
|
||||
|
||||
3) A second higher-level API which speaks to the previous APIs via
|
||||
worker messages. This one is intended for use in the main
|
||||
thread, with the lower-level APIs installed in a Worker thread,
|
||||
and talking to them via Worker messages. Because Workers are
|
||||
asynchronouns and have only a single message channel, some
|
||||
acrobatics are needed here to feed async work results back to
|
||||
the client (as we cannot simply pass around callbacks between
|
||||
the main and Worker threads).
|
||||
|
||||
- Insofar as possible, support client-side storage using JS
|
||||
filesystem APIs. As of this writing, such things are still very
|
||||
much under development.
|
||||
|
||||
Specific non-goals of this project:
|
||||
|
||||
- As WASM is a web-centric technology and UTF-8 is the King of
|
||||
Encodings in that realm, there are no currently plans to support
|
||||
the UTF16-related sqlite3 APIs. They would add a complication to
|
||||
the bindings for no appreciable benefit. Though web-related
|
||||
implementation details take priority, and the JavaScript
|
||||
components of the API specifically focus on browser clients, the
|
||||
lower-level WASM module "should" work in non-web WASM
|
||||
environments.
|
||||
|
||||
- Supporting old or niche-market platforms. WASM is built for a
|
||||
modern web and requires modern platforms.
|
||||
|
||||
- Though scalar User-Defined Functions (UDFs) may be created in
|
||||
JavaScript, there are currently no plans to add support for
|
||||
aggregate and window functions.
|
||||
|
||||
Attribution:
|
||||
|
||||
This project is endebted to the work of sql.js:
|
||||
|
||||
https://github.com/sql-js/sql.js
|
||||
|
||||
sql.js was an essential stepping stone in this code's development as
|
||||
it demonstrated how to handle some of the WASM-related voodoo (like
|
||||
handling pointers-to-pointers and adding JS implementations of
|
||||
C-bound callback functions). These APIs have a considerably
|
||||
different shape than sql.js's, however.
|
||||
Documentation home page: https://sqlite.org/wasm
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -83,6 +32,10 @@
|
||||
for the current environment, and then optionally be removed from
|
||||
the global object using `delete self.sqlite3ApiBootstrap`.
|
||||
|
||||
This function is not intended for client-level use. It is intended
|
||||
for use in creating bundles configured for specific WASM
|
||||
environments.
|
||||
|
||||
This function expects a configuration object, intended to abstract
|
||||
away details specific to any given WASM environment, primarily so
|
||||
that it can be used without any _direct_ dependency on
|
||||
@@ -90,11 +43,13 @@
|
||||
config object is only honored the first time this is
|
||||
called. Subsequent calls ignore the argument and return the same
|
||||
(configured) object which gets initialized by the first call.
|
||||
This function will throw if any of the required config options are
|
||||
missing.
|
||||
|
||||
The config object properties include:
|
||||
|
||||
- `exports`[^1]: the "exports" object for the current WASM
|
||||
environment. In an Emscripten build, this should be set to
|
||||
environment. In an Emscripten-based build, this should be set to
|
||||
`Module['asm']`.
|
||||
|
||||
- `memory`[^1]: optional WebAssembly.Memory object, defaulting to
|
||||
@@ -104,7 +59,7 @@
|
||||
WASM-exported memory.
|
||||
|
||||
- `bigIntEnabled`: true if BigInt support is enabled. Defaults to
|
||||
true if self.BigInt64Array is available, else false. Some APIs
|
||||
true if `self.BigInt64Array` is available, else false. Some APIs
|
||||
will throw exceptions if called without BigInt support, as BigInt
|
||||
is required for marshalling C-side int64 into and out of JS.
|
||||
|
||||
@@ -116,10 +71,12 @@
|
||||
the `free(3)`-compatible routine for the WASM
|
||||
environment. Defaults to `"free"`.
|
||||
|
||||
- `wasmfsOpfsDir`[^1]: if the environment supports persistent storage, this
|
||||
directory names the "mount point" for that directory. It must be prefixed
|
||||
by `/` and may currently contain only a single directory-name part. Using
|
||||
the root directory name is not supported by any current persistent backend.
|
||||
- `wasmfsOpfsDir`[^1]: if the environment supports persistent
|
||||
storage using OPFS-over-WASMFS , this directory names the "mount
|
||||
point" for that directory. It must be prefixed by `/` and may
|
||||
contain only a single directory-name part. Using the root
|
||||
directory name is not supported by any current persistent
|
||||
backend. This setting is only used in WASMFS-enabled builds.
|
||||
|
||||
|
||||
[^1] = This property may optionally be a function, in which case this
|
||||
@@ -180,11 +137,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
const capi = Object.create(null);
|
||||
/**
|
||||
Holds state which are specific to the WASM-related
|
||||
infrastructure and glue code. It is not expected that client
|
||||
code will normally need these, but they're exposed here in case
|
||||
it does. These APIs are _not_ to be considered an
|
||||
official/stable part of the sqlite3 WASM API. They may change
|
||||
as the developers' experience suggests appropriate changes.
|
||||
infrastructure and glue code.
|
||||
|
||||
Note that a number of members of this object are injected
|
||||
dynamically after the api object is fully constructed, so
|
||||
@@ -217,7 +170,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns
|
||||
falsy) a synthesized string which contains that integer.
|
||||
|
||||
- If passed 2 arguments and the 2nd is a object, it bevaves
|
||||
- If passed 2 arguments and the 2nd is a object, it behaves
|
||||
like the Error(string,object) constructor except that the first
|
||||
argument is subject to the is-integer semantics from the
|
||||
previous point.
|
||||
@@ -370,14 +323,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
If v is-a Array, its join('') result is returned. If
|
||||
If v is-a Array, its join("") result is returned. If
|
||||
isSQLableTypedArray(v) is true then typedArrayToString(v) is
|
||||
returned. If it looks like a WASM pointer, wasm.cstringToJs(v) is
|
||||
returned. Else v is returned as-is.
|
||||
*/
|
||||
const flexibleString = function(v){
|
||||
if(isSQLableTypedArray(v)) return typedArrayToString(v);
|
||||
else if(Array.isArray(v)) return v.join('');
|
||||
else if(Array.isArray(v)) return v.join("");
|
||||
else if(wasm.isPtr(v)) v = wasm.cstringToJs(v);
|
||||
return v;
|
||||
};
|
||||
@@ -388,8 +341,22 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
exceptions.
|
||||
*/
|
||||
class WasmAllocError extends Error {
|
||||
/**
|
||||
If called with 2 arguments and the 2nd one is an object, it
|
||||
behaves like the Error constructor, else it concatenates all
|
||||
arguments together with a single space between each to
|
||||
construct an error message string. As a special case, if
|
||||
called with no arguments then it uses a default error
|
||||
message.
|
||||
*/
|
||||
constructor(...args){
|
||||
super(...args);
|
||||
if(2===args.length && 'object'===typeof args){
|
||||
super(...args);
|
||||
}else if(args.length){
|
||||
super(args.join(' '));
|
||||
}else{
|
||||
super("Allocation failed.");
|
||||
}
|
||||
this.name = 'WasmAllocError';
|
||||
}
|
||||
};
|
||||
@@ -661,9 +628,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
/**
|
||||
The WASM IR (Intermediate Representation) value for
|
||||
pointer-type values. It MUST refer to a value type of the
|
||||
size described by this.ptrSizeof _or_ it may be any value
|
||||
which ends in '*', which Emscripten's glue code internally
|
||||
translates to i32.
|
||||
size described by this.ptrSizeof.
|
||||
*/
|
||||
ptrIR: config.wasmPtrIR || "i32",
|
||||
/**
|
||||
@@ -699,21 +664,33 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
API NOT throw and must instead return SQLITE_NOMEM (or
|
||||
equivalent, depending on the context).
|
||||
|
||||
That said, very few cases in the API can result in
|
||||
Very few cases in the sqlite3 JS APIs can result in
|
||||
client-defined functions propagating exceptions via the C-style
|
||||
API. Most notably, this applies ot User-defined SQL Functions
|
||||
(UDFs) registered via sqlite3_create_function_v2(). For that
|
||||
specific case it is recommended that all UDF creation be
|
||||
funneled through a utility function and that a wrapper function
|
||||
be added around the UDF which catches any exception and sets
|
||||
the error state to OOM. (The overall complexity of registering
|
||||
UDFs essentially requires a helper for doing so!)
|
||||
API. Most notably, this applies to WASM-bound JS functions
|
||||
which are created directly by clients and passed on _as WASM
|
||||
function pointers_ to functions such as
|
||||
sqlite3_create_function_v2(). Such bindings created
|
||||
transparently by this API will automatically use wrappers which
|
||||
catch exceptions and convert them to appropriate error codes.
|
||||
|
||||
For cases where non-throwing allocation is required, use
|
||||
sqlite3.wasm.alloc.impl(), which is direct binding of the
|
||||
underlying C-level allocator.
|
||||
|
||||
Design note: this function is not named "malloc" primarily
|
||||
because Emscripten uses that name and we wanted to avoid any
|
||||
confusion early on in this code's development, when it still
|
||||
had close ties to Emscripten's glue code.
|
||||
*/
|
||||
alloc: undefined/*installed later*/,
|
||||
|
||||
/**
|
||||
The API's one single point of access to the WASM-side memory
|
||||
deallocator. Works like free(3) (and is likely bound to
|
||||
free()).
|
||||
|
||||
Design note: this function is not named "free" for the same
|
||||
reason that this.alloc() is not called this.malloc().
|
||||
*/
|
||||
dealloc: undefined/*installed later*/
|
||||
|
||||
@@ -741,7 +718,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
wasm.allocFromTypedArray = function(srcTypedArray){
|
||||
affirmBindableTypedArray(srcTypedArray);
|
||||
const pRet = wasm.alloc(srcTypedArray.byteLength || 1);
|
||||
wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet);
|
||||
wasm.heapForSize(srcTypedArray.constructor).set(
|
||||
srcTypedArray.byteLength ? srcTypedArray : [0], pRet
|
||||
);
|
||||
return pRet;
|
||||
};
|
||||
|
||||
@@ -752,13 +731,13 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
if(!(f instanceof Function)) toss3("Missing required exports[",key,"] function.");
|
||||
}
|
||||
|
||||
wasm.alloc = function(n){
|
||||
const m = wasm.exports[keyAlloc](n);
|
||||
if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes.");
|
||||
wasm.alloc = function f(n){
|
||||
const m = f.impl(n);
|
||||
if(!m) throw new WasmAllocError("Failed to allocate",n," bytes.");
|
||||
return m;
|
||||
};
|
||||
|
||||
wasm.dealloc = (m)=>wasm.exports[keyDealloc](m);
|
||||
wasm.alloc.impl = wasm.exports[keyAlloc];
|
||||
wasm.dealloc = wasm.exports[keyDealloc];
|
||||
|
||||
/**
|
||||
Reports info about compile-time options using
|
||||
@@ -858,6 +837,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
the lines of sqlite3_prepare_v3(). The slightly problematic
|
||||
part is the final argument (text destructor). */
|
||||
],
|
||||
["sqlite3_busy_timeout","int", "sqlite3*", "int"],
|
||||
["sqlite3_close_v2", "int", "sqlite3*"],
|
||||
["sqlite3_changes", "int", "sqlite3*"],
|
||||
["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
|
||||
@@ -1267,17 +1247,24 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
Serializes the given `sqlite3*` pointer to a Uint8Array, as per
|
||||
sqlite3_serialize(). On success it returns a Uint8Array. On
|
||||
error it throws with a description of the problem.
|
||||
A convenience wrapper around sqlite3_serialize() which serializes
|
||||
the given `sqlite3*` pointer to a Uint8Array.
|
||||
|
||||
On success it returns a Uint8Array. If the schema is empty, an
|
||||
empty array is returned.
|
||||
|
||||
`schema` is the schema to serialize. It may be a WASM C-string
|
||||
pointer or a JS string. If it is falsy, it defaults to `"main"`.
|
||||
|
||||
On error it throws with a description of the problem.
|
||||
*/
|
||||
capi.sqlite3_js_db_export = function(pDb){
|
||||
capi.sqlite3_js_db_export = function(pDb, schema=0){
|
||||
if(!pDb) toss3('Invalid sqlite3* argument.');
|
||||
if(!wasm.bigIntEnabled) toss3('BigInt64 support is not enabled.');
|
||||
const stack = wasm.pstack.pointer;
|
||||
const scope = wasm.scopedAllocPush();
|
||||
let pOut;
|
||||
try{
|
||||
const pSize = wasm.pstack.alloc(8/*i64*/ + wasm.ptrSizeof);
|
||||
const pSize = wasm.scopedAlloc(8/*i64*/ + wasm.ptrSizeof);
|
||||
const ppOut = pSize + 8;
|
||||
/**
|
||||
Maintenance reminder, since this cost a full hour of grief
|
||||
@@ -1286,8 +1273,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
export reads a garbage size because it's not on an 8-byte
|
||||
memory boundary!
|
||||
*/
|
||||
const zSchema = schema
|
||||
? (wasm.isPtr(schema) ? schema : wasm.scopedAllocCString(''+schema))
|
||||
: 0;
|
||||
let rc = wasm.exports.sqlite3_wasm_db_serialize(
|
||||
pDb, ppOut, pSize, 0
|
||||
pDb, zSchema, ppOut, pSize, 0
|
||||
);
|
||||
if(rc){
|
||||
toss3("Database serialization failed with code",
|
||||
@@ -1301,7 +1291,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
return rc;
|
||||
}finally{
|
||||
if(pOut) wasm.exports.sqlite3_free(pOut);
|
||||
wasm.pstack.restore(stack);
|
||||
wasm.scopedAllocPop(scope);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1333,21 +1323,21 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
|
||||
/**
|
||||
Internal helper for sqlite3_js_kvvfs_clear() and friends.
|
||||
Its argument should be one of ('local','session','').
|
||||
Its argument should be one of ('local','session',"").
|
||||
*/
|
||||
const __kvvfsInfo = function(which){
|
||||
const rc = Object.create(null);
|
||||
rc.prefix = 'kvvfs-'+which;
|
||||
rc.stores = [];
|
||||
if('session'===which || ''===which) rc.stores.push(self.sessionStorage);
|
||||
if('local'===which || ''===which) rc.stores.push(self.localStorage);
|
||||
if('session'===which || ""===which) rc.stores.push(self.sessionStorage);
|
||||
if('local'===which || ""===which) rc.stores.push(self.localStorage);
|
||||
return rc;
|
||||
};
|
||||
|
||||
/**
|
||||
Clears all storage used by the kvvfs DB backend, deleting any
|
||||
DB(s) stored there. Its argument must be either 'session',
|
||||
'local', or ''. In the first two cases, only sessionStorage
|
||||
'local', or "". In the first two cases, only sessionStorage
|
||||
resp. localStorage is cleared. If it's an empty string (the
|
||||
default) then both are cleared. Only storage keys which match
|
||||
the pattern used by kvvfs are cleared: any other client-side
|
||||
@@ -1357,7 +1347,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
|
||||
Returns the number of entries cleared.
|
||||
*/
|
||||
capi.sqlite3_js_kvvfs_clear = function(which=''){
|
||||
capi.sqlite3_js_kvvfs_clear = function(which=""){
|
||||
let rc = 0;
|
||||
const kvinfo = __kvvfsInfo(which);
|
||||
kvinfo.stores.forEach((s)=>{
|
||||
@@ -1377,7 +1367,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
This routine guesses the approximate amount of
|
||||
window.localStorage and/or window.sessionStorage in use by the
|
||||
kvvfs database backend. Its argument must be one of
|
||||
('session', 'local', ''). In the first two cases, only
|
||||
('session', 'local', ""). In the first two cases, only
|
||||
sessionStorage resp. localStorage is counted. If it's an empty
|
||||
string (the default) then both are counted. Only storage keys
|
||||
which match the pattern used by kvvfs are counted. The returned
|
||||
@@ -1390,7 +1380,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
those limits are unspecified and may include per-entry
|
||||
overhead invisible to clients.
|
||||
*/
|
||||
capi.sqlite3_js_kvvfs_size = function(which=''){
|
||||
capi.sqlite3_js_kvvfs_size = function(which=""){
|
||||
let sz = 0;
|
||||
const kvinfo = __kvvfsInfo(which);
|
||||
kvinfo.stores.forEach((s)=>{
|
||||
|
||||
@@ -429,7 +429,7 @@ sqlite3.initWorker1API = function(){
|
||||
};
|
||||
|
||||
const isSpecialDbFilename = (n)=>{
|
||||
return ''===n || ':'===n[0];
|
||||
return ""===n || ':'===n[0];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -453,7 +453,7 @@ sqlite3.initWorker1API = function(){
|
||||
let byteArray, pVfs;
|
||||
oargs.vfs = args.vfs;
|
||||
if(isSpecialDbFilename(args.filename)){
|
||||
oargs.filename = args.filename || '';
|
||||
oargs.filename = args.filename || "";
|
||||
}else{
|
||||
oargs.filename = args.filename;
|
||||
byteArray = args.byteArray;
|
||||
|
||||
@@ -29,6 +29,22 @@
|
||||
This file represents an implementation detail of a larger piece of
|
||||
code, and not a public interface. Its details may change at any time
|
||||
and are not intended to be used by any client-level code.
|
||||
|
||||
2022-11-27: Chrome v108 changes some async methods to synchronous, as
|
||||
documented at:
|
||||
|
||||
https://developer.chrome.com/blog/sync-methods-for-accesshandles/
|
||||
|
||||
We cannot change to the sync forms at this point without breaking
|
||||
clients who use Chrome v104-ish or higher. truncate(), getSize(),
|
||||
flush(), and close() are now (as of v108) synchronous. Calling them
|
||||
with an "await", as we have to for the async forms, is still legal
|
||||
with the sync forms but is superfluous. Calling the async forms with
|
||||
theFunc().then(...) is not compatible with the change to
|
||||
synchronous, but we do do not use those APIs that way. i.e. we don't
|
||||
_need_ to change anything for this, but at some point (after Chrome
|
||||
versions (approximately) 104-107 are extinct) should change our
|
||||
usage of those methods to remove the "await".
|
||||
*/
|
||||
"use strict";
|
||||
const toss = function(...args){throw new Error(args.join(' '))};
|
||||
@@ -44,6 +60,7 @@ if(self.window === self){
|
||||
this API.
|
||||
*/
|
||||
const state = Object.create(null);
|
||||
|
||||
/**
|
||||
verbose:
|
||||
|
||||
@@ -52,7 +69,7 @@ const state = Object.create(null);
|
||||
2 = warnings and errors
|
||||
3 = debug, warnings, and errors
|
||||
*/
|
||||
state.verbose = 2;
|
||||
state.verbose = 1;
|
||||
|
||||
const loggers = {
|
||||
0:console.error.bind(console),
|
||||
@@ -96,13 +113,27 @@ metrics.dump = ()=>{
|
||||
};
|
||||
|
||||
/**
|
||||
Map of sqlite3_file pointers (integers) to metadata related to a
|
||||
given OPFS file handles. The pointers are, in this side of the
|
||||
interface, opaque file handle IDs provided by the synchronous
|
||||
part of this constellation. Each value is an object with a structure
|
||||
demonstrated in the xOpen() impl.
|
||||
__openFiles is a map of sqlite3_file pointers (integers) to
|
||||
metadata related to a given OPFS file handles. The pointers are, in
|
||||
this side of the interface, opaque file handle IDs provided by the
|
||||
synchronous part of this constellation. Each value is an object
|
||||
with a structure demonstrated in the xOpen() impl.
|
||||
*/
|
||||
const __openFiles = Object.create(null);
|
||||
/**
|
||||
__implicitLocks is a Set of sqlite3_file pointers (integers) which were
|
||||
"auto-locked". i.e. those for which we obtained a sync access
|
||||
handle without an explicit xLock() call. Such locks will be
|
||||
released during db connection idle time, whereas a sync access
|
||||
handle obtained via xLock(), or subsequently xLock()'d after
|
||||
auto-acquisition, will not be released until xUnlock() is called.
|
||||
|
||||
Maintenance reminder: if we relinquish auto-locks at the end of the
|
||||
operation which acquires them, we pay a massive performance
|
||||
penalty: speedtest1 benchmarks take up to 4x as long. By delaying
|
||||
the lock release until idle time, the hit is negligible.
|
||||
*/
|
||||
const __implicitLocks = new Set();
|
||||
|
||||
/**
|
||||
Expects an OPFS file path. It gets resolved, such that ".."
|
||||
@@ -135,6 +166,71 @@ const getDirForFilename = async function f(absFilename, createDirs = false){
|
||||
return [dh, filename];
|
||||
};
|
||||
|
||||
/**
|
||||
If the given file-holding object has a sync handle attached to it,
|
||||
that handle is remove and asynchronously closed. Though it may
|
||||
sound sensible to continue work as soon as the close() returns
|
||||
(noting that it's asynchronous), doing so can cause operations
|
||||
performed soon afterwards, e.g. a call to getSyncHandle() to fail
|
||||
because they may happen out of order from the close(). OPFS does
|
||||
not guaranty that the actual order of operations is retained in
|
||||
such cases. i.e. always "await" on the result of this function.
|
||||
*/
|
||||
const closeSyncHandle = async (fh)=>{
|
||||
if(fh.syncHandle){
|
||||
log("Closing sync handle for",fh.filenameAbs);
|
||||
const h = fh.syncHandle;
|
||||
delete fh.syncHandle;
|
||||
delete fh.xLock;
|
||||
__implicitLocks.delete(fh.fid);
|
||||
return h.close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
A proxy for closeSyncHandle() which is guaranteed to not throw.
|
||||
|
||||
This function is part of a lock/unlock step in functions which
|
||||
require a sync access handle but may be called without xLock()
|
||||
having been called first. Such calls need to release that
|
||||
handle to avoid locking the file for all of time. This is an
|
||||
_attempt_ at reducing cross-tab contention but it may prove
|
||||
to be more of a problem than a solution and may need to be
|
||||
removed.
|
||||
*/
|
||||
const closeSyncHandleNoThrow = async (fh)=>{
|
||||
try{await closeSyncHandle(fh)}
|
||||
catch(e){
|
||||
warn("closeSyncHandleNoThrow() ignoring:",e,fh);
|
||||
}
|
||||
};
|
||||
|
||||
/* Release all auto-locks. */
|
||||
const releaseImplicitLocks = async ()=>{
|
||||
if(__implicitLocks.size){
|
||||
/* Release all auto-locks. */
|
||||
for(const fid of __implicitLocks){
|
||||
const fh = __openFiles[fid];
|
||||
await closeSyncHandleNoThrow(fh);
|
||||
log("Auto-unlocked",fid,fh.filenameAbs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
An experiment in improving concurrency by freeing up implicit locks
|
||||
sooner. This is known to impact performance dramatically but it has
|
||||
also shown to improve concurrency considerably.
|
||||
|
||||
If fh.releaseImplicitLocks is truthy and fh is in __implicitLocks,
|
||||
this routine returns closeSyncHandleNoThrow(), else it is a no-op.
|
||||
*/
|
||||
const releaseImplicitLock = async (fh)=>{
|
||||
if(fh.releaseImplicitLocks && __implicitLocks.has(fh.fid)){
|
||||
return closeSyncHandleNoThrow(fh);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
An error class specifically for use with getSyncHandle(), the goal
|
||||
of which is to eventually be able to distinguish unambiguously
|
||||
@@ -153,7 +249,22 @@ class GetSyncHandleError extends Error {
|
||||
this.name = 'GetSyncHandleError';
|
||||
}
|
||||
};
|
||||
GetSyncHandleError.convertRc = (e,rc)=>{
|
||||
if(0){
|
||||
/* This approach makes the very wild assumption that such a
|
||||
failure _is_ a locking error. In practice that appears to be
|
||||
the most common error, by far, but we cannot unambiguously
|
||||
distinguish that from other errors.
|
||||
|
||||
This approach is highly questionable.
|
||||
*/
|
||||
return (e instanceof GetSyncHandleError)
|
||||
? state.sq3Codes.SQLITE_IOERR_LOCK
|
||||
: rc;
|
||||
}else{
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
/**
|
||||
Returns the sync access handle associated with the given file
|
||||
handle object (which must be a valid handle object, as created by
|
||||
@@ -165,11 +276,11 @@ class GetSyncHandleError extends Error {
|
||||
still fails at that point it will give up and propagate the
|
||||
exception.
|
||||
*/
|
||||
const getSyncHandle = async (fh)=>{
|
||||
const getSyncHandle = async (fh,opName)=>{
|
||||
if(!fh.syncHandle){
|
||||
const t = performance.now();
|
||||
log("Acquiring sync handle for",fh.filenameAbs);
|
||||
const maxTries = 4, msBase = 300;
|
||||
const maxTries = 6, msBase = 300;
|
||||
let i = 1, ms = msBase;
|
||||
for(; true; ms = msBase * ++i){
|
||||
try {
|
||||
@@ -181,39 +292,26 @@ const getSyncHandle = async (fh)=>{
|
||||
}catch(e){
|
||||
if(i === maxTries){
|
||||
throw new GetSyncHandleError(
|
||||
e, "Error getting sync handle.",maxTries,
|
||||
e, "Error getting sync handle for",opName+"().",maxTries,
|
||||
"attempts failed.",fh.filenameAbs
|
||||
);
|
||||
}
|
||||
warn("Error getting sync handle. Waiting",ms,
|
||||
"ms and trying again.",fh.filenameAbs,e);
|
||||
warn("Error getting sync handle for",opName+"(). Waiting",ms,
|
||||
"ms and trying again.",fh.filenameAbs,e);
|
||||
//await releaseImplicitLocks();
|
||||
Atomics.wait(state.sabOPView, state.opIds.retry, 0, ms);
|
||||
}
|
||||
}
|
||||
log("Got sync handle for",fh.filenameAbs,'in',performance.now() - t,'ms');
|
||||
log("Got",opName+"() sync handle for",fh.filenameAbs,
|
||||
'in',performance.now() - t,'ms');
|
||||
if(!fh.xLock){
|
||||
__implicitLocks.add(fh.fid);
|
||||
log("Auto-locked for",opName+"()",fh.fid,fh.filenameAbs);
|
||||
}
|
||||
}
|
||||
return fh.syncHandle;
|
||||
};
|
||||
|
||||
/**
|
||||
If the given file-holding object has a sync handle attached to it,
|
||||
that handle is remove and asynchronously closed. Though it may
|
||||
sound sensible to continue work as soon as the close() returns
|
||||
(noting that it's asynchronous), doing so can cause operations
|
||||
performed soon afterwards, e.g. a call to getSyncHandle() to fail
|
||||
because they may happen out of order from the close(). OPFS does
|
||||
not guaranty that the actual order of operations is retained in
|
||||
such cases. i.e. always "await" on the result of this function.
|
||||
*/
|
||||
const closeSyncHandle = async (fh)=>{
|
||||
if(fh.syncHandle){
|
||||
log("Closing sync handle for",fh.filenameAbs);
|
||||
const h = fh.syncHandle;
|
||||
delete fh.syncHandle;
|
||||
return h.close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Stores the given value at state.sabOPView[state.opIds.rc] and then
|
||||
Atomics.notify()'s it.
|
||||
@@ -342,9 +440,10 @@ const vfsAsyncImpls = {
|
||||
xClose: async function(fid/*sqlite3_file pointer*/){
|
||||
const opName = 'xClose';
|
||||
mTimeStart(opName);
|
||||
__implicitLocks.delete(fid);
|
||||
const fh = __openFiles[fid];
|
||||
let rc = 0;
|
||||
wTimeStart('xClose');
|
||||
wTimeStart(opName);
|
||||
if(fh){
|
||||
delete __openFiles[fid];
|
||||
await closeSyncHandle(fh);
|
||||
@@ -406,13 +505,14 @@ const vfsAsyncImpls = {
|
||||
wTimeStart('xFileSize');
|
||||
try{
|
||||
affirmLocked('xFileSize',fh);
|
||||
rc = await (await getSyncHandle(fh)).getSize();
|
||||
state.s11n.serialize(Number(rc));
|
||||
const sz = await (await getSyncHandle(fh,'xFileSize')).getSize();
|
||||
state.s11n.serialize(Number(sz));
|
||||
rc = 0;
|
||||
}catch(e){
|
||||
state.s11n.storeException(2,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR;
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
wTimeEnd();
|
||||
storeAndNotify('xFileSize', rc);
|
||||
mTimeEnd();
|
||||
@@ -422,12 +522,17 @@ const vfsAsyncImpls = {
|
||||
mTimeStart('xLock');
|
||||
const fh = __openFiles[fid];
|
||||
let rc = 0;
|
||||
const oldLockType = fh.xLock;
|
||||
fh.xLock = lockType;
|
||||
if( !fh.syncHandle ){
|
||||
wTimeStart('xLock');
|
||||
try { await getSyncHandle(fh) }
|
||||
catch(e){
|
||||
try {
|
||||
await getSyncHandle(fh,'xLock');
|
||||
__implicitLocks.delete(fid);
|
||||
}catch(e){
|
||||
state.s11n.storeException(1,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_LOCK;
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_LOCK);
|
||||
fh.xLock = oldLockType;
|
||||
}
|
||||
wTimeEnd();
|
||||
}
|
||||
@@ -435,10 +540,10 @@ const vfsAsyncImpls = {
|
||||
mTimeEnd();
|
||||
},
|
||||
xOpen: async function(fid/*sqlite3_file pointer*/, filename,
|
||||
flags/*SQLITE_OPEN_...*/){
|
||||
flags/*SQLITE_OPEN_...*/,
|
||||
opfsFlags/*OPFS_...*/){
|
||||
const opName = 'xOpen';
|
||||
mTimeStart(opName);
|
||||
const deleteOnClose = (state.sq3Codes.SQLITE_OPEN_DELETEONCLOSE & flags);
|
||||
const create = (state.sq3Codes.SQLITE_OPEN_CREATE & flags);
|
||||
wTimeStart('xOpen');
|
||||
try{
|
||||
@@ -453,14 +558,9 @@ const vfsAsyncImpls = {
|
||||
return;
|
||||
}
|
||||
const hFile = await hDir.getFileHandle(filenamePart, {create});
|
||||
/**
|
||||
wa-sqlite, at this point, grabs a SyncAccessHandle and
|
||||
assigns it to the syncHandle prop of the file state
|
||||
object, but only for certain cases and it's unclear why it
|
||||
places that limitation on it.
|
||||
*/
|
||||
wTimeEnd();
|
||||
__openFiles[fid] = Object.assign(Object.create(null),{
|
||||
const fh = Object.assign(Object.create(null),{
|
||||
fid: fid,
|
||||
filenameAbs: filename,
|
||||
filenamePart: filenamePart,
|
||||
dirHandle: hDir,
|
||||
@@ -468,8 +568,26 @@ const vfsAsyncImpls = {
|
||||
sabView: state.sabFileBufView,
|
||||
readOnly: create
|
||||
? false : (state.sq3Codes.SQLITE_OPEN_READONLY & flags),
|
||||
deleteOnClose: deleteOnClose
|
||||
deleteOnClose: !!(state.sq3Codes.SQLITE_OPEN_DELETEONCLOSE & flags)
|
||||
});
|
||||
fh.releaseImplicitLocks =
|
||||
(opfsFlags & state.opfsFlags.OPFS_UNLOCK_ASAP)
|
||||
|| state.opfsFlags.defaultUnlockAsap;
|
||||
if(0 /* this block is modelled after something wa-sqlite
|
||||
does but it leads to immediate contention on journal files. */
|
||||
&& (0===(flags & state.sq3Codes.SQLITE_OPEN_MAIN_DB))){
|
||||
/* sqlite does not lock these files, so go ahead and grab an OPFS
|
||||
lock.
|
||||
|
||||
https://www.sqlite.org/uri.html
|
||||
*/
|
||||
fh.xLock = "xOpen"/* Truthy value to keep entry from getting
|
||||
flagged as auto-locked. String value so
|
||||
that we can easily distinguish is later
|
||||
if needed. */;
|
||||
await getSyncHandle(fh,'xOpen');
|
||||
}
|
||||
__openFiles[fid] = fh;
|
||||
storeAndNotify(opName, 0);
|
||||
}catch(e){
|
||||
wTimeEnd();
|
||||
@@ -486,7 +604,7 @@ const vfsAsyncImpls = {
|
||||
try{
|
||||
affirmLocked('xRead',fh);
|
||||
wTimeStart('xRead');
|
||||
nRead = (await getSyncHandle(fh)).read(
|
||||
nRead = (await getSyncHandle(fh,'xRead')).read(
|
||||
fh.sabView.subarray(0, n),
|
||||
{at: Number(offset64)}
|
||||
);
|
||||
@@ -499,8 +617,9 @@ const vfsAsyncImpls = {
|
||||
if(undefined===nRead) wTimeEnd();
|
||||
error("xRead() failed",e,fh);
|
||||
state.s11n.storeException(1,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_READ;
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_READ);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
storeAndNotify('xRead',rc);
|
||||
mTimeEnd();
|
||||
},
|
||||
@@ -529,12 +648,13 @@ const vfsAsyncImpls = {
|
||||
try{
|
||||
affirmLocked('xTruncate',fh);
|
||||
affirmNotRO('xTruncate', fh);
|
||||
await (await getSyncHandle(fh)).truncate(size);
|
||||
await (await getSyncHandle(fh,'xTruncate')).truncate(size);
|
||||
}catch(e){
|
||||
error("xTruncate():",e,fh);
|
||||
state.s11n.storeException(2,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_TRUNCATE;
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_TRUNCATE);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
wTimeEnd();
|
||||
storeAndNotify('xTruncate',rc);
|
||||
mTimeEnd();
|
||||
@@ -566,15 +686,16 @@ const vfsAsyncImpls = {
|
||||
affirmLocked('xWrite',fh);
|
||||
affirmNotRO('xWrite', fh);
|
||||
rc = (
|
||||
n === (await getSyncHandle(fh))
|
||||
n === (await getSyncHandle(fh,'xWrite'))
|
||||
.write(fh.sabView.subarray(0, n),
|
||||
{at: Number(offset64)})
|
||||
) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE;
|
||||
}catch(e){
|
||||
error("xWrite():",e,fh);
|
||||
state.s11n.storeException(1,e);
|
||||
rc = state.sq3Codes.SQLITE_IOERR_WRITE;
|
||||
rc = GetSyncHandleError.convertRc(e,state.sq3Codes.SQLITE_IOERR_WRITE);
|
||||
}
|
||||
await releaseImplicitLock(fh);
|
||||
wTimeEnd();
|
||||
storeAndNotify('xWrite',rc);
|
||||
mTimeEnd();
|
||||
@@ -610,7 +731,7 @@ const initS11n = ()=>{
|
||||
default: toss("Invalid type ID:",tid);
|
||||
}
|
||||
};
|
||||
state.s11n.deserialize = function(){
|
||||
state.s11n.deserialize = function(clear=false){
|
||||
++metrics.s11n.deserialize.count;
|
||||
const t = performance.now();
|
||||
const argc = viewU8[0];
|
||||
@@ -635,6 +756,7 @@ const initS11n = ()=>{
|
||||
rc.push(v);
|
||||
}
|
||||
}
|
||||
if(clear) viewU8[0] = 0;
|
||||
//log("deserialize:",argc, rc);
|
||||
metrics.s11n.deserialize.time += performance.now() - t;
|
||||
return rc;
|
||||
@@ -678,7 +800,7 @@ const initS11n = ()=>{
|
||||
state.s11n.storeException = state.asyncS11nExceptions
|
||||
? ((priority,e)=>{
|
||||
if(priority<=state.asyncS11nExceptions){
|
||||
state.s11n.serialize([e.name,': ',e.message].join(''));
|
||||
state.s11n.serialize([e.name,': ',e.message].join(""));
|
||||
}
|
||||
})
|
||||
: ()=>{};
|
||||
@@ -699,23 +821,26 @@ const waitLoop = async function f(){
|
||||
/**
|
||||
waitTime is how long (ms) to wait for each Atomics.wait().
|
||||
We need to wake up periodically to give the thread a chance
|
||||
to do other things.
|
||||
to do other things. If this is too high (e.g. 500ms) then
|
||||
even two workers/tabs can easily run into locking errors.
|
||||
*/
|
||||
const waitTime = 1000;
|
||||
const waitTime = 100;
|
||||
while(!flagAsyncShutdown){
|
||||
try {
|
||||
if('timed-out'===Atomics.wait(
|
||||
state.sabOPView, state.opIds.whichOp, 0, waitTime
|
||||
)){
|
||||
await releaseImplicitLocks();
|
||||
continue;
|
||||
}
|
||||
const opId = Atomics.load(state.sabOPView, state.opIds.whichOp);
|
||||
Atomics.store(state.sabOPView, state.opIds.whichOp, 0);
|
||||
const hnd = opHandlers[opId] ?? toss("No waitLoop handler for whichOp #",opId);
|
||||
const args = state.s11n.deserialize() || [];
|
||||
state.s11n.serialize(/* clear s11n to keep the caller from
|
||||
confusing this with an exception string
|
||||
written by the upcoming operation */);
|
||||
const args = state.s11n.deserialize(
|
||||
true /* clear s11n to keep the caller from confusing this with
|
||||
an exception string written by the upcoming
|
||||
operation */
|
||||
) || [];
|
||||
//warn("waitLoop() whichOp =",opId, hnd, args);
|
||||
if(hnd.f) await hnd.f(...args);
|
||||
else error("Missing callback for opId",opId);
|
||||
@@ -735,7 +860,7 @@ navigator.storage.getDirectory().then(function(d){
|
||||
const opt = data.args;
|
||||
state.littleEndian = opt.littleEndian;
|
||||
state.asyncS11nExceptions = opt.asyncS11nExceptions;
|
||||
state.verbose = opt.verbose ?? 2;
|
||||
state.verbose = opt.verbose ?? 1;
|
||||
state.fileBufferSize = opt.fileBufferSize;
|
||||
state.sabS11nOffset = opt.sabS11nOffset;
|
||||
state.sabS11nSize = opt.sabS11nSize;
|
||||
@@ -746,6 +871,7 @@ navigator.storage.getDirectory().then(function(d){
|
||||
state.sabS11nView = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
|
||||
state.opIds = opt.opIds;
|
||||
state.sq3Codes = opt.sq3Codes;
|
||||
state.opfsFlags = opt.opfsFlags;
|
||||
Object.keys(vfsAsyncImpls).forEach((k)=>{
|
||||
if(!Number.isFinite(state.opIds[k])){
|
||||
toss("Maintenance required: missing state.opIds[",k,"]");
|
||||
|
||||
+12
-13
@@ -67,7 +67,7 @@
|
||||
** larger cache benefits the larger workloads. Speed differences
|
||||
** between 2x and nearly 3x have been measured with ample page cache.
|
||||
*/
|
||||
# define SQLITE_DEFAULT_CACHE_SIZE -16777216
|
||||
# define SQLITE_DEFAULT_CACHE_SIZE -16384
|
||||
#endif
|
||||
#if 0 && !defined(SQLITE_DEFAULT_PAGE_SIZE)
|
||||
/* TODO: experiment with this. */
|
||||
@@ -916,25 +916,27 @@ int sqlite3_wasm_db_export_chunked( sqlite3* pDb,
|
||||
}
|
||||
|
||||
/*
|
||||
** A proxy for sqlite3_serialize() which serializes the "main" schema
|
||||
** A proxy for sqlite3_serialize() which serializes the schema zSchema
|
||||
** of pDb, placing the serialized output in pOut and nOut. nOut may be
|
||||
** NULL. If pDb or pOut are NULL then SQLITE_MISUSE is returned. If
|
||||
** allocation of the serialized copy fails, SQLITE_NOMEM is returned.
|
||||
** On success, 0 is returned and `*pOut` will contain a pointer to the
|
||||
** memory unless mFlags includes SQLITE_SERIALIZE_NOCOPY and the
|
||||
** database has no contiguous memory representation, in which case
|
||||
** `*pOut` will be NULL but 0 will be returned.
|
||||
** NULL. If zSchema is NULL then "main" is assumed. If pDb or pOut are
|
||||
** NULL then SQLITE_MISUSE is returned. If allocation of the
|
||||
** serialized copy fails, SQLITE_NOMEM is returned. On success, 0 is
|
||||
** returned and `*pOut` will contain a pointer to the memory unless
|
||||
** mFlags includes SQLITE_SERIALIZE_NOCOPY and the database has no
|
||||
** contiguous memory representation, in which case `*pOut` will be
|
||||
** NULL but 0 will be returned.
|
||||
**
|
||||
** If `*pOut` is not NULL, the caller is responsible for passing it to
|
||||
** sqlite3_free() to free it.
|
||||
*/
|
||||
SQLITE_WASM_KEEP
|
||||
int sqlite3_wasm_db_serialize( sqlite3 *pDb, unsigned char **pOut,
|
||||
int sqlite3_wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
unsigned char **pOut,
|
||||
sqlite3_int64 *nOut, unsigned int mFlags ){
|
||||
unsigned char * z;
|
||||
if( !pDb || !pOut ) return SQLITE_MISUSE;
|
||||
if(nOut) *nOut = 0;
|
||||
z = sqlite3_serialize(pDb, "main", nOut, mFlags);
|
||||
z = sqlite3_serialize(pDb, zSchema ? zSchema : "main", nOut, mFlags);
|
||||
if( z || (SQLITE_SERIALIZE_NOCOPY & mFlags) ){
|
||||
*pOut = z;
|
||||
return 0;
|
||||
@@ -1108,9 +1110,6 @@ int sqlite3_wasm_init_wasmfs(const char *zMountPoint){
|
||||
/** It's not enough to instantiate the backend. We have to create a
|
||||
mountpoint in the VFS and attach the backend to it. */
|
||||
if( pOpfs && 0!=access(zMountPoint, F_OK) ){
|
||||
/* mkdir() simply hangs when called from fiddle app. Cause is
|
||||
not yet determined but the hypothesis is an init-order
|
||||
issue. */
|
||||
/* Note that this check and is not robust but it will
|
||||
hypothetically suffice for the transient wasm-based virtual
|
||||
filesystem we're currently running in. */
|
||||
|
||||
+1525
File diff suppressed because it is too large
Load Diff
@@ -1101,7 +1101,7 @@ self.WhWasmUtilInstaller = function(target){
|
||||
property and a forEach() method. A block of memory list.length
|
||||
entries long is allocated and each pointer-sized block of that
|
||||
memory is populated with a scopedAllocCString() conversion of the
|
||||
(''+value) of each element. Returns a pointer to the start of the
|
||||
(""+value) of each element. Returns a pointer to the start of the
|
||||
list, suitable for passing as the 2nd argument to a C-style
|
||||
main() function.
|
||||
|
||||
|
||||
+33
-27
@@ -6,35 +6,38 @@
|
||||
# 'make dist' rules for creating a distribution archive of the WASM/JS
|
||||
# pieces, noting that we only build a dist of the built files, not the
|
||||
# numerous pieces required to build them.
|
||||
#
|
||||
# Use 'make snapshot' to create "snapshot" releases. They use a
|
||||
# distinctly different zip file and top directory name to distinguish
|
||||
# them from release builds.
|
||||
#######################################################################
|
||||
MAKEFILE.dist := $(lastword $(MAKEFILE_LIST))
|
||||
|
||||
########################################################################
|
||||
# Chicken/egg situation: we need $(bin.version-info) to get the version
|
||||
# info for the archive name, but that binary may not yet be built, and
|
||||
# won't be built until we expand the dependencies. We have to use a
|
||||
# temporary name for the archive.
|
||||
dist-name = sqlite-wasm-TEMP
|
||||
#ifeq (0,1)
|
||||
# $(info WARNING *******************************************************************)
|
||||
# $(info ** Be sure to create the desired build configuration before creating the)
|
||||
# $(info ** distribution archive. Use one of the following targets to do so:)
|
||||
# $(info **)
|
||||
# $(info ** o2: builds with -O2, resulting in the fastest builds)
|
||||
# $(info ** oz: builds with -Oz, resulting in the smallest builds)
|
||||
# $(info /WARNING *******************************************************************)
|
||||
#endif
|
||||
# Chicken/egg situation: we need $(bin.version-info) to get the
|
||||
# version info for the archive name, but that binary may not yet be
|
||||
# built, and won't be built until we expand the dependencies. Thus we
|
||||
# have to use a temporary name for the archive until we can get
|
||||
# that binary built.
|
||||
ifeq (,$(filter snapshot,$(MAKECMDGOALS)))
|
||||
dist-name-prefix := sqlite-wasm
|
||||
else
|
||||
dist-name-prefix := sqlite-wasm-snapshot-$(shell /usr/bin/date +%Y%m%d)
|
||||
endif
|
||||
dist-name := $(dist-name-prefix)-TEMP
|
||||
|
||||
########################################################################
|
||||
# dist.build must be the name of a target which triggers the
|
||||
# build of the files to be packed into the dist archive. The
|
||||
# intention is that it be one of (o0, o1, o2, o3, os, oz), each of
|
||||
# which uses like-named -Ox optimization level flags. The o2 target
|
||||
# provides the best overall runtime speeds. The oz target provides
|
||||
# slightly slower speeds (roughly 10%) with significantly smaller WASM
|
||||
# file sizes. Note that -O2 (the o2 target) results in faster binaries
|
||||
# than both -O3 and -Os (the o3 and os targets) in all tests run to
|
||||
# date.
|
||||
# dist.build must be the name of a target which triggers the build of
|
||||
# the files to be packed into the dist archive. The intention is that
|
||||
# it be one of (o0, o1, o2, o3, os, oz), each of which uses like-named
|
||||
# -Ox optimization level flags. The o2 target provides the best
|
||||
# overall runtime speeds. The oz target provides slightly slower
|
||||
# speeds (roughly 10%) with significantly smaller WASM file
|
||||
# sizes. Note that -O2 (the o2 target) results in faster binaries than
|
||||
# both -O3 and -Os (the o3 and os targets) in all tests run to
|
||||
# date. Our general policy is that we want the smallest binaries for
|
||||
# dist zip files, so use the oz build unless there is a compelling
|
||||
# reason not to.
|
||||
dist.build ?= oz
|
||||
|
||||
dist-dir.top := $(dist-name)
|
||||
@@ -42,7 +45,8 @@ dist-dir.jswasm := $(dist-dir.top)/$(notdir $(dir.dout))
|
||||
dist-dir.common := $(dist-dir.top)/common
|
||||
dist.top.extras := \
|
||||
demo-123.html demo-123-worker.html demo-123.js \
|
||||
tester1.html tester1-worker.html tester1.js \
|
||||
tester1.html tester1-worker.html tester1-esm.html \
|
||||
tester1.js tester1.mjs \
|
||||
demo-jsstorage.html demo-jsstorage.js \
|
||||
demo-worker1.html demo-worker1.js \
|
||||
demo-worker1-promiser.html demo-worker1-promiser.js
|
||||
@@ -51,7 +55,7 @@ dist.common.extras := \
|
||||
$(wildcard $(dir.common)/*.css) \
|
||||
$(dir.common)/SqliteTestUtil.js
|
||||
|
||||
.PHONY: dist
|
||||
.PHONY: dist snapshot
|
||||
########################################################################
|
||||
# dist: create the end-user deliverable archive.
|
||||
#
|
||||
@@ -78,10 +82,12 @@ dist: \
|
||||
@cp -p $(dist.jswasm.extras) $(dist-dir.jswasm)
|
||||
@$(bin.stripccomments) -k -k < $(sqlite3.js) \
|
||||
> $(dist-dir.jswasm)/$(notdir $(sqlite3.js))
|
||||
@$(bin.stripccomments) -k -k < $(sqlite3.mjs) \
|
||||
> $(dist-dir.jswasm)/$(notdir $(sqlite3.mjs))
|
||||
@cp -p $(dist.common.extras) $(dist-dir.common)
|
||||
@set -e; \
|
||||
vnum=$$($(bin.version-info) --download-version); \
|
||||
vdir=sqlite-wasm-$$vnum; \
|
||||
vdir=$(dist-name-prefix)-$$vnum; \
|
||||
arczip=$$vdir.zip; \
|
||||
echo "Making $$arczip ..."; \
|
||||
rm -fr $$arczip $$vdir; \
|
||||
@@ -91,7 +97,7 @@ dist: \
|
||||
ls -la $$arczip; \
|
||||
set +e; \
|
||||
unzip -lv $$arczip || echo "Missing unzip app? Not fatal."
|
||||
|
||||
snapshot: dist
|
||||
# We need a separate `clean` rule to account for weirdness in
|
||||
# a sub-make, where we get a copy of the $(dist-name) dir
|
||||
# copied into the new $(dist-name) dir.
|
||||
|
||||
@@ -58,12 +58,12 @@ fiddle.SOAP.js := $(dir.fiddle)/$(notdir $(SOAP.js))
|
||||
$(fiddle.SOAP.js): $(SOAP.js)
|
||||
cp $< $@
|
||||
|
||||
$(eval $(call call-make-pre-js,fiddle-module))
|
||||
$(eval $(call call-make-pre-js,fiddle-module,vanilla))
|
||||
$(fiddle-module.js): $(MAKEFILE) $(MAKEFILE.fiddle) \
|
||||
$(EXPORTED_FUNCTIONS.fiddle) \
|
||||
$(fiddle.cses) $(pre-post-fiddle-module.deps) $(fiddle.SOAP.js)
|
||||
$(fiddle.cses) $(pre-post-fiddle-module.deps.vanilla) $(fiddle.SOAP.js)
|
||||
$(emcc.bin) -o $@ $(fiddle.emcc-flags) \
|
||||
$(pre-post-common.flags) $(pre-post-fiddle-module.flags) \
|
||||
$(pre-post-fiddle-module.flags.vanilla) \
|
||||
$(fiddle.cses)
|
||||
$(maybe-wasm-strip) $(fiddle-module.wasm)
|
||||
gzip < $@ > $@.gz
|
||||
|
||||
@@ -56,6 +56,16 @@
|
||||
utility code.</li>
|
||||
<li><a href='tester1-worker.html'>tester1-worker</a>: same thing
|
||||
but running in a Worker.</li>
|
||||
<li><a href='tester1-esm.html'>tester1-esm</a>: same as
|
||||
<code>tester1</code> but loads sqlite3 in the main thread via
|
||||
an ES6 module.
|
||||
</li>
|
||||
<li><a href='tester1-worker.html?esm'>tester1-worker?esm</a>:
|
||||
same as <code>tester1-esm</code> but loads a Worker Module which
|
||||
then loads the sqlite3 API via an ES6 module. Note that
|
||||
not all browsers permit loading modules in Worker
|
||||
threads.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Higher-level apps and demos...
|
||||
|
||||
+14
-5
@@ -32,10 +32,6 @@
|
||||
and testing is currently done against a dev-channel release
|
||||
of Chrome (v107 as of 2022-09-26).
|
||||
</li>
|
||||
<li>Whether or not WASMFS/OPFS support is enabled on any given
|
||||
page may depend on build-time options which are <em>off by
|
||||
default</em>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>The tests and demos...
|
||||
@@ -46,7 +42,17 @@
|
||||
regression tests for the various APIs and surrounding
|
||||
utility code.</li>
|
||||
<li><a href='tester1-worker.html'>tester1-worker</a>: same thing
|
||||
but running in a Worker.</li>
|
||||
but running in a Worker.</li>
|
||||
<li><a href='tester1-esm.html'>tester1-esm</a>: same as
|
||||
<code>tester1</code> but loads sqlite3 in the main thread via
|
||||
an ES6 module.
|
||||
</li>
|
||||
<li><a href='tester1-worker.html?esm'>tester1-worker?esm</a>:
|
||||
same as <code>tester1-esm</code> but loads a Worker Module which
|
||||
then loads the sqlite3 API via an ES6 module. Note that
|
||||
not all browsers permit loading modules in Worker
|
||||
threads.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>High-level apps and demos...
|
||||
@@ -98,6 +104,9 @@
|
||||
synchronous sqlite3_vfs interface and the async OPFS
|
||||
impl.
|
||||
</li>
|
||||
<li><a href='tests/opfs/concurrency/index.html'>OPFS concurrency</a>
|
||||
tests using multiple workers.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!--li><a href='x.html'></a></li-->
|
||||
|
||||
@@ -361,7 +361,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
||||
framework's native format or in Emscripten format.
|
||||
*/
|
||||
const __memberSignature = function f(obj,memberName,emscriptenFormat=false){
|
||||
if(!f._) f._ = (x)=>x.replace(/[^vipPsjrd]/g,'').replace(/[pPs]/g,'i');
|
||||
if(!f._) f._ = (x)=>x.replace(/[^vipPsjrd]/g,"").replace(/[pPs]/g,'i');
|
||||
const m = __lookupMember(obj.structInfo, memberName, true);
|
||||
return emscriptenFormat ? f._(m.signature) : m.signature;
|
||||
};
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
<div id='list-compile-options' class='pseudolist wide2'></div>
|
||||
|
||||
</div><!-- .initially-hidden -->
|
||||
<script src="jswasm/sqlite3.js">/* This tag MUST be in side the
|
||||
<script src="jswasm/sqlite3.js">/* This tag MUST be inside the
|
||||
fossil-doc block so that this part can work without modification in
|
||||
the wasm docs repo. */</script>
|
||||
<script>(async function(){
|
||||
|
||||
@@ -169,7 +169,11 @@
|
||||
return str+a.join(' ');
|
||||
};
|
||||
|
||||
const W = new Worker("speedtest1-worker.js?sqlite3.dir=jswasm");
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const W = new Worker(
|
||||
"speedtest1-worker.js?sqlite3.dir=jswasm"+
|
||||
(urlParams.has('opfs-verbose') ? '&opfs-verbose' : '')
|
||||
);
|
||||
const mPost = function(msgType,payload){
|
||||
W.postMessage({type: msgType, data: payload});
|
||||
};
|
||||
@@ -179,7 +183,6 @@
|
||||
const eLinkMainThread = E('#link-main-thread');
|
||||
const eLinkWasmfs = E('#link-wasmfs');
|
||||
const eLinkKvvfs = E('#link-kvvfs');
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const getSelectedFlags = ()=>{
|
||||
const f = Array.prototype.map.call(eFlags.selectedOptions, (v)=>v.value);
|
||||
[
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<link rel="stylesheet" href="../common/emscripten.css"/>
|
||||
<link rel="stylesheet" href="../common/testing.css"/>
|
||||
<title>sqlite3 tester #1 (Worker thread)</title>
|
||||
<title>sqlite3 tester #1: Worker thread</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: monospace;
|
||||
@@ -14,7 +14,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='color-target'>sqlite3 WASM/JS tester #1 (Worker thread)</h1>
|
||||
<h1 id='color-target'>sqlite3 tester #1: Worker thread</h1>
|
||||
<div>See <a href='tester1.html' target='tester1.html'>tester1.html</a>
|
||||
for the UI-thread variant.</div>
|
||||
<div class='input-wrapper'>
|
||||
@@ -40,7 +40,20 @@
|
||||
};
|
||||
cbReverse.addEventListener('change',cbReverseIt,true);
|
||||
cbReverseIt();
|
||||
const w = new Worker("tester1.js?sqlite3.dir=jswasm");
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const workerArgs = [];
|
||||
if(urlParams.has('esm')){
|
||||
logHtml('warning',"Attempting to run an ES6 Worker Module, "+
|
||||
"which is not supported by all browsers! "+
|
||||
"e.g. Firefox (as of 2022-11) cannot do this.");
|
||||
workerArgs.push("tester1.mjs",{type:"module"});
|
||||
document.querySelectorAll('title,#color-target').forEach((e)=>{
|
||||
e.innerText = "sqlite3 tester #1: ES6 Worker Module";
|
||||
});
|
||||
}else{
|
||||
workerArgs.push("tester1.js?sqlite3.dir=jswasm");
|
||||
}
|
||||
const w = new Worker(...workerArgs);
|
||||
w.onmessage = function({data}){
|
||||
switch(data.type){
|
||||
case 'log':
|
||||
|
||||
@@ -6,7 +6,13 @@
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<link rel="stylesheet" href="common/emscripten.css"/>
|
||||
<link rel="stylesheet" href="common/testing.css"/>
|
||||
<title>sqlite3 tester #1 (UI thread)</title>
|
||||
<title>sqlite3 tester #1:
|
||||
//#if target=es6-module
|
||||
ES6 Module in UI thread
|
||||
//#else
|
||||
UI thread
|
||||
//#endif
|
||||
</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: monospace;
|
||||
@@ -14,7 +20,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='color-target'>sqlite3 WASM/JS tester #1 (UI thread)</h1>
|
||||
<h1 id='color-target'></h1>
|
||||
<div>See <a href='tester1-worker.html' target='tester1-worker.html'>tester1-worker.html</a>
|
||||
for the Worker-thread variant.</div>
|
||||
<div class='input-wrapper'>
|
||||
@@ -22,7 +28,15 @@
|
||||
<label for='cb-log-reverse'>Reverse log order?</label>
|
||||
</div>
|
||||
<div id='test-output'></div>
|
||||
<script>(function(){
|
||||
document.querySelector('h1').innerHTML =
|
||||
document.querySelector('title').innerHTML;
|
||||
})();</script>
|
||||
//#if target=es6-module
|
||||
<script src="tester1.mjs" type="module"></script>
|
||||
//#else
|
||||
<script src="jswasm/sqlite3.js"></script>
|
||||
<script src="tester1.js"></script>
|
||||
//#endif
|
||||
</body>
|
||||
</html>
|
||||
@@ -29,8 +29,25 @@
|
||||
a db in an early test and close it in a later test. Each test gets
|
||||
passed the sqlite3 namespace object as its only argument.
|
||||
*/
|
||||
/*
|
||||
This file is intended to be processed by c-pp to inject (or not)
|
||||
code specific to ES6 modules which is illegal in non-module code.
|
||||
|
||||
Non-ES6 module build and ES6 module for the main-thread:
|
||||
|
||||
./c-pp -f tester1.c-pp.js -o tester1.js
|
||||
|
||||
ES6 worker module build:
|
||||
|
||||
./c-pp -f tester1.c-pp.js -o tester1-esm.js -Dtarget=es6-module
|
||||
*/
|
||||
//#if target=es6-module
|
||||
import {default as sqlite3InitModule} from './jswasm/sqlite3.mjs';
|
||||
self.sqlite3InitModule = sqlite3InitModule;
|
||||
//#else
|
||||
'use strict';
|
||||
(function(){
|
||||
//#endif
|
||||
(function(self){
|
||||
/**
|
||||
Set up our output channel differently depending
|
||||
on whether we are running in a worker thread or
|
||||
@@ -342,8 +359,20 @@
|
||||
throw new sqlite3.WasmAllocError;
|
||||
}catch(e){
|
||||
T.assert(e instanceof Error)
|
||||
.assert(e instanceof sqlite3.WasmAllocError);
|
||||
.assert(e instanceof sqlite3.WasmAllocError)
|
||||
.assert("Allocation failed." === e.message);
|
||||
}
|
||||
try {
|
||||
throw new sqlite3.WasmAllocError("test",{
|
||||
cause: 3
|
||||
});
|
||||
}catch(e){
|
||||
T.assert(3 === e.cause)
|
||||
.assert("test" === e.message);
|
||||
}
|
||||
try {throw new sqlite3.WasmAllocError("test","ing",".")}
|
||||
catch(e){T.assert("test ing ." === e.message)}
|
||||
|
||||
try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) }
|
||||
catch(e){ T.assert('SQLITE_SCHEMA' === e.message) }
|
||||
try{ sqlite3.SQLite3Error.toss(capi.SQLITE_CORRUPT,{cause: true}) }
|
||||
@@ -1134,6 +1163,16 @@
|
||||
.assert(0 === capi.sqlite3_errmsg(db.pointer).indexOf("Invalid SQL"))
|
||||
.assert(dbFile === db.dbFilename())
|
||||
.assert(!db.dbFilename('nope'));
|
||||
//Sanity check DB.checkRc()...
|
||||
let ex;
|
||||
try{db.checkRc(rc)}
|
||||
catch(e){ex = e}
|
||||
T.assert(ex instanceof sqlite3.SQLite3Error)
|
||||
.assert(0===ex.message.indexOf("sqlite3 result code"))
|
||||
.assert(ex.message.indexOf("Invalid SQL")>0);
|
||||
T.assert(db === db.checkRc(0))
|
||||
.assert(db === sqlite3.oo1.DB.checkRc(db,0))
|
||||
.assert(null === sqlite3.oo1.DB.checkRc(null,0))
|
||||
})
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@@ -1805,7 +1844,8 @@
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
log("Loading and initializing sqlite3 WASM module...");
|
||||
if(!isUIThread()){
|
||||
if(!self.sqlite3InitModule && !isUIThread()){
|
||||
/* Vanilla worker, as opposed to an ES6 module worker */
|
||||
/*
|
||||
If sqlite3.js is in a directory other than this script, in order
|
||||
to get sqlite3.js to resolve sqlite3.wasm properly, we have to
|
||||
@@ -1849,4 +1889,5 @@
|
||||
}
|
||||
TestUtil.runTests(sqlite3);
|
||||
});
|
||||
})();
|
||||
})(self);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<link rel="stylesheet" href="../../../common/testing.css"/>
|
||||
<title>sqlite3 OPFS Worker concurrency tester</title>
|
||||
<style>
|
||||
body { display: revert; }
|
||||
body > * {}
|
||||
#test-output {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1></h1>
|
||||
<p>
|
||||
OPFS concurrency tester using multiple independent Workers.
|
||||
Disclaimer: concurrency in OPFS is currently a pain point
|
||||
and timing/concurrency mitigation in this environment is
|
||||
highly unpredictable!
|
||||
</p>
|
||||
<p>
|
||||
URL flags: pass a number of workers using
|
||||
the <code>workers=N</code> URL flag. Set the time between each
|
||||
workload with <code>interval=N</code> (milliseconds). Set the
|
||||
number of worker iterations with <code>iterations=N</code>.
|
||||
Enable OPFS VFS verbosity with <code>verbose=1-3</code> (output
|
||||
goes to the dev console). Enable/disable "unlock ASAP" mode
|
||||
(higher concurrency, lower speed)
|
||||
with <code>unlock-asap=0-1</code>.
|
||||
</p>
|
||||
<p>Achtung: if it does not start to do anything within a couple of
|
||||
seconds, check the dev console: Chrome often fails with "cannot allocate
|
||||
WasmMemory" at startup. Closing and re-opening the tab usually resolves
|
||||
it.
|
||||
</p>
|
||||
<div class='input-wrapper'>
|
||||
<input type='checkbox' id='cb-log-reverse'>
|
||||
<label for='cb-log-reverse'>Reverse log order?</label>
|
||||
</div>
|
||||
<div id='test-output'></div>
|
||||
<script>(function(){
|
||||
document.querySelector('h1').innerHTML =
|
||||
document.querySelector('title').innerHTML;
|
||||
})();</script>
|
||||
<script src="test.js?sqlite3.dir=../../../jswasm"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,133 @@
|
||||
(async function(self){
|
||||
|
||||
const logCss = (function(){
|
||||
const mapToString = (v)=>{
|
||||
switch(typeof v){
|
||||
case 'number': case 'string': case 'boolean':
|
||||
case 'undefined': case 'bigint':
|
||||
return ''+v;
|
||||
default: break;
|
||||
}
|
||||
if(null===v) return 'null';
|
||||
if(v instanceof Error){
|
||||
v = {
|
||||
message: v.message,
|
||||
stack: v.stack,
|
||||
errorClass: v.name
|
||||
};
|
||||
}
|
||||
return JSON.stringify(v,undefined,2);
|
||||
};
|
||||
const normalizeArgs = (args)=>args.map(mapToString);
|
||||
const logTarget = document.querySelector('#test-output');
|
||||
const logCss = function(cssClass,...args){
|
||||
const ln = document.createElement('div');
|
||||
if(cssClass){
|
||||
for(const c of (Array.isArray(cssClass) ? cssClass : [cssClass])){
|
||||
ln.classList.add(c);
|
||||
}
|
||||
}
|
||||
ln.append(document.createTextNode(normalizeArgs(args).join(' ')));
|
||||
logTarget.append(ln);
|
||||
};
|
||||
const cbReverse = document.querySelector('#cb-log-reverse');
|
||||
const cbReverseKey = 'tester1:cb-log-reverse';
|
||||
const cbReverseIt = ()=>{
|
||||
logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
|
||||
localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0);
|
||||
};
|
||||
cbReverse.addEventListener('change', cbReverseIt, true);
|
||||
if(localStorage.getItem(cbReverseKey)){
|
||||
cbReverse.checked = !!(+localStorage.getItem(cbReverseKey));
|
||||
}
|
||||
cbReverseIt();
|
||||
return logCss;
|
||||
})();
|
||||
const stdout = (...args)=>logCss('',...args);
|
||||
const stderr = (...args)=>logCss('error',...args);
|
||||
|
||||
const wait = async (ms)=>{
|
||||
return new Promise((resolve)=>setTimeout(resolve,ms));
|
||||
};
|
||||
|
||||
const urlArgsJs = new URL(document.currentScript.src).searchParams;
|
||||
const urlArgsHtml = new URL(self.location.href).searchParams;
|
||||
const options = Object.create(null);
|
||||
options.sqlite3Dir = urlArgsJs.get('sqlite3.dir');
|
||||
options.workerCount = (
|
||||
urlArgsHtml.has('workers') ? +urlArgsHtml.get('workers') : 3
|
||||
) || 4;
|
||||
options.opfsVerbose = (
|
||||
urlArgsHtml.has('verbose') ? +urlArgsHtml.get('verbose') : 1
|
||||
) || 1;
|
||||
options.interval = (
|
||||
urlArgsHtml.has('interval') ? +urlArgsHtml.get('interval') : 750
|
||||
) || 1000;
|
||||
options.iterations = (
|
||||
urlArgsHtml.has('iterations') ? +urlArgsHtml.get('iterations') : 10
|
||||
) || 10;
|
||||
options.unlockAsap = (
|
||||
urlArgsHtml.has('unlock-asap') ? +urlArgsHtml.get('unlock-asap') : 0
|
||||
) || 0;
|
||||
const workers = [];
|
||||
workers.post = (type,...args)=>{
|
||||
for(const w of workers) w.postMessage({type, payload:args});
|
||||
};
|
||||
workers.counts = {loaded: 0, passed: 0, failed: 0};
|
||||
const checkFinished = function(){
|
||||
if(workers.counts.passed + workers.counts.failed !== workers.length){
|
||||
return;
|
||||
}
|
||||
if(workers.counts.failed>0){
|
||||
logCss('tests-fail',"Finished with",workers.counts.failed,"failure(s).");
|
||||
}else{
|
||||
logCss('tests-pass',"All",workers.length,"workers finished.");
|
||||
}
|
||||
};
|
||||
workers.onmessage = function(msg){
|
||||
msg = msg.data;
|
||||
const prefix = 'Worker #'+msg.worker+':';
|
||||
switch(msg.type){
|
||||
case 'loaded':
|
||||
stdout(prefix,"loaded");
|
||||
if(++workers.counts.loaded === workers.length){
|
||||
stdout("All",workers.length,"workers loaded. Telling them to run...");
|
||||
workers.post('run');
|
||||
}
|
||||
break;
|
||||
case 'stdout': stdout(prefix,...msg.payload); break;
|
||||
case 'stderr': stderr(prefix,...msg.payload); break;
|
||||
case 'error': stderr(prefix,"ERROR:",...msg.payload); break;
|
||||
case 'finished':
|
||||
++workers.counts.passed;
|
||||
logCss('tests-pass',prefix,...msg.payload);
|
||||
checkFinished();
|
||||
break;
|
||||
case 'failed':
|
||||
++workers.counts.failed;
|
||||
logCss('tests-fail',prefix,"FAILED:",...msg.payload);
|
||||
checkFinished();
|
||||
break;
|
||||
default: logCss('error',"Unhandled message type:",msg); break;
|
||||
}
|
||||
};
|
||||
|
||||
stdout("Launching",options.workerCount,"workers. Options:",options);
|
||||
workers.uri = (
|
||||
'worker.js?'
|
||||
+ 'sqlite3.dir='+options.sqlite3Dir
|
||||
+ '&interval='+options.interval
|
||||
+ '&iterations='+options.iterations
|
||||
+ '&opfs-verbose='+options.opfsVerbose
|
||||
+ '&opfs-unlock-asap='+options.unlockAsap
|
||||
);
|
||||
for(let i = 0; i < options.workerCount; ++i){
|
||||
stdout("Launching worker...");
|
||||
workers.push(new Worker(
|
||||
workers.uri+'&workerId='+(i+1)+(i ? '' : '&unlink-db')
|
||||
));
|
||||
}
|
||||
// Have to delay onmessage assignment until after the loop
|
||||
// to avoid that early workers get an undue head start.
|
||||
workers.forEach((w)=>w.onmessage = workers.onmessage);
|
||||
})(self);
|
||||
@@ -0,0 +1,113 @@
|
||||
importScripts(
|
||||
(new URL(self.location.href).searchParams).get('sqlite3.dir') + '/sqlite3.js'
|
||||
);
|
||||
self.sqlite3InitModule().then(async function(sqlite3){
|
||||
const urlArgs = new URL(self.location.href).searchParams;
|
||||
const options = {
|
||||
workerName: urlArgs.get('workerId') || Math.round(Math.random()*10000),
|
||||
unlockAsap: urlArgs.get('opfs-unlock-asap') || 0 /*EXPERIMENTAL*/
|
||||
};
|
||||
const wPost = (type,...payload)=>{
|
||||
postMessage({type, worker: options.workerName, payload});
|
||||
};
|
||||
const stdout = (...args)=>wPost('stdout',...args);
|
||||
const stderr = (...args)=>wPost('stderr',...args);
|
||||
if(!sqlite3.opfs){
|
||||
stderr("OPFS support not detected. Aborting.");
|
||||
return;
|
||||
}
|
||||
|
||||
const wait = async (ms)=>{
|
||||
return new Promise((resolve)=>setTimeout(resolve,ms));
|
||||
};
|
||||
|
||||
const dbName = 'concurrency-tester.db';
|
||||
if(urlArgs.has('unlink-db')){
|
||||
await sqlite3.opfs.unlink(dbName);
|
||||
stdout("Unlinked",dbName);
|
||||
}
|
||||
wPost('loaded');
|
||||
let db;
|
||||
const interval = Object.assign(Object.create(null),{
|
||||
delay: urlArgs.has('interval') ? (+urlArgs.get('interval') || 750) : 750,
|
||||
handle: undefined,
|
||||
count: 0
|
||||
});
|
||||
const finish = ()=>{
|
||||
if(db){
|
||||
if(!db.pointer) return;
|
||||
db.close();
|
||||
}
|
||||
if(interval.error){
|
||||
wPost('failed',"Ending work after interval #"+interval.count,
|
||||
"due to error:",interval.error);
|
||||
}else{
|
||||
wPost('finished',"Ending work after",interval.count,"intervals.");
|
||||
}
|
||||
};
|
||||
const run = async function(){
|
||||
db = new sqlite3.opfs.OpfsDb({
|
||||
filename: 'file:'+dbName+'?opfs-unlock-asap='+options.unlockAsap,
|
||||
flags: 'c'
|
||||
});
|
||||
sqlite3.capi.sqlite3_busy_timeout(db.pointer, 5000);
|
||||
db.transaction((db)=>{
|
||||
db.exec([
|
||||
"create table if not exists t1(w TEXT UNIQUE ON CONFLICT REPLACE,v);",
|
||||
"create table if not exists t2(w TEXT UNIQUE ON CONFLICT REPLACE,v);"
|
||||
]);
|
||||
});
|
||||
|
||||
const maxIterations =
|
||||
urlArgs.has('iterations') ? (+urlArgs.get('iterations') || 10) : 10;
|
||||
stdout("Starting interval-based db updates with delay of",interval.delay,"ms.");
|
||||
const doWork = async ()=>{
|
||||
const tm = new Date().getTime();
|
||||
++interval.count;
|
||||
const prefix = "v(#"+interval.count+")";
|
||||
stdout("Setting",prefix,"=",tm);
|
||||
try{
|
||||
db.exec({
|
||||
sql:"INSERT OR REPLACE INTO t1(w,v) VALUES(?,?)",
|
||||
bind: [options.workerName, new Date().getTime()]
|
||||
});
|
||||
//stdout("Set",prefix);
|
||||
}catch(e){
|
||||
interval.error = e;
|
||||
}
|
||||
};
|
||||
if(1){/*use setInterval()*/
|
||||
setTimeout(async function timer(){
|
||||
await doWork();
|
||||
if(interval.error || maxIterations === interval.count){
|
||||
finish();
|
||||
}else{
|
||||
setTimeout(timer, interval.delay);
|
||||
}
|
||||
}, interval.delay);
|
||||
}else{
|
||||
/*This approach provides no concurrency whatsoever: each worker
|
||||
is run to completion before any others can work.*/
|
||||
let i;
|
||||
for(i = 0; i < maxIterations; ++i){
|
||||
await doWork();
|
||||
if(interval.error) break;
|
||||
await wait(interval.ms);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}/*run()*/;
|
||||
|
||||
self.onmessage = function({data}){
|
||||
switch(data.type){
|
||||
case 'run': run().catch((e)=>{
|
||||
if(!interval.error) interval.error = e;
|
||||
finish();
|
||||
});
|
||||
break;
|
||||
default:
|
||||
stderr("Unhandled message type '"+data.type+"'.");
|
||||
break;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -67,14 +67,17 @@ sqlite3-wasmfs.jsflags += $(sqlite3-wasmfs.fsflags)
|
||||
# USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly,
|
||||
# see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth]
|
||||
sqlite3-wasmfs.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
|
||||
$(eval $(call call-make-pre-js,sqlite3-wasmfs))
|
||||
sqlite3-wasmfs.jsflags += $(pre-post-common.flags) $(pre-post-sqlite3-wasmfs.flags)
|
||||
$(eval $(call call-make-pre-js,sqlite3-wasmfs,vanilla))
|
||||
sqlite3-wasmfs.jsflags += \
|
||||
$(pre-post-common.flags.vanilla) \
|
||||
$(pre-post-sqlite3-wasmfs.flags.vanilla)
|
||||
$(sqlite3-wasmfs.js): $(sqlite3-wasm.c) \
|
||||
$(EXPORTED_FUNCTIONS.api) $(MAKEFILE) $(MAKEFILE.wasmfs) \
|
||||
$(pre-post-sqlite3-wasmfs.deps)
|
||||
$(pre-post-sqlite3-wasmfs.deps.vanilla)
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) -o $@ $(emcc_opt_full) $(emcc.flags) \
|
||||
$(sqlite3-wasmfs.cflags) $(sqlite3-wasmfs.jsflags) \
|
||||
$(pre-post-sqlite3-wasm.flags.vanilla) \
|
||||
$(sqlite3-wasm.c)
|
||||
chmod -x $(sqlite3-wasmfs.wasm)
|
||||
$(maybe-wasm-strip) $(sqlite3-wasmfs.wasm)
|
||||
@@ -98,7 +101,7 @@ $(speedtest1-wasmfs.js): $(speedtest1.cses) $(sqlite3-wasmfs.js) \
|
||||
$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) \
|
||||
$(speedtest1-wasmfs.eflags) $(speedtest1-common.eflags) \
|
||||
$(speedtest1-wasmfs.eflags) $(speedtest1.eflags.common) \
|
||||
$(pre-post-speedtest1-wasmfs.flags) \
|
||||
$(speedtest1.cflags) \
|
||||
$(sqlite3-wasmfs.cflags) \
|
||||
|
||||
@@ -390,7 +390,10 @@ TESTSRC += \
|
||||
$(TOP)/ext/fts5/fts5_tcl.c \
|
||||
$(TOP)/ext/fts5/fts5_test_mi.c \
|
||||
$(TOP)/ext/fts5/fts5_test_tok.c \
|
||||
$(TOP)/ext/rtree/test_rtreedoc.c
|
||||
$(TOP)/ext/rtree/test_rtreedoc.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/test_recover.c
|
||||
|
||||
|
||||
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
|
||||
@@ -446,9 +449,6 @@ TESTSRC2 = \
|
||||
$(TOP)/ext/misc/stmt.c \
|
||||
$(TOP)/ext/session/sqlite3session.c \
|
||||
$(TOP)/ext/session/test_session.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/test_recover.c \
|
||||
fts5.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
C Minor\sbuild\scleanups\sand\sfix\sa\sharmless\srace\scondition\sin\sthe\sOPFS\spart\sof\stester1.js.
|
||||
D 2022-11-02T14:08:59.340
|
||||
C Sync\sw/trunk,\szap\ssurplus\sspace.
|
||||
D 2022-11-28T14:11:48.108
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
F Makefile.in 78e4c4916f2c3993a8a454018745ff02094a8029d449d0c22db98f1cf8260420
|
||||
F Makefile.in c223963d7b0828f26cb62ea3e0f583d26839b7d3ef0d1cca87f35c4b222ff01b
|
||||
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
|
||||
F Makefile.msc e7a564ceec71f0d9666031d5638cf0d4f88b050b44e8df5d32125137cd259ac0
|
||||
F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e
|
||||
F VERSION 8868ddfa6e1eee218286021a94b3e22d13e550c76c72d878857547ca001de24a
|
||||
F VERSION 413ec94920a487ae32c9a2a8819544d690662d6f7c7ce025c0d0b8a1e74fa9db
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
@@ -33,7 +33,7 @@ F autoconf/tea/win/nmakehlp.c b01f822eabbe1ed2b64e70882d97d48402b42d2689a1ea0034
|
||||
F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
|
||||
F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6
|
||||
F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559
|
||||
F configure b93755fe94b3e9b2015f3c2d6c63928e8899b0a7c54e042deb843498094179da x
|
||||
F configure 9cbf6a15a4b5f2b3551a466420e5f51ce04c40aaed7f90c886402bf0b66ec110 x
|
||||
F configure.ac 48dc6bfee293eef05910faf085760f2fd79b680aa47b50e8e6a22ca40bb026bb
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
|
||||
@@ -118,7 +118,7 @@ F ext/fts5/fts5_buffer.c 3001fbabb585d6de52947b44b455235072b741038391f830d6b7292
|
||||
F ext/fts5/fts5_config.c 501e7d3566bc92766b0e11c0109a7c5a6146bc41144195459af5422f6c2078aa
|
||||
F ext/fts5/fts5_expr.c 40174a64829d30cc86e8266306ad24980f6911edd5ca0b8c1ce7821ea1341b88
|
||||
F ext/fts5/fts5_hash.c d4fb70940359f2120ccd1de7ffe64cc3efe65de9e8995b822cd536ff64c96982
|
||||
F ext/fts5/fts5_index.c 4b1ac44c665667be970df780bd8e734748047bd30a971d0bb7e884af8ac6e62c
|
||||
F ext/fts5/fts5_index.c a8ee270724ae1f958d0ce9897bcd60a5b760ecbeaa058fc8632805a283f1c20a
|
||||
F ext/fts5/fts5_main.c 6078ae86d3b813753a4f1201054550aff21a3f660e97b30f200d2b1472874151
|
||||
F ext/fts5/fts5_storage.c 76c6085239eb44424004c022e9da17a5ecd5aaec859fba90ad47d3b08f4c8082
|
||||
F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae
|
||||
@@ -289,6 +289,9 @@ F ext/misc/README.md d6dd0fe1d8af77040216798a6a2b0c46c73054d2f0ea544fbbcdccf6f23
|
||||
F ext/misc/amatch.c e3ad5532799cee9a97647f483f67f43b38796b84b5a8c60594fe782a4338f358
|
||||
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
|
||||
F ext/misc/appendvfs.c 9642c7a194a2a25dca7ad3e36af24a0a46d7702168c4ad7e59c9f9b0e16a3824
|
||||
F ext/misc/base64.c fb140039d4e15710298e28cafe12d2bdff1b927fc57540cbcd07996f82bf9800 x
|
||||
F ext/misc/base85.c ace591246855806a70e63eb6705ce8736f4b782da2137bdbb5d39cc337346e0d
|
||||
F ext/misc/basexx.c b37ea21f00deea1029d2ddc670f5701d9370ae06f7d173c5bed79d0f26a67ec1
|
||||
F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a
|
||||
F ext/misc/btreeinfo.c d28ce349b40054eaa9473e835837bad7a71deec33ba13e39f963d50933bfa0f9
|
||||
F ext/misc/carray.c b752f46411e4e47e34dce6f0c88bc8e51bb821ba9e49bfcd882506451c928f69
|
||||
@@ -316,13 +319,13 @@ F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d
|
||||
F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691
|
||||
F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196
|
||||
F ext/misc/qpvtab.c 09738419e25f603a35c0ac8bd0a04daab794f48d08a9bc07a6085b9057b99009
|
||||
F ext/misc/regexp.c 5abed0ace2d9340b42b9ab1dbe64db9c276e4e8eba38a903232b6253e05ccdaf
|
||||
F ext/misc/regexp.c 064838f7b31e90d312cce089cfafbb992034e75d359009c48886ca06c0a794b2
|
||||
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
|
||||
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
|
||||
F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
|
||||
F ext/misc/series.c 8d79354f2c3d46b95ee21272a07cf0bcabb58d1f2b06d9e7b8a31dca1dacb3e5
|
||||
F ext/misc/sha1.c 4011aef176616872b2a0d5bccf0ecfb1f7ce3fe5c3d107f3a8e949d8e1e3f08d
|
||||
F ext/misc/shathree.c 7b17615869a495659f1569ada1d8d3d21b4a24614f2746d93cc87ef7c0b6b36d
|
||||
F ext/misc/shathree.c 9b7af7b7d55b27c5bbd16548b67fe6aa47a819e71bc6a80a456144f86f4e834d
|
||||
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
|
||||
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
|
||||
@@ -374,33 +377,34 @@ F ext/rbu/rbumisc.test 329986cf5dd51890c4eb906c2f960ebb773a79a64bed90f506b7c4178
|
||||
F ext/rbu/rbumulti.test 5fb139058f37ddc5a113c5b93238de915b769b7792de41b44c983bc7c18cf5b9
|
||||
F ext/rbu/rbupartial.test f25df014b8dbe3c5345851fba6e66f79ab237f57dc201b2d5f0dbae658ae5a4c
|
||||
F ext/rbu/rbuprogress.test 857cf1f8166c83ef977edb9ef4fc42d80f71fbd798652b46ae2f3a7031870f8d
|
||||
F ext/rbu/rburename.test a9b4aea612352b74c45de1757edd2ecb2079348b1d4cc734572dc29e55b1b376
|
||||
F ext/rbu/rburesume.test dbdc4ca504e9c76375a69e5f0d91205db967dcc509a5166ca80231f8fda49eb1
|
||||
F ext/rbu/rbusave.test f4190a1a86fccf84f723af5c93813365ae33feda35845ba107b59683d1cdd926
|
||||
F ext/rbu/rbusplit.test b37e7b40b38760881dc9c854bd40b4744c6b6cd74990754eca3bda0f407051e8
|
||||
F ext/rbu/rbutemplimit.test 05ceefa90a2e26a99f40dd48282ed63a00df5e59c1f2bfd479c143e201a1b0ba
|
||||
F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697d79f73534
|
||||
F ext/rbu/rbuvacuum2.test 886add83fd74bcb02e6dd016ae5b585367bd58c5d0694c9d9ca7bdb1d1f578c2
|
||||
F ext/rbu/rbuvacuum2.test 2643b58f4d8d3573db0f93faae18805a35ab162b4c55ff6b656062ff432ed55b
|
||||
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
|
||||
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
|
||||
F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291
|
||||
F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
|
||||
F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
|
||||
F ext/recover/dbdata.c 3ae32f9b7f02a141889b9075beb87895a826d0fcf3b702b8251cbb0dd3c91a83
|
||||
F ext/recover/recover1.test 522e2c3353734dbef9118f08e47209470e50308ba35ce818fed00b8e265aee44
|
||||
F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c
|
||||
F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60
|
||||
F ext/recover/recovercorrupt.test 6540aae95e17398dd70b44518367fd56588c44962cb276d2623a0fedba9efe9e
|
||||
F ext/recover/recovercorrupt2.test a7e0735cefb79de7959ebd1ee6d963f9505305fe7983ac58394eb5f5aa9236c7
|
||||
F ext/recover/recoverfault.test 3a0a32b9fc216592b97775d69220695b0926980c0f7424b7a59144e47d7cb568
|
||||
F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72ddb661669be9020d36b
|
||||
F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0
|
||||
F ext/recover/recoverpgsz.test 93e970eab05e4e89f8fd6b1bd23f9ec137ea09857e66ba0d4d27a83cd1ba4a89
|
||||
F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8
|
||||
F ext/recover/recoverslowidx.test f356bb9fba7ffd6fc50e045e419464f0129ac6e24decf6e919584f79c3493727
|
||||
F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f
|
||||
F ext/recover/sqlite3recover.c d2feca815f489f3beed4af94b916e0cba046937b9cc760b0f2baacf1ae515fa2
|
||||
F ext/rbu/sqlite3rbu.c c4ba7901b2d3e0c7845f30840e3ffb35c6f999d6da0d80f121866491f982794c
|
||||
F ext/rbu/sqlite3rbu.h 02d981e2d39c151391759e1a400e29c7388730812957ac3db8dad7f6c9f9cfc8
|
||||
F ext/rbu/test_rbu.c ee6ede75147bc081fe9bc3931e6b206277418d14d3fbceea6fdc6216d9b47055
|
||||
F ext/recover/dbdata.c 8f1f75d636431de69d7977ec50fc41bfdd0c48c510d5ee7eae0cbd4164e1429a
|
||||
F ext/recover/recover1.test 02004eb8f9ec2825ba77e24742c18e45162cb21d27e76a3a435b83a759a1131a
|
||||
F ext/recover/recover_common.tcl a61306c1eb45c0c3fc45652c35b2d4ec19729e340bdf65a272ce4c229cefd85a
|
||||
F ext/recover/recoverclobber.test 3ba6c0c373c5c63d17e82eced64c05c57ccaf26c1abe1ca7141334022a79f32e
|
||||
F ext/recover/recovercorrupt.test 64c081ad1200ae77b447da99eb724785d6bf71715f394543dc7689642e92bf49
|
||||
F ext/recover/recovercorrupt2.test 74bef7dd2d7dd4856f3da21be6e213d27da44827e0f5f0946ca0325b46d163ed
|
||||
F ext/recover/recoverfault.test 9d9f88eeb222615a25e7514f234c950d46bee20d24cd8db49d8fff8d650dcfe1
|
||||
F ext/recover/recoverfault2.test 730e7371bcda769554d15460cb23126abba1be8eca9539ccabf63623e7bb7e09
|
||||
F ext/recover/recoverold.test 68db3d6f85dd2b98e785b6c4da4f5eea4bbe52ccf6674d9a94c7506dc92596aa
|
||||
F ext/recover/recoverpgsz.test 3658ab8e68475b1bb87d6af88baa04551c84b73280a566a1be847182410ffc58
|
||||
F ext/recover/recoverrowid.test f948bf4024a5f41b0e21b8af80c60564c5b5d78c05a8d64fc00787715ff9f45f
|
||||
F ext/recover/recoverslowidx.test 5205a9742dd9490ee99950dabb622307355ef1662dea6a3a21030057bfd81411
|
||||
F ext/recover/recoversql.test e66d01f95302a223bcd3fd42b5ee58dc2b53d70afa90b0d00e41e4b8eab20486
|
||||
F ext/recover/sqlite3recover.c 1e4de9cd30daa67e5c1cd0cafb764ddf183087ce037ab47b7c1a7e2921c90d37
|
||||
F ext/recover/sqlite3recover.h 011c799f02deb70ab685916f6f538e6bb32c4e0025e79bfd0e24ff9c74820959
|
||||
F ext/recover/test_recover.c 61ec931e47abca6b2210f46239cafd9f3060741605e3d3c45a7c7a53f63dd957
|
||||
F ext/recover/test_recover.c 1a34e2d04533d919a30ae4d5caeb1643f6684e9ccd7597ca27721d8af81f4ade
|
||||
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
|
||||
F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996
|
||||
F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890
|
||||
@@ -487,35 +491,36 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
||||
F ext/wasm/GNUmakefile 78b64d110e66e18709c11437a74c29bc7855d85a713217db99127c7f304a332f
|
||||
F ext/wasm/GNUmakefile bc1696a1189f4c571b3d878b8f3a67c1f4b52c222f52d356027a5a0c707337c7
|
||||
F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20
|
||||
F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 9120c2f8f51fa85f46dcf4dcb6b12f4a807d428f6089b99cdb08d8ddfcfd88b2
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b4d68c97d14944b48d55e06aa44f544a6f56a7fa2bcb6f9e030936a5b2a9479a
|
||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||
F ext/wasm/api/README.md 1350088aee90e959ad9a94fab1bb6bcb5e99d4d27f976db389050f54f2640c78
|
||||
F ext/wasm/api/extern-post-js.js f3dc4219a2a1f183d98452dcbd55a0c5351b759eccca75480a92473974d8b047
|
||||
F ext/wasm/api/README.md 29276a845e57004e82efba61fa5866fd05f9137380a1dc26dc4c6d65264cd81c
|
||||
F ext/wasm/api/extern-post-js.js 59e52f579cd3a332d73dae94c91b9579daafb10dd6ada03803f1afa6bdad7689
|
||||
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
|
||||
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
|
||||
F ext/wasm/api/post-js-header.js d6ab3dfef4a06960d28a7eaa338d4e2a1a5981e9b38718168bbde8fdb2a439b8
|
||||
F ext/wasm/api/pre-js.js 287e462f969342b032c03900e668099fa1471d852df7a472de5bc349161d9c04
|
||||
F ext/wasm/api/pre-js.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js ecdc69dbfccfe26146f04799fcfd4a6f5790d46e7e3b9b6e9b0491f92ed8ae34
|
||||
F ext/wasm/api/sqlite3-api-glue.js 9cfa26a9818532c80c2555bc98615de3b170d5db0cf4b141cc3aa83c33c8758f
|
||||
F ext/wasm/api/sqlite3-api-oo1.js e9a83489bbb4838ce0aee46eaaa9350e0e25a5b926b565e4f5ae8e840e4fbaed
|
||||
F ext/wasm/api/sqlite3-api-opfs.js cdcbb57acc66f4569ac9e18f9d13d5a3657d8aae195725c6324943da56c1005d
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 1f97261b4d8a60a48f30ee41261e1c4a0c3efff35ae08d7ece243fcf49b3eee3
|
||||
F ext/wasm/api/sqlite3-api-worker1.js cac2f5c63f950f69b5249c9880d4cd385e914c354c459d4096ed5dbb1248de76
|
||||
F ext/wasm/api/sqlite3-api-glue.js 056f44b82c126358a0175e08a892d56fadfce177b0d7a0012502a6acf67ea6d5
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 06ad2079368e16cb9f182c18cd37bdc3932536856dff4f60582d0ca5f6c491a8
|
||||
F ext/wasm/api/sqlite3-api-opfs.js e98a8bd67dea8c20b0ec17332698b44658a6fbc4be18dd87fab2ce05284a10d7
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 7fce4c6a138ec3d7c285b7c125cee809e6b668d2cb0d2328a1b790b7037765bd
|
||||
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
|
||||
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 936f57737eb65afc0f4c3494b93f7b02208055226a7b3cb58f551c38b03ab083
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 798383f6b46fd5dac122d6e35962d25b10401ddb825b5c66df1d21e6b1d8aacc
|
||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||
F ext/wasm/api/sqlite3-wasm.c af472ec27bc7a398a2b94329cb7a77f3411109fc17529a289fa10cc55424ece1
|
||||
F ext/wasm/api/sqlite3-wasm.c 8b32787a3b6bb2990cbaba2304bd5b75a9652acbc8d29909b3279019b6cbaef5
|
||||
F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b
|
||||
F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54
|
||||
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
|
||||
F ext/wasm/batch-runner.js 49609e89aaac9989d6c1ad3fae268e4878e1ad7bc5fd3e5c2f44959660780b2e
|
||||
F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c0779
|
||||
F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
|
||||
F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
||||
F ext/wasm/common/testing.css 35889709547d89a6109ff83b25c11bbc91d8dd43aab8722e428655ca98880a06
|
||||
F ext/wasm/common/whwasmutil.js 77930367c2a65cf6fd6f99ad3644ede33e4d20466f5e506eb87b8d101a0a7655
|
||||
F ext/wasm/common/whwasmutil.js 16a592d5c304a2d268ca1c28e08a5b029a2f3cbe10af78dbc3456cfc9e3559d1
|
||||
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
|
||||
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
|
||||
F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
|
||||
@@ -525,21 +530,21 @@ F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98
|
||||
F ext/wasm/demo-worker1-promiser.js b85a2bb1b918db4f09dfa24419241cb3edad7791389425c2505092e9b715017d
|
||||
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
|
||||
F ext/wasm/demo-worker1.js a619adffc98b75b66c633b00f747b856449a134a9a0357909287d80a182d70fa
|
||||
F ext/wasm/dist.make 481289899a07958439d07ee4302ff86235fa0fbb72f17ea05db2be90a94abf90
|
||||
F ext/wasm/fiddle.make e570ec1bfc7d803507a2e514fe32f673fe001b2114b85c73c3964a462ba8bcfc
|
||||
F ext/wasm/dist.make 11b98da79385701a568a4728671821fe2524c1d1ecd05ff2e24cb3e33b2c6c4f
|
||||
F ext/wasm/fiddle.make 2812c44c9bafb5be9c8767963d1b9f374d77af7795fcaa06483c03e7059dea74
|
||||
F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
||||
F ext/wasm/fiddle/fiddle-worker.js b4a0c8ab6c0983218543ca771c45f6075449f63a1dcf290ae5a681b2cba8800d
|
||||
F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5653284071715
|
||||
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
|
||||
F ext/wasm/index-dist.html cb0da16cba0f21cda2c25724c5869102d48eb0af04446acd3cd0ca031f80ed19
|
||||
F ext/wasm/index.html ce6a68a75532b47e3c0adb83381a06d15de8c0ac0331fb7bf31d33f8e7c77dc4
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215
|
||||
F ext/wasm/index-dist.html c4337617c4d6d4d0796827cec28ac81d128c6f911dcf888a290a32ad50890408
|
||||
F ext/wasm/index.html 5be176de5be8ae96889798f803fef4f6a2ef31cee305a0430ca4629f6ae04c27
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.js 95f573de1826474c9605dda620ee622fcb1673ae74f191eb324c0853aa4dcb66
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.md 9aa6951b529a8b29f578ec8f0355713c39584c92cf1708f63ba0cf917cb5b68e
|
||||
F ext/wasm/module-symbols.html eca884ef4380612145ee550213be57478ee2b9cd9a9c2b27530cc23359c99682
|
||||
F ext/wasm/module-symbols.html b8eebafef8e536624bbe5f7a3da40c07a9062b843dfd3161a0bb72cbb6763dc5
|
||||
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
||||
F ext/wasm/scratchpad-wasmfs-main.js 4c140457f4d6da9d646a49addd91edb6e9ad1643c6c48e3258b5bce24725dc18
|
||||
F ext/wasm/speedtest1-wasmfs.html bc28eb29b69a73864b8d7aae428448f8b7e1de81d8bfb9bba99541322054dbd0
|
||||
F ext/wasm/speedtest1-worker.html 94246488e10af9daa1ebd0979b1b8d7a22a579e5a983fa2a5ad94591ecf55f2c
|
||||
F ext/wasm/speedtest1-worker.html d75d9de9347c6f2f3278b73ccb6bceb559080644aef385fe496fa389f58bcd66
|
||||
F ext/wasm/speedtest1-worker.js 13b57c4a41729678a1194014afec2bd5b94435dcfc8d1039dfa9a533ac819ee1
|
||||
F ext/wasm/speedtest1.html e4c4e5c1c8ec1ad13c995e346e4216a1df152fd2c5cd17e0793b865b2f3c5000
|
||||
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
|
||||
@@ -547,15 +552,18 @@ F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d826
|
||||
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
|
||||
F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f
|
||||
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
|
||||
F ext/wasm/tester1-worker.html 51bf39e2b87f974ae3d5bc3086e2fb36d258f3698c54f6e21ba4b3b99636fa27
|
||||
F ext/wasm/tester1.html 624ec41cd9f78a1f2b6d7df70aaa7a6394396b1f2455ecbd6de5775c1275b121
|
||||
F ext/wasm/tester1.js 3a5558201359ff8a1c3051ab24fcc8bed5a4e99ae5e086e5184cbfc915d08724
|
||||
F ext/wasm/tester1-worker.html 5ef353348c37cf2e4fd0b23da562d3275523e036260b510734e9a3239ba8c987
|
||||
F ext/wasm/tester1.c-pp.html 74aa9b31c75f12490653f814b53c3dd39f40cd3f70d6a53a716f4e8587107399
|
||||
F ext/wasm/tester1.c-pp.js 3b91f192c159088004fba6fe3441edea58421a8b88bccf3dd20978a077648d19
|
||||
F ext/wasm/tests/opfs/concurrency/index.html e8fec75ea6eddc600c8a382da7ea2579feece2263a2fb4417f2cf3e9d451744c
|
||||
F ext/wasm/tests/opfs/concurrency/test.js bfc3d7e27b207f0827f12568986b8d516a744529550b449314f5c21c9e9faf4a
|
||||
F ext/wasm/tests/opfs/concurrency/worker.js 0eff027cbd3a495acb2ac94f57ca9e4d21125ab9fda07d45f3701b0efe82d450
|
||||
F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
|
||||
F ext/wasm/wasmfs.make edfd60691d10fd19ada4c061280fd7fbe4cf5f6bf6b913268e8ebedfccea6ab5
|
||||
F ext/wasm/wasmfs.make 8fea9b4f3cde06141de1fc4c586ab405bd32c3f401554f4ebb18c797401a678d
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
|
||||
F main.mk 73410c1f180660fd95c8203f35e1d4c1003e033d6bd0dbcb2c41610e4166500d
|
||||
F main.mk fd90f1bd90bd4070c9af7e9c8396bd0cf4208a0186c8d18fa6e2609dff447d5d
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@@ -575,9 +583,9 @@ F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf
|
||||
F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7
|
||||
F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d
|
||||
F src/btmutex.c 6ffb0a22c19e2f9110be0964d0731d2ef1c67b5f7fabfbaeb7b9dabc4b7740ca
|
||||
F src/btree.c 74fc5f6a0577df703d6f98d0c51ee0d8d91d22dbc0ba86e42e056517e2b45576
|
||||
F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22
|
||||
F src/btreeInt.h 8ce1332edd89dfd2461d561ac10a0ab5601c8e06200cb5230596c3caaf54482e
|
||||
F src/btree.c 522df0f1173495e06c5589f0b17a61f53a212017b82be53171133fcfc0e1e90a
|
||||
F src/btree.h 4fcbb0b041013071dd5e9f53c538d49916c092e6ad8842185985e5270a0792de
|
||||
F src/btreeInt.h 88ad499c92b489afedbfefc3f067c4d15023ec021afe622db240dc9d2277cfa5
|
||||
F src/build.c d3e43e950e4e377c1d451a4862556792acdef1faba14a03f899d30d09731c48b
|
||||
F src/callback.c 4cd7225b26a97f7de5fee5ae10464bed5a78f2adefe19534cc2095b3a8ca484a
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
@@ -586,10 +594,10 @@ F src/date.c 94ce83b4cd848a387680a5f920c9018c16655db778c4d36525af0a0f34679ac5
|
||||
F src/dbpage.c f1a87f4ebcf22284e0aaf0697862f4ccfc120dcd6db3d8dfa3b049b2580c01d8
|
||||
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
|
||||
F src/delete.c 86573edae75e3d3e9a8b590d87db8e47222103029df4f3e11fa56044459b514e
|
||||
F src/expr.c 847f87d9df3ede2b2b0a8db088af0b9c1923b21009f8ea1b9b7b28cb0a383170
|
||||
F src/expr.c 9e7fadc664b938c18f006be0d4f6669888f9302756ee204420c7eccaed5435a6
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 722f20779f5342a787922deded3628d8c74b5249cab04098cf17ee2f2aaff002
|
||||
F src/func.c d25f3c667d59dbac195e65f3539fdbbd8a36c142ce7bcdb45d1baf07446ad13a
|
||||
F src/func.c 7e86074afc4dc702691a29b7801f6dcc191db092b52e8bbe69dcd2f7be52194d
|
||||
F src/global.c e06ff8e0acd85aec13563c9ecb44fbbf38232ccf73594998fd880b92d619594b
|
||||
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
||||
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
|
||||
@@ -598,15 +606,15 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 90a32bc7faa755cd5292ade21d2b3c6edba8fd1d70754a364caccabfde2c3bb2
|
||||
F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 8086232d10e51e183a7f64199815bad1c579896354db69435347665f62f481e9
|
||||
F src/loadext.c 25663175950c5c4404b9377840b7b4c6fe5c53b415caf43634c62f442c02a9a7
|
||||
F src/main.c fa53bb2ae09549dab5629271c3cfd681f89059f5192afaaaf5c0d396bb3957fe
|
||||
F src/malloc.c dfddca1e163496c0a10250cedeafaf56dff47673e0f15888fb0925340a8e3f90
|
||||
F src/malloc.c 3d4ec162214024ee071d85711b93bec25cd3371280aee3702b63bcf312ca8238
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
|
||||
F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
|
||||
F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
|
||||
F src/mem5.c 5a3dbd8ac8a6501152a4fc1fcae9b0900c2d7eb0589c4ec7456fdde15725a26c
|
||||
F src/memdb.c c2dc88f97c410eb68a24468344b65526685e18354ddfd15906750c1eaf9dc2dd
|
||||
F src/memdb.c a42248bc04dc1f80da3511618a78956ed77b65058b73e251bde39061204e9f97
|
||||
F src/memjournal.c c283c6c95d940eb9dc70f1863eef3ee40382dbd35e5a1108026e7817c206e8a0
|
||||
F src/msvc.h 3a15918220367a8876be3fa4f2abe423a861491e84b864fb2b7426bf022a28f8
|
||||
F src/mutex.c 5e3409715552348732e97b9194abe92fdfcd934cfb681df4ba0ab87ac6c18d25
|
||||
@@ -621,33 +629,33 @@ F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
|
||||
F src/os_kv.c 0e59600d25b72034c7666b8b7dcc527f039b5d9c16f24a7eca4c08c66f63c364
|
||||
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
|
||||
F src/os_unix.c 287aa5f5691a2b356780c63e83abaa33549add84227b8313395f04088486d79c
|
||||
F src/os_win.c 8d129ae3e59e0fa900e20d0ad789e96f2e08177f0b00b53cdda65c40331e0902
|
||||
F src/os_win.c 295fe45f18bd86f2477f4cd79f3377c6f883ceb941b1f46808665c73747f2345
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 6176d9752eb580419e8fef4592dc417a6b00ddfd43ee22f818819bf8840ceee8
|
||||
F src/pager.c d3122cf67f327f1e2df12d06236a3473a8099542071e257067552f42917f172d
|
||||
F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
|
||||
F src/parse.y 8e67d820030d2655b9942ffe61c1e7e6b96cea2f2f72183533299393907d0564
|
||||
F src/pcache.c f4268f7f73c6a3db12ce22fd25bc68dc42315d19599414ab1207d7cf32f79197
|
||||
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||
F src/pcache1.c dee95e3cd2b61e6512dc814c5ab76d5eb36f0bfc9441dbb4260fccc0d12bbddc
|
||||
F src/pragma.c 41430ca04735cc8e5d003bfd9315eadede3ec326e50805cc81bcf34e46601292
|
||||
F src/pragma.c 894c2621d35edd4beea9b331cfdb1b42032394420074d2294c8febe548eea8a1
|
||||
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
|
||||
F src/prepare.c 1b02be0441eda4579471fea097f678effcbb77ef0c39ab3f703c837822bcd674
|
||||
F src/prepare.c 9ebd3a1b12bbd1951f0d6db850f32cf5d4547a6ab8bb9e958d75dfbe4e60d0a3
|
||||
F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764
|
||||
F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 12cb5162e606290354f9512ff0c30fc6dc4d7a77a92b6c5b581395f9c4edd047
|
||||
F src/shell.c.in 84bb08d8762920285f08f1c0993f1b3992ac43af5d72445cb8a973fc50c71923
|
||||
F src/sqlite.h.in bf5846820130b6cf01b002e90427eae29f02db07d9cb9b5ccd0e0aad867eed14
|
||||
F src/select.c c1eb8f3ee25152327f2e7e87db8cea549e57c104b63638bff4fc584d479c33f0
|
||||
F src/shell.c.in 9fda74d40b206a707aaa69fc5dc38e2c6a9137a3f4a1dcd7af581d59d92c063c
|
||||
F src/sqlite.h.in 100fc660c2f19961b8ed8437b9d53d687de2f8eb2b96437ec6da216adcb643ca
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h c4b9fa7a7e2bcdf850cfeb4b8a91d5ec47b7a00033bc996fd2ee96cbf2741f5f
|
||||
F src/sqliteInt.h 2c24ba38f78e32fe5d7ec136321a6ad827698b33ca98664970a8b7274d69ef7c
|
||||
F src/sqliteInt.h 5dd5d3d47f40b6a12be4a5fc131673bfe00c00373ed266ff4c4ec05d1991e69f
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
F src/tclsqlite.c 4e64ba300a5a26e0f1170e09032429faeb65e45e8f3d1a7833e8edb69fc2979e
|
||||
F src/test1.c 40c9a40975512985762f87b83d0c63e4904833a9fe78cbcca664a37095301b1d
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test1.c 09ad3f2bf5f0efe70eb16e460054954b36be8ed09dfcafa0642b37d25d379f6f
|
||||
F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef
|
||||
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
|
||||
F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
|
||||
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
|
||||
@@ -701,18 +709,18 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 1305797eab3542a0896b552c6e7669c972c1468e11e92b370533c1f37a37082b
|
||||
F src/treeview.c 07787f67cd297a6d09d04b8d70c06769c60c9c1d9080378f93929c16f8fd3298
|
||||
F src/trigger.c 4163ada044af89d51caba1cb713a73165347b2ec05fe84a283737c134d61fcd5
|
||||
F src/treeview.c 29b1dc7e0f84ba090734febe27393d4719682af0cae1b902d5ebf0236ecebea4
|
||||
F src/trigger.c 5e68b790f022b8dafbfb0eb244786512a95c9575fc198719d2557d73e5795858
|
||||
F src/update.c 5b0302c47cf31b533d5dff04c497ca1d8b9d89c39727e633fbe7b882fd5ac5aa
|
||||
F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 0be191521ff6d2805995f4910f0b6231b42843678b2efdc1abecaf39929a673f
|
||||
F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd
|
||||
F src/vdbe.c 0c7cb1b934ad8611e14e7efaf2c3a95df7dd3f7964d63ea07fef42a23df86131
|
||||
F src/vdbe.c c2d6d0c0c343d8ebffef996c73cbb69e337225f757fea7fe5c0e3ea14662adec
|
||||
F src/vdbe.h 58675f47dcf3105bab182c3ad3726efd60ffd003e954386904ac9107d0d2b743
|
||||
F src/vdbeInt.h 17b7461ffcf9ee760d1341731715a419f6b8c763089a7ece25c2e8098d702b3f
|
||||
F src/vdbeapi.c 1e8713d0b653acb43cd1bdf579c40e005c4844ea90f414f065946a83db3c27fb
|
||||
F src/vdbeaux.c 6d0a75c1fbc7efea6924f6895ebceca664001464bc7ac56949d3c60aa5e498a0
|
||||
F src/vdbeaux.c 87684b89877eae0c58c78b340bb5356aa1c8fb1dd650b29410c8b745aeeb20b5
|
||||
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
|
||||
F src/vdbemem.c 6cfed43758d57b6e3b99d9cdedfeccd86e45a07e427b22d8487cbdbebb6c522a
|
||||
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
|
||||
@@ -723,11 +731,11 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d
|
||||
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
|
||||
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
|
||||
F src/where.c 1ef5aae7fac877057b9f360f06b26d4275888460d8fb6e92bbb9e70e07afe946
|
||||
F src/where.c ea0f518df9e00aa44013a1d384090b4b3a499ee11d4daa0a7d99c4eb9f7ab4ba
|
||||
F src/whereInt.h df0c79388c0b71b4a91f480d02791679fe0345d40410435c541c8893e95a4d3f
|
||||
F src/wherecode.c 133a94f82858787217d073143617df19e4a6a7d0b771a1519f957608109ad5a5
|
||||
F src/whereexpr.c 05295b44b54eea76d1ba766f0908928d0e20e990c249344c9521454d3d09c7ae
|
||||
F src/window.c 928e215840e2f2d9a2746e018c9643ef42c66c4ab6630ef0df7fa388fa145e86
|
||||
F src/window.c 14836767adb26573b50f528eb37f8b1336f2c430ab38de7cead1e5c546bb4d8c
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
|
||||
F test/affinity3.test b5c19d504dec222c0dc66642673d23dce915d35737b68e74d9f237b80493eb53
|
||||
@@ -808,6 +816,7 @@ F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
|
||||
F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027
|
||||
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
|
||||
F test/basexx1.test d8a50f0744b93dca656625597bcd3499ff4b9a4ea2a82432b119b7d46e3e0c08
|
||||
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
|
||||
F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
|
||||
F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
|
||||
@@ -821,9 +830,9 @@ F test/bestindex9.test bf2eb8556e8d5c00ef3ee18c521751cd03c1b55454b6e7683b4c6742e
|
||||
F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263
|
||||
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
|
||||
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
|
||||
F test/bigmmap.test b820c234daa56d24bc3bf006e3ac7aa9d9623c8ac656a38f59063b444a2d65d1
|
||||
F test/bigmmap.test 6021e205487347c6d7e5a541aa472a4b8efc4e9f4a3799a823b61a8e6616105d
|
||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||
F test/bigsort.test 8299fa9298f4f1e02fc7d2712e8b77d6cd60e5a2
|
||||
F test/bigsort.test 997e172009905873c06426145e4b3794c7dfe2d563724cb2fd39d45f319cf3d2
|
||||
F test/bind.test 1e136709b306f7ed3192d349c2930d89df6ab621654ad6f1a72381d3fe76f483
|
||||
F test/bind2.test 918bc35135f4141809ead7585909cde57d44db90a7a62aef540127148f91aab7
|
||||
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
|
||||
@@ -901,7 +910,7 @@ F test/corruptH.test 79801d97ec5c2f9f3c87739aa1ec2eb786f96454
|
||||
F test/corruptI.test a17bbf54fdde78d43cf3cc34b0057719fd4a173a3d824285b67dc5257c064c7b
|
||||
F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
|
||||
F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af
|
||||
F test/corruptL.test ecce40d7b9b909a670a42a45d86e30d927735d7e7f09041af438b19529d35532
|
||||
F test/corruptL.test 9d1a0055c8db19baccd12f22ac36a33ec7d63afb59e82eb30835aea8f89b94df
|
||||
F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
|
||||
F test/corruptN.test 7c099d153a554001b4fb829c799b01f2ea6276cbc32479131e0db0da4efd9cc4
|
||||
F test/cost.test b11cdbf9f11ffe8ef99c9881bf390e61fe92baf2182bad1dbe6de59a7295c576
|
||||
@@ -934,7 +943,7 @@ F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b
|
||||
F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee
|
||||
F test/dbfuzz2.c 4b3c12de4d98b1b2d908ab03d217d4619e47c8b23d5e67f8a6f2b1bdee7cae23
|
||||
F test/dbpage.test fce29035c7566fd7835ec0f19422cb4b9c6944ce0e1b936ff8452443f92e887d
|
||||
F test/dbpagefault.test 20fe3a2a295f1c8cb30195b7b58a010530ecbc060e53e146af9e3e4fde4cab15
|
||||
F test/dbpagefault.test b893e9e43d55edc0cdf3f71ae093802f80f0fb517d839eefeae5647c49a41f36
|
||||
F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759
|
||||
F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef
|
||||
F test/decimal.test fcf403fd5585f47342234e153c4a4338cd737b8e0884ac66fc484df47dbcf1a7
|
||||
@@ -1089,7 +1098,7 @@ F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
F test/fts3expr.test ebae205a7a89446c32583bcd492dcb817b9f6b31819bb4dde2583bb99c77e526
|
||||
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
|
||||
F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8
|
||||
F test/fts3expr4.test f5b2832549f01b1f7f73389fa21d4b875499bc95bf7c8b36271844888c6a0938
|
||||
F test/fts3expr4.test 6c7675bbdbffe6ffc95e9e861500b8ac3f739c4d004ffda812f138eeb1b45529
|
||||
F test/fts3expr5.test a5b9a053becbdb8e973fbf4d6d3abaabeb42d511d1848bd57931f3e0a1cf983e
|
||||
F test/fts3f.test 8c438d5e1cab526b0021988fb1dc70cf3597b006a33ffd6c955ee89929077fe3
|
||||
F test/fts3fault.test f4e1342acfe6d216a001490e8cd52afac1f9ffe4a11bbcdcb296129a45c5df45
|
||||
@@ -1335,7 +1344,7 @@ F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
|
||||
F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3
|
||||
F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
|
||||
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
|
||||
F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101
|
||||
F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
|
||||
F test/multiplex4.test e8ae4c4bd70606a5727743241f13b5701990abe4
|
||||
F test/mutex1.test 177db2e4edb530f2ff21edc52ac79a412dbe63e4c47c3ae9504d3fb4f1ce81fa
|
||||
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
|
||||
@@ -1385,9 +1394,9 @@ F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035c
|
||||
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
|
||||
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
|
||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||
F test/permutations.test 650d89ab5aad0c9fab9325b11deca8662cb5e72f43e005073d35f12ad00eaca2
|
||||
F test/permutations.test 3e0d6eea70e5087f3240b1a2fe621b0c73445f38a262029f0a1d2d89564026f7
|
||||
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
|
||||
F test/pragma.test cae534c12a033a5c319ccc94f50b32811acdef9f67bf19a82ff42697caccd69f
|
||||
F test/pragma.test 620622fb0815f1cbea8e26e1d8abad38e0cbcbed8927fd84048fe9fd6239e323
|
||||
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
|
||||
F test/pragma3.test 92a46bbea12322dd94a404f49edcfbfc913a2c98115f0d030a7459bb4712ef31
|
||||
F test/pragma4.test ca5e4dfc46adfe490f75d73734f70349d95a199e6510973899e502eef2c8b1f8
|
||||
@@ -1398,7 +1407,7 @@ F test/printf.test 390d0d7fcffc3c4ea3c1bb4cbb267444e32b33b048ae21895f23a291844fe
|
||||
F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224cce60
|
||||
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/pushdown.test 5e72c51c5e33253ed639ccee1e01ce62d62b6eee5ca893cd82334e4ee7b1d7fc
|
||||
F test/pushdown.test c69f0970ea17e0afc674b89741f60c172cb6f761d81665fc71015f674f0f66ba
|
||||
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
|
||||
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
|
||||
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
|
||||
@@ -1409,14 +1418,14 @@ F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
||||
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
|
||||
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
|
||||
F test/recover.test cd81539661b9a6263d68f7bfd1440f4ac0aeb68f0fa0370db32a9e706da89de7
|
||||
F test/regexp1.test 83c631617357150f8054ca1d1fed40a552b0d0f8eb7a7f090c3be02cee9f9913
|
||||
F test/recover.test fd5199f928757cb308661b5fdca1abc19398a798ff7f24b57c3071e9f8e0471e
|
||||
F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1
|
||||
F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed1368b5
|
||||
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
|
||||
F test/releasetest_data.tcl 11ba48a21ed1c808147b0e77c6e93d204577f4327ffe6d7c3b34cd3c01eac3a2
|
||||
F test/releasetest_data.tcl 0db8aee0c348090fd06da47020ab4ed8ec692e0723427b2f3947d4dfb806f3b0
|
||||
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/returning1.test c43b8370a351f77aec6d71f4a2cde59b849369ed1933261a2c2c69e23e34ff5e
|
||||
F test/returning1.test 1366e04566cfe1a082d17b1e0f195ec64473c79374b3a5d4ae00c43d885dea31
|
||||
F test/returningfault.test ae4c4b5e8745813287a359d9ccdb9d5c883c2e68afb18fb0767937d5de5692a4
|
||||
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
|
||||
F test/rollback2.test 3f3a4e20401825017df7e7671e9f31b6de5fae5620c2b9b49917f52f8c160a8f
|
||||
@@ -1492,7 +1501,7 @@ F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test e4b4de56f454708e0747b52915135baa2cbfec4965406d6eaf02a4a5c22a9880
|
||||
F test/shell2.test c536c2aab4852608f8a606262330797abc4d964a4c2c782a7760f54ea1f17a6a
|
||||
F test/shell3.test 91febeac0412812bf6370abb8ed72700e32bf8f9878849414518f662dfd55e8a
|
||||
F test/shell4.test 7dc8a515705bc093d8ffe381670e8fa7a969661e8ed177c35c847e3c6dfc35e2
|
||||
F test/shell4.test 9abd0c12a7e20a4c49e84d5be208d2124fa6c09e728f56f1f4bee0f02853935f
|
||||
F test/shell5.test c8b6c54f26ec537f8558273d7ed293ca3725ef42e6b12b8f151718628bd1473b
|
||||
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
|
||||
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
|
||||
@@ -1617,12 +1626,13 @@ F test/tkt-78e04e52ea.test b731f2ab7d1c2482ac5152097da02ef4805a45147ba9498d3cd9d
|
||||
F test/tkt-7a31705a7e6.test 9e9c057b6a9497c8f7ba7b16871029414ccf6550e7345d9085d6d71c9a56bb6f
|
||||
F test/tkt-7bbfb7d442.test e87b59e620700b5a52ecd92f05d56686c1cad9e1aa17456eada55e0bb821b698
|
||||
F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8
|
||||
F test/tkt-80e031a00f.test 9ee36348b761bf7c14261e002b75a4c0d5a04d4c
|
||||
F test/tkt-80e031a00f.test 7c93af53f43527f50020983a4bcc39b077e77c7362d7af8e04a5fd155fe5e829
|
||||
F test/tkt-8454a207b9.test aff2e76143cfa443ddce6f7d85968a2e9b57a3deb0b881b730120740555f9e2f
|
||||
F test/tkt-868145d012.test a5f941107ece6a64410ca4755c6329b7eb57a356
|
||||
F test/tkt-8c63ff0ec.test 258b7fc8d7e4e1cb5362c7d65c143528b9c4cbed
|
||||
F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
|
||||
F test/tkt-94c04eaadb.test f738c57c7f68ab8be1c054415af7774617cb6223
|
||||
F test/tkt-99378177930f87bd.test 0f932e85fa1d41f30532cb7be9718d82e491e953123b8c4c85cf025f36ffe34b
|
||||
F test/tkt-9a8b09f8e6.test b2ef151d0984b2ebf237760dbeaa50724e5a0667
|
||||
F test/tkt-9d68c883.test 16f7cb96781ba579bc2e19bb14b4ad609d9774b6
|
||||
F test/tkt-9f2eb3abac.test cb6123ac695a08b4454c3792fbe85108f67fabf8
|
||||
@@ -1872,7 +1882,7 @@ F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
|
||||
F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747
|
||||
F test/walvfs.test e1a6ad0f3c78e98b55c3d5f0889cf366cc0d0a1cb2bccb44ac9ec67384adc4a1
|
||||
F test/wapp.tcl b440cd8cf57953d3a49e7ee81e6a18f18efdaf113b69f7d8482b0710a64566ec
|
||||
F test/wapptest.tcl e3b6d5afa021c39a0f459ea9fbd1077459c1d81fca98eb40af8404ad3fc2360f x
|
||||
F test/wapptest.tcl 1bea58a6a8e68a73f542ee4fca28b771b84ed803bd0c9e385087070b3d747b3c x
|
||||
F test/where.test d13cd7c24e80009d2b54e2f7a8893c457afa49c64f99359c9eb3fe668ba1d9d4
|
||||
F test/where2.test 03c21a11e7b90e2845fc3c8b4002fc44cc2797fa74c86ee47d70bd7ea4f29ed6
|
||||
F test/where3.test 5b4ffc0ac2ea0fe92f02b1244b7531522fe4d7bccf6fa8741d54e82c10e67753
|
||||
@@ -1986,12 +1996,12 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61
|
||||
F tool/mkopcodeh.tcl bcb2bd5affb545fd219ef0304c7978e2a356407ab723f45ec8569235892c1c3f
|
||||
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
|
||||
F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d
|
||||
F tool/mkshellc.tcl df5d249617f9cc94d5c48eb0401673eb3f31f383ecbc54e8a13ca3dd97e89450
|
||||
F tool/mkshellc.tcl 02d0de8349ef830c0fb20d29680320bde2466e2ec422e5bd94c4317a7a7e8cc9
|
||||
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
|
||||
F tool/mksqlite3c.tcl 4fc26a9bfa0c4505b203d7ca0cf086e75ebcd4d63eb719c82f37e3fecdf23d37
|
||||
F tool/mksqlite3h.tcl 1f5e4a1dbbbc43c83cc6e74fe32c6c620502240b66c7c0f33a51378e78fc4edf
|
||||
F tool/mksqlite3c.tcl eb47021591b1ad4a6862e2cb5625f1ac67ec1e0c6db5ba3953c069c635284cf5
|
||||
F tool/mksqlite3h.tcl d391cff7cad0a372ee1406faee9ccc7dad9cb80a0c95cae0f73d10dd26e06762
|
||||
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
|
||||
F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
|
||||
F tool/offsets.c 8ed2b344d33f06e71366a9b93ccedaa38c096cc1dbd4c3c26ad08c6115285845
|
||||
@@ -2011,7 +2021,7 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
|
||||
F tool/showwal.c 0253c187ae16fdae9cde89e63e1dfcd3bb35e5416d066415f99e2f8cac6ab03d
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/spaceanal.tcl 1b5be34c6223cb1af06da2a10fb77863eb869b1962d055820b0a11cf2336ab45
|
||||
F tool/speed-check.sh 13f8e07dbfe25f3aecda33fb6068894665af61ca1360a7b654be0ad0c3f3ae0b
|
||||
F tool/speed-check.sh 9b27e158330a6587e92214b2344cc6fadde514996c0648fc0de7955ef7a79d77
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@@ -2054,8 +2064,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P f6fa0cffa921ccde8910e7fa4a63c2e4ef8ddb376c8ce99e436b27ac332c4498
|
||||
R 49cc6fc37416a3ba7fdfd0b20ce15895
|
||||
U stephan
|
||||
Z b66cc4ccdf23cf485ede2fda7e429a86
|
||||
P 17b823500a2ed135c1f40aa7f4d87ba5b2eab35c0abd9e0856041cf0f510cbee a2449bcc2c71d0f4c3289621fbf1cb97f0f407c9f7b5bf18245b7854a07c6cfa
|
||||
R b25d12f698cc565856f0ef09be44e05e
|
||||
U larrybr
|
||||
Z cf8d223b7c2dae4330a88af45b5f7bd0
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
70ee6ee014bc4e2c1daa9b4a8909cf76ccecf32de1eb39055f20d3d0b1daa1bd
|
||||
b8345630a2a322234bda49ee4b996f6ba20e2b080621e229a2ec5e820892a663
|
||||
+119
-109
@@ -1655,7 +1655,6 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
|
||||
** fragmented bytes within the page. */
|
||||
memcpy(&aData[iAddr], &aData[pc], 2);
|
||||
aData[hdr+7] += (u8)x;
|
||||
testcase( pc+x>maxPC );
|
||||
return &aData[pc];
|
||||
}else if( x+pc > maxPC ){
|
||||
/* This slot extends off the end of the usable part of the page */
|
||||
@@ -1901,62 +1900,67 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
** Only the following combinations are supported. Anything different
|
||||
** indicates a corrupt database files:
|
||||
**
|
||||
** PTF_ZERODATA
|
||||
** PTF_ZERODATA | PTF_LEAF
|
||||
** PTF_LEAFDATA | PTF_INTKEY
|
||||
** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF
|
||||
** PTF_ZERODATA (0x02, 2)
|
||||
** PTF_LEAFDATA | PTF_INTKEY (0x05, 5)
|
||||
** PTF_ZERODATA | PTF_LEAF (0x0a, 10)
|
||||
** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF (0x0d, 13)
|
||||
*/
|
||||
static int decodeFlags(MemPage *pPage, int flagByte){
|
||||
BtShared *pBt; /* A copy of pPage->pBt */
|
||||
|
||||
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 );
|
||||
flagByte &= ~PTF_LEAF;
|
||||
pPage->childPtrSize = 4-4*pPage->leaf;
|
||||
pBt = pPage->pBt;
|
||||
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
|
||||
/* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
|
||||
** interior table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
|
||||
/* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
|
||||
** leaf table b-tree page. */
|
||||
assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
|
||||
pPage->intKey = 1;
|
||||
if( pPage->leaf ){
|
||||
pPage->max1bytePayload = pBt->max1bytePayload;
|
||||
if( flagByte>=(PTF_ZERODATA | PTF_LEAF) ){
|
||||
pPage->childPtrSize = 0;
|
||||
pPage->leaf = 1;
|
||||
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF) ){
|
||||
pPage->intKeyLeaf = 1;
|
||||
pPage->xCellSize = cellSizePtrTableLeaf;
|
||||
pPage->xParseCell = btreeParseCellPtr;
|
||||
pPage->intKey = 1;
|
||||
pPage->maxLocal = pBt->maxLeaf;
|
||||
pPage->minLocal = pBt->minLeaf;
|
||||
}else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pPage->xParseCell = btreeParseCellPtrIndex;
|
||||
pPage->maxLocal = pBt->maxLocal;
|
||||
pPage->minLocal = pBt->minLocal;
|
||||
}else{
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pPage->xParseCell = btreeParseCellPtrIndex;
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
}else{
|
||||
pPage->childPtrSize = 4;
|
||||
pPage->leaf = 0;
|
||||
if( flagByte==(PTF_ZERODATA) ){
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pPage->xParseCell = btreeParseCellPtrIndex;
|
||||
pPage->maxLocal = pBt->maxLocal;
|
||||
pPage->minLocal = pBt->minLocal;
|
||||
}else if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->xCellSize = cellSizePtrNoPayload;
|
||||
pPage->xParseCell = btreeParseCellPtrNoPayload;
|
||||
pPage->intKey = 1;
|
||||
pPage->maxLocal = pBt->maxLeaf;
|
||||
pPage->minLocal = pBt->minLeaf;
|
||||
}else{
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pPage->xParseCell = btreeParseCellPtrIndex;
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
pPage->maxLocal = pBt->maxLeaf;
|
||||
pPage->minLocal = pBt->minLeaf;
|
||||
}else if( flagByte==PTF_ZERODATA ){
|
||||
/* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
|
||||
** interior index b-tree page. */
|
||||
assert( (PTF_ZERODATA)==2 );
|
||||
/* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
|
||||
** leaf index b-tree page. */
|
||||
assert( (PTF_ZERODATA|PTF_LEAF)==10 );
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pPage->xParseCell = btreeParseCellPtrIndex;
|
||||
pPage->maxLocal = pBt->maxLocal;
|
||||
pPage->minLocal = pBt->minLocal;
|
||||
}else{
|
||||
/* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is
|
||||
** an error. */
|
||||
pPage->intKey = 0;
|
||||
pPage->intKeyLeaf = 0;
|
||||
pPage->xCellSize = cellSizePtr;
|
||||
pPage->xParseCell = btreeParseCellPtrIndex;
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
pPage->max1bytePayload = pBt->max1bytePayload;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -5496,9 +5500,25 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
|
||||
** on success. Set *pRes to 0 if the cursor actually points to something
|
||||
** or set *pRes to 1 if the table is empty.
|
||||
*/
|
||||
static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){
|
||||
int rc = moveToRoot(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
*pRes = 0;
|
||||
rc = moveToRightmost(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->curFlags |= BTCF_AtLast;
|
||||
}else{
|
||||
pCur->curFlags &= ~BTCF_AtLast;
|
||||
}
|
||||
}else if( rc==SQLITE_EMPTY ){
|
||||
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
||||
*pRes = 1;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
|
||||
int rc;
|
||||
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
|
||||
|
||||
@@ -5519,23 +5539,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
rc = moveToRoot(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
*pRes = 0;
|
||||
rc = moveToRightmost(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->curFlags |= BTCF_AtLast;
|
||||
}else{
|
||||
pCur->curFlags &= ~BTCF_AtLast;
|
||||
}
|
||||
}else if( rc==SQLITE_EMPTY ){
|
||||
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
|
||||
*pRes = 1;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
return btreeLast(pCur, pRes);
|
||||
}
|
||||
|
||||
/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY)
|
||||
@@ -6256,8 +6260,8 @@ static int allocateBtreePage(
|
||||
assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
|
||||
pPage1 = pBt->pPage1;
|
||||
mxPage = btreePagecount(pBt);
|
||||
/* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
|
||||
** stores stores the total number of pages on the freelist. */
|
||||
/* EVIDENCE-OF: R-21003-45125 The 4-byte big-endian integer at offset 36
|
||||
** stores the total number of pages on the freelist. */
|
||||
n = get4byte(&pPage1->aData[36]);
|
||||
testcase( n==mxPage-1 );
|
||||
if( n>=mxPage ){
|
||||
@@ -6602,7 +6606,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
|
||||
/* If the database supports auto-vacuum, write an entry in the pointer-map
|
||||
** to indicate that the page is free.
|
||||
*/
|
||||
if( ISAUTOVACUUM ){
|
||||
if( ISAUTOVACUUM(pBt) ){
|
||||
ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc);
|
||||
if( rc ) goto freepage_out;
|
||||
}
|
||||
@@ -7042,24 +7046,20 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
** in pTemp or the original pCell) and also record its index.
|
||||
** Allocating a new entry in pPage->aCell[] implies that
|
||||
** pPage->nOverflow is incremented.
|
||||
**
|
||||
** *pRC must be SQLITE_OK when this routine is called.
|
||||
*/
|
||||
static void insertCell(
|
||||
static int insertCell(
|
||||
MemPage *pPage, /* Page into which we are copying */
|
||||
int i, /* New cell becomes the i-th cell of the page */
|
||||
u8 *pCell, /* Content of the new cell */
|
||||
int sz, /* Bytes of content in pCell */
|
||||
u8 *pTemp, /* Temp storage space for pCell, if needed */
|
||||
Pgno iChild, /* If non-zero, replace first 4 bytes with this value */
|
||||
int *pRC /* Read and write return code from here */
|
||||
Pgno iChild /* If non-zero, replace first 4 bytes with this value */
|
||||
){
|
||||
int idx = 0; /* Where to write new cell content in data[] */
|
||||
int j; /* Loop counter */
|
||||
u8 *data; /* The content of the whole page */
|
||||
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
|
||||
|
||||
assert( *pRC==SQLITE_OK );
|
||||
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
|
||||
assert( MX_CELL(pPage->pBt)<=10921 );
|
||||
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
|
||||
@@ -7094,14 +7094,13 @@ static void insertCell(
|
||||
}else{
|
||||
int rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pRC = rc;
|
||||
return;
|
||||
return rc;
|
||||
}
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
data = pPage->aData;
|
||||
assert( &data[pPage->cellOffset]==pPage->aCellIdx );
|
||||
rc = allocateSpace(pPage, sz, &idx);
|
||||
if( rc ){ *pRC = rc; return; }
|
||||
if( rc ){ return rc; }
|
||||
/* The allocateSpace() routine guarantees the following properties
|
||||
** if it returns successfully */
|
||||
assert( idx >= 0 );
|
||||
@@ -7128,13 +7127,16 @@ static void insertCell(
|
||||
assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pPage->pBt->autoVacuum ){
|
||||
int rc = SQLITE_OK;
|
||||
/* The cell may contain a pointer to an overflow page. If so, write
|
||||
** the entry for the overflow page into the pointer map.
|
||||
*/
|
||||
ptrmapPutOvflPtr(pPage, pPage, pCell, pRC);
|
||||
ptrmapPutOvflPtr(pPage, pPage, pCell, &rc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -7235,14 +7237,16 @@ struct CellArray {
|
||||
** computed.
|
||||
*/
|
||||
static void populateCellCache(CellArray *p, int idx, int N){
|
||||
MemPage *pRef = p->pRef;
|
||||
u16 *szCell = p->szCell;
|
||||
assert( idx>=0 && idx+N<=p->nCell );
|
||||
while( N>0 ){
|
||||
assert( p->apCell[idx]!=0 );
|
||||
if( p->szCell[idx]==0 ){
|
||||
p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]);
|
||||
if( szCell[idx]==0 ){
|
||||
szCell[idx] = pRef->xCellSize(pRef, p->apCell[idx]);
|
||||
}else{
|
||||
assert( CORRUPT_DB ||
|
||||
p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) );
|
||||
szCell[idx]==pRef->xCellSize(pRef, p->apCell[idx]) );
|
||||
}
|
||||
idx++;
|
||||
N--;
|
||||
@@ -7444,8 +7448,8 @@ static int pageFreeArray(
|
||||
int nRet = 0;
|
||||
int i;
|
||||
int iEnd = iFirst + nCell;
|
||||
u8 *pFree = 0;
|
||||
int szFree = 0;
|
||||
u8 *pFree = 0; /* \__ Parameters for pending call to */
|
||||
int szFree = 0; /* / freeSpace() */
|
||||
|
||||
for(i=iFirst; i<iEnd; i++){
|
||||
u8 *pCell = pCArray->apCell[i];
|
||||
@@ -7466,6 +7470,9 @@ static int pageFreeArray(
|
||||
return 0;
|
||||
}
|
||||
}else{
|
||||
/* The current cell is adjacent to and before the pFree cell.
|
||||
** Combine the two regions into one to reduce the number of calls
|
||||
** to freeSpace(). */
|
||||
pFree = pCell;
|
||||
szFree += sz;
|
||||
}
|
||||
@@ -7673,7 +7680,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
||||
** be marked as dirty. Returning an error code will cause a
|
||||
** rollback, undoing any changes made to the parent page.
|
||||
*/
|
||||
if( ISAUTOVACUUM ){
|
||||
if( ISAUTOVACUUM(pBt) ){
|
||||
ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc);
|
||||
if( szCell>pNew->minLocal ){
|
||||
ptrmapPutOvflPtr(pNew, pNew, pCell, &rc);
|
||||
@@ -7701,8 +7708,8 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
||||
|
||||
/* Insert the new divider cell into pParent. */
|
||||
if( rc==SQLITE_OK ){
|
||||
insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
|
||||
0, pPage->pgno, &rc);
|
||||
rc = insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
|
||||
0, pPage->pgno);
|
||||
}
|
||||
|
||||
/* Set the right-child pointer of pParent to point to the new page. */
|
||||
@@ -7811,7 +7818,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
|
||||
/* If this is an auto-vacuum database, update the pointer-map entries
|
||||
** for any b-tree or overflow pages that pTo now contains the pointers to.
|
||||
*/
|
||||
if( ISAUTOVACUUM ){
|
||||
if( ISAUTOVACUUM(pBt) ){
|
||||
*pRC = setChildPtrmaps(pTo);
|
||||
}
|
||||
}
|
||||
@@ -8235,15 +8242,17 @@ static int balance_nonroot(
|
||||
d = r + 1 - leafData;
|
||||
(void)cachedCellSize(&b, d);
|
||||
do{
|
||||
int szR, szD;
|
||||
assert( d<nMaxCells );
|
||||
assert( r<nMaxCells );
|
||||
(void)cachedCellSize(&b, r);
|
||||
szR = cachedCellSize(&b, r);
|
||||
szD = b.szCell[d];
|
||||
if( szRight!=0
|
||||
&& (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){
|
||||
&& (bBulk || szRight+szD+2 > szLeft-(szR+(i==k-1?0:2)))){
|
||||
break;
|
||||
}
|
||||
szRight += b.szCell[d] + 2;
|
||||
szLeft -= b.szCell[r] + 2;
|
||||
szRight += szD + 2;
|
||||
szLeft -= szR + 2;
|
||||
cntNew[i-1] = r;
|
||||
r--;
|
||||
d--;
|
||||
@@ -8297,7 +8306,7 @@ static int balance_nonroot(
|
||||
cntOld[i] = b.nCell;
|
||||
|
||||
/* Set the pointer-map entry for the new sibling page. */
|
||||
if( ISAUTOVACUUM ){
|
||||
if( ISAUTOVACUUM(pBt) ){
|
||||
ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto balance_cleanup;
|
||||
@@ -8390,7 +8399,7 @@ static int balance_nonroot(
|
||||
** updated. This happens below, after the sibling pages have been
|
||||
** populated, not here.
|
||||
*/
|
||||
if( ISAUTOVACUUM ){
|
||||
if( ISAUTOVACUUM(pBt) ){
|
||||
MemPage *pOld;
|
||||
MemPage *pNew = pOld = apNew[0];
|
||||
int cntOldNext = pNew->nCell + pNew->nOverflow;
|
||||
@@ -8487,7 +8496,7 @@ static int balance_nonroot(
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto balance_cleanup;
|
||||
}
|
||||
insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc);
|
||||
rc = insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno);
|
||||
if( rc!=SQLITE_OK ) goto balance_cleanup;
|
||||
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
|
||||
}
|
||||
@@ -8583,7 +8592,7 @@ static int balance_nonroot(
|
||||
);
|
||||
copyNodeContent(apNew[0], pParent, &rc);
|
||||
freePage(apNew[0], &rc);
|
||||
}else if( ISAUTOVACUUM && !leafCorrection ){
|
||||
}else if( ISAUTOVACUUM(pBt) && !leafCorrection ){
|
||||
/* Fix the pointer map entries associated with the right-child of each
|
||||
** sibling page. All other pointer map entries have already been taken
|
||||
** care of. */
|
||||
@@ -8604,7 +8613,7 @@ static int balance_nonroot(
|
||||
}
|
||||
|
||||
#if 0
|
||||
if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){
|
||||
if( ISAUTOVACUUM(pBt) && rc==SQLITE_OK && apNew[0]->isInit ){
|
||||
/* The ptrmapCheckPages() contains assert() statements that verify that
|
||||
** all pointer map pages are set correctly. This is helpful while
|
||||
** debugging. This is usually disabled because a corrupt database may
|
||||
@@ -8666,7 +8675,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0);
|
||||
copyNodeContent(pRoot, pChild, &rc);
|
||||
if( ISAUTOVACUUM ){
|
||||
if( ISAUTOVACUUM(pBt) ){
|
||||
ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc);
|
||||
}
|
||||
}
|
||||
@@ -8998,7 +9007,6 @@ int sqlite3BtreeInsert(
|
||||
int idx;
|
||||
MemPage *pPage;
|
||||
Btree *p = pCur->pBtree;
|
||||
BtShared *pBt = p->pBt;
|
||||
unsigned char *oldCell;
|
||||
unsigned char *newCell = 0;
|
||||
|
||||
@@ -9017,7 +9025,7 @@ int sqlite3BtreeInsert(
|
||||
** not to clear the cursor here.
|
||||
*/
|
||||
if( pCur->curFlags & BTCF_Multiple ){
|
||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||
rc = saveAllCursors(p->pBt, pCur->pgnoRoot, pCur);
|
||||
if( rc ) return rc;
|
||||
if( loc && pCur->iPage<0 ){
|
||||
/* This can only happen if the schema is corrupt such that there is more
|
||||
@@ -9041,8 +9049,8 @@ int sqlite3BtreeInsert(
|
||||
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
assert( (pCur->curFlags & BTCF_WriteFlag)!=0
|
||||
&& pBt->inTransaction==TRANS_WRITE
|
||||
&& (pBt->btsFlags & BTS_READ_ONLY)==0 );
|
||||
&& p->pBt->inTransaction==TRANS_WRITE
|
||||
&& (p->pBt->btsFlags & BTS_READ_ONLY)==0 );
|
||||
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
|
||||
|
||||
/* Assert that the caller has been consistent. If this cursor was opened
|
||||
@@ -9159,26 +9167,28 @@ int sqlite3BtreeInsert(
|
||||
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
|
||||
loc==0 ? "overwrite" : "new entry"));
|
||||
assert( pPage->isInit || CORRUPT_DB );
|
||||
newCell = pBt->pTmpSpace;
|
||||
newCell = p->pBt->pTmpSpace;
|
||||
assert( newCell!=0 );
|
||||
assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
|
||||
if( flags & BTREE_PREFORMAT ){
|
||||
rc = SQLITE_OK;
|
||||
szNew = pBt->nPreformatSize;
|
||||
szNew = p->pBt->nPreformatSize;
|
||||
if( szNew<4 ) szNew = 4;
|
||||
if( ISAUTOVACUUM && szNew>pPage->maxLocal ){
|
||||
if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
|
||||
CellInfo info;
|
||||
pPage->xParseCell(pPage, newCell, &info);
|
||||
if( info.nPayload!=info.nLocal ){
|
||||
Pgno ovfl = get4byte(&newCell[szNew-4]);
|
||||
ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
|
||||
ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
|
||||
if( NEVER(rc) ) goto end_insert;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = fillInCell(pPage, newCell, pX, &szNew);
|
||||
if( rc ) goto end_insert;
|
||||
}
|
||||
if( rc ) goto end_insert;
|
||||
assert( szNew==pPage->xCellSize(pPage, newCell) );
|
||||
assert( szNew <= MX_CELL_SIZE(pBt) );
|
||||
assert( szNew <= MX_CELL_SIZE(p->pBt) );
|
||||
idx = pCur->ix;
|
||||
if( loc==0 ){
|
||||
CellInfo info;
|
||||
@@ -9198,7 +9208,7 @@ int sqlite3BtreeInsert(
|
||||
testcase( pCur->curFlags & BTCF_ValidOvfl );
|
||||
invalidateOverflowCache(pCur);
|
||||
if( info.nSize==szNew && info.nLocal==info.nPayload
|
||||
&& (!ISAUTOVACUUM || szNew<pPage->minLocal)
|
||||
&& (!ISAUTOVACUUM(p->pBt) || szNew<pPage->minLocal)
|
||||
){
|
||||
/* Overwrite the old cell with the new if they are the same size.
|
||||
** We could also try to do this if the old cell is smaller, then add
|
||||
@@ -9228,7 +9238,7 @@ int sqlite3BtreeInsert(
|
||||
}else{
|
||||
assert( pPage->leaf );
|
||||
}
|
||||
insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
|
||||
rc = insertCell(pPage, idx, newCell, szNew, 0, 0);
|
||||
assert( pPage->nOverflow==0 || rc==SQLITE_OK );
|
||||
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
|
||||
|
||||
@@ -9301,7 +9311,6 @@ end_insert:
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
||||
*/
|
||||
int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
|
||||
int rc = SQLITE_OK;
|
||||
BtShared *pBt = pDest->pBt;
|
||||
u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */
|
||||
const u8 *aIn; /* Pointer to next input buffer */
|
||||
@@ -9324,7 +9333,9 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
|
||||
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
|
||||
memcpy(aOut, aIn, nIn);
|
||||
pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
int rc = SQLITE_OK;
|
||||
Pager *pSrcPager = pSrc->pBt->pPager;
|
||||
u8 *pPgnoOut = 0;
|
||||
Pgno ovflIn = 0;
|
||||
@@ -9376,7 +9387,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
|
||||
MemPage *pNew = 0;
|
||||
rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
|
||||
put4byte(pPgnoOut, pgnoNew);
|
||||
if( ISAUTOVACUUM && pPageOut ){
|
||||
if( ISAUTOVACUUM(pBt) && pPageOut ){
|
||||
ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
|
||||
}
|
||||
releasePage(pPageOut);
|
||||
@@ -9392,9 +9403,8 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
|
||||
|
||||
releasePage(pPageOut);
|
||||
sqlite3PagerUnref(pPageIn);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -9549,7 +9559,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
assert( pTmp!=0 );
|
||||
rc = sqlite3PagerWrite(pLeaf->pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
|
||||
rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n);
|
||||
}
|
||||
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
|
||||
if( rc ) return rc;
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ int sqlite3BtreeNewDb(Btree *p);
|
||||
** reduce network bandwidth.
|
||||
**
|
||||
** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by
|
||||
** standard SQLite. The other hints are provided for extentions that use
|
||||
** standard SQLite. The other hints are provided for extensions that use
|
||||
** the SQLite parser and code generator but substitute their own storage
|
||||
** engine.
|
||||
*/
|
||||
|
||||
+2
-2
@@ -674,9 +674,9 @@ struct BtCursor {
|
||||
** So, this macro is defined instead.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
#define ISAUTOVACUUM (pBt->autoVacuum)
|
||||
#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
|
||||
#else
|
||||
#define ISAUTOVACUUM 0
|
||||
#define ISAUTOVACUUM(pBt) 0
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
+118
-74
@@ -53,7 +53,7 @@ char sqlite3ExprAffinity(const Expr *pExpr){
|
||||
}
|
||||
op = pExpr->op;
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
if( op==TK_COLUMN || op==TK_AGG_COLUMN ){
|
||||
if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
assert( pExpr->y.pTab!=0 );
|
||||
return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
|
||||
@@ -173,7 +173,9 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){
|
||||
while( p ){
|
||||
int op = p->op;
|
||||
if( op==TK_REGISTER ) op = p->op2;
|
||||
if( op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER ){
|
||||
if( (op==TK_AGG_COLUMN && p->y.pTab!=0)
|
||||
|| op==TK_COLUMN || op==TK_TRIGGER
|
||||
){
|
||||
int j;
|
||||
assert( ExprUseYTab(p) );
|
||||
assert( p->y.pTab!=0 );
|
||||
@@ -4037,7 +4039,7 @@ static int exprCodeInlineFunction(
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if pExpr is one of the indexed expressions on pParse->pIdxExpr.
|
||||
** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr.
|
||||
** If it is, then resolve the expression by reading from the index and
|
||||
** return the register into which the value has been read. If pExpr is
|
||||
** not an indexed expression, then return negative.
|
||||
@@ -4049,7 +4051,7 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
|
||||
){
|
||||
IndexedExpr *p;
|
||||
Vdbe *v;
|
||||
for(p=pParse->pIdxExpr; p; p=p->pIENext){
|
||||
for(p=pParse->pIdxEpr; p; p=p->pIENext){
|
||||
int iDataCur = p->iDataCur;
|
||||
if( iDataCur<0 ) continue;
|
||||
if( pParse->iSelfTab ){
|
||||
@@ -4069,10 +4071,10 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
|
||||
sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
|
||||
VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol));
|
||||
sqlite3VdbeGoto(v, 0);
|
||||
p = pParse->pIdxExpr;
|
||||
pParse->pIdxExpr = 0;
|
||||
p = pParse->pIdxEpr;
|
||||
pParse->pIdxEpr = 0;
|
||||
sqlite3ExprCode(pParse, pExpr, target);
|
||||
pParse->pIdxExpr = p;
|
||||
pParse->pIdxEpr = p;
|
||||
sqlite3VdbeJumpHere(v, addr+2);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
|
||||
@@ -4111,7 +4113,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
expr_code_doover:
|
||||
if( pExpr==0 ){
|
||||
op = TK_NULL;
|
||||
}else if( pParse->pIdxExpr!=0
|
||||
}else if( pParse->pIdxEpr!=0
|
||||
&& !ExprHasProperty(pExpr, EP_Leaf)
|
||||
&& (r1 = sqlite3IndexedExprLookup(pParse, pExpr, target))>=0
|
||||
){
|
||||
@@ -4128,15 +4130,16 @@ expr_code_doover:
|
||||
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
|
||||
pCol = &pAggInfo->aCol[pExpr->iAgg];
|
||||
if( !pAggInfo->directMode ){
|
||||
assert( pCol->iMem>0 );
|
||||
return pCol->iMem;
|
||||
return AggInfoColumnReg(pAggInfo, pExpr->iAgg);
|
||||
}else if( pAggInfo->useSortingIdx ){
|
||||
Table *pTab = pCol->pTab;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
|
||||
pCol->iSorterColumn, target);
|
||||
if( pCol->iColumn<0 ){
|
||||
if( pTab==0 ){
|
||||
/* No comment added */
|
||||
}else if( pCol->iColumn<0 ){
|
||||
VdbeComment((v,"%s.rowid",pTab->zName));
|
||||
}else if( ALWAYS(pTab!=0) ){
|
||||
}else{
|
||||
VdbeComment((v,"%s.%s",
|
||||
pTab->zName, pTab->aCol[pCol->iColumn].zCnName));
|
||||
if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){
|
||||
@@ -4144,6 +4147,11 @@ expr_code_doover:
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}else if( pExpr->y.pTab==0 ){
|
||||
/* This case happens when the argument to an aggregate function
|
||||
** is rewritten by aggregateConvertIndexedExprRefToColumn() */
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, pExpr->iColumn, target);
|
||||
return target;
|
||||
}
|
||||
/* Otherwise, fall thru into the TK_COLUMN case */
|
||||
/* no break */ deliberate_fall_through
|
||||
@@ -4441,7 +4449,7 @@ expr_code_doover:
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr);
|
||||
}else{
|
||||
return pInfo->aFunc[pExpr->iAgg].iMem;
|
||||
return AggInfoFuncReg(pInfo, pExpr->iAgg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4730,7 +4738,7 @@ expr_code_doover:
|
||||
if( pAggInfo ){
|
||||
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
|
||||
if( !pAggInfo->directMode ){
|
||||
inReg = pAggInfo->aCol[pExpr->iAgg].iMem;
|
||||
inReg = AggInfoColumnReg(pAggInfo, pExpr->iAgg);
|
||||
break;
|
||||
}
|
||||
if( pExpr->pAggInfo->useSortingIdx ){
|
||||
@@ -6242,6 +6250,72 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search the AggInfo object for an aCol[] entry that has iTable and iColumn.
|
||||
** Return the index in aCol[] of the entry that describes that column.
|
||||
**
|
||||
** If no prior entry is found, create a new one and return -1. The
|
||||
** new column will have an idex of pAggInfo->nColumn-1.
|
||||
*/
|
||||
static void findOrCreateAggInfoColumn(
|
||||
Parse *pParse, /* Parsing context */
|
||||
AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */
|
||||
Expr *pExpr /* Expr describing the column to find or insert */
|
||||
){
|
||||
struct AggInfo_col *pCol;
|
||||
int k;
|
||||
|
||||
assert( pAggInfo->iFirstReg==0 );
|
||||
pCol = pAggInfo->aCol;
|
||||
for(k=0; k<pAggInfo->nColumn; k++, pCol++){
|
||||
if( pCol->iTable==pExpr->iTable
|
||||
&& pCol->iColumn==pExpr->iColumn
|
||||
&& pExpr->op!=TK_IF_NULL_ROW
|
||||
){
|
||||
goto fix_up_expr;
|
||||
}
|
||||
}
|
||||
k = addAggInfoColumn(pParse->db, pAggInfo);
|
||||
if( k<0 ){
|
||||
/* OOM on resize */
|
||||
assert( pParse->db->mallocFailed );
|
||||
return;
|
||||
}
|
||||
pCol = &pAggInfo->aCol[k];
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
pCol->pTab = pExpr->y.pTab;
|
||||
pCol->iTable = pExpr->iTable;
|
||||
pCol->iColumn = pExpr->iColumn;
|
||||
pCol->iSorterColumn = -1;
|
||||
pCol->pCExpr = pExpr;
|
||||
if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){
|
||||
int j, n;
|
||||
ExprList *pGB = pAggInfo->pGroupBy;
|
||||
struct ExprList_item *pTerm = pGB->a;
|
||||
n = pGB->nExpr;
|
||||
for(j=0; j<n; j++, pTerm++){
|
||||
Expr *pE = pTerm->pExpr;
|
||||
if( pE->op==TK_COLUMN
|
||||
&& pE->iTable==pExpr->iTable
|
||||
&& pE->iColumn==pExpr->iColumn
|
||||
){
|
||||
pCol->iSorterColumn = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pCol->iSorterColumn<0 ){
|
||||
pCol->iSorterColumn = pAggInfo->nSortingColumn++;
|
||||
}
|
||||
fix_up_expr:
|
||||
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
||||
pExpr->pAggInfo = pAggInfo;
|
||||
if( pExpr->op==TK_COLUMN ){
|
||||
pExpr->op = TK_AGG_COLUMN;
|
||||
}
|
||||
pExpr->iAgg = (i16)k;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xExprCallback for a tree walker. It is used to
|
||||
** implement sqlite3ExprAnalyzeAggregates(). See sqlite3ExprAnalyzeAggregates
|
||||
@@ -6255,7 +6329,36 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
AggInfo *pAggInfo = pNC->uNC.pAggInfo;
|
||||
|
||||
assert( pNC->ncFlags & NC_UAggInfo );
|
||||
assert( pAggInfo->iFirstReg==0 );
|
||||
switch( pExpr->op ){
|
||||
default: {
|
||||
IndexedExpr *pIEpr;
|
||||
Expr tmp;
|
||||
assert( pParse->iSelfTab==0 );
|
||||
if( (pNC->ncFlags & NC_InAggFunc)==0 ) break;
|
||||
if( pParse->pIdxEpr==0 ) break;
|
||||
for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
|
||||
int iDataCur = pIEpr->iDataCur;
|
||||
if( iDataCur<0 ) continue;
|
||||
if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break;
|
||||
}
|
||||
if( pIEpr==0 ) break;
|
||||
if( NEVER(!ExprUseYTab(pExpr)) ) break;
|
||||
|
||||
/* If we reach this point, it means that expression pExpr can be
|
||||
** translated into a reference to an index column as described by
|
||||
** pIEpr.
|
||||
*/
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
tmp.op = TK_AGG_COLUMN;
|
||||
tmp.iTable = pIEpr->iIdxCur;
|
||||
tmp.iColumn = pIEpr->iIdxCol;
|
||||
findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp);
|
||||
pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr;
|
||||
pExpr->pAggInfo = pAggInfo;
|
||||
pExpr->iAgg = tmp.iAgg;
|
||||
return WRC_Prune;
|
||||
}
|
||||
case TK_IF_NULL_ROW:
|
||||
case TK_AGG_COLUMN:
|
||||
case TK_COLUMN: {
|
||||
@@ -6267,67 +6370,9 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
if( ALWAYS(pSrcList!=0) ){
|
||||
SrcItem *pItem = pSrcList->a;
|
||||
for(i=0; i<pSrcList->nSrc; i++, pItem++){
|
||||
struct AggInfo_col *pCol;
|
||||
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
|
||||
if( pExpr->iTable==pItem->iCursor ){
|
||||
/* If we reach this point, it means that pExpr refers to a table
|
||||
** that is in the FROM clause of the aggregate query.
|
||||
**
|
||||
** Make an entry for the column in pAggInfo->aCol[] if there
|
||||
** is not an entry there already.
|
||||
*/
|
||||
int k;
|
||||
pCol = pAggInfo->aCol;
|
||||
for(k=0; k<pAggInfo->nColumn; k++, pCol++){
|
||||
if( pCol->iTable==pExpr->iTable
|
||||
&& pCol->iColumn==pExpr->iColumn
|
||||
&& pExpr->op!=TK_IF_NULL_ROW
|
||||
){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( (k>=pAggInfo->nColumn)
|
||||
&& (k = addAggInfoColumn(pParse->db, pAggInfo))>=0
|
||||
){
|
||||
pCol = &pAggInfo->aCol[k];
|
||||
assert( ExprUseYTab(pExpr) );
|
||||
pCol->pTab = pExpr->y.pTab;
|
||||
pCol->iTable = pExpr->iTable;
|
||||
pCol->iColumn = pExpr->iColumn;
|
||||
pCol->iMem = ++pParse->nMem;
|
||||
pCol->iSorterColumn = -1;
|
||||
pCol->pCExpr = pExpr;
|
||||
if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){
|
||||
int j, n;
|
||||
ExprList *pGB = pAggInfo->pGroupBy;
|
||||
struct ExprList_item *pTerm = pGB->a;
|
||||
n = pGB->nExpr;
|
||||
for(j=0; j<n; j++, pTerm++){
|
||||
Expr *pE = pTerm->pExpr;
|
||||
if( pE->op==TK_COLUMN
|
||||
&& pE->iTable==pExpr->iTable
|
||||
&& pE->iColumn==pExpr->iColumn
|
||||
){
|
||||
pCol->iSorterColumn = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pCol->iSorterColumn<0 ){
|
||||
pCol->iSorterColumn = pAggInfo->nSortingColumn++;
|
||||
}
|
||||
}
|
||||
/* There is now an entry for pExpr in pAggInfo->aCol[] (either
|
||||
** because it was there before or because we just created it).
|
||||
** Convert the pExpr to be a TK_AGG_COLUMN referring to that
|
||||
** pAggInfo->aCol[] entry.
|
||||
*/
|
||||
ExprSetVVAProperty(pExpr, EP_NoReduce);
|
||||
pExpr->pAggInfo = pAggInfo;
|
||||
if( pExpr->op==TK_COLUMN ){
|
||||
pExpr->op = TK_AGG_COLUMN;
|
||||
}
|
||||
pExpr->iAgg = (i16)k;
|
||||
findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr);
|
||||
break;
|
||||
} /* endif pExpr->iTable==pItem->iCursor */
|
||||
} /* end loop over pSrcList */
|
||||
@@ -6357,7 +6402,6 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pItem = &pAggInfo->aFunc[i];
|
||||
pItem->pFExpr = pExpr;
|
||||
pItem->iMem = ++pParse->nMem;
|
||||
assert( ExprUseUToken(pExpr) );
|
||||
pItem->pFunc = sqlite3FindFunction(pParse->db,
|
||||
pExpr->u.zToken,
|
||||
|
||||
+3
-5
@@ -2106,17 +2106,15 @@ static void logFunc(
|
||||
}
|
||||
ans = log(x)/b;
|
||||
}else{
|
||||
ans = log(x);
|
||||
switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
|
||||
case 1:
|
||||
/* Convert from natural logarithm to log base 10 */
|
||||
ans /= M_LN10;
|
||||
ans = log10(x);
|
||||
break;
|
||||
case 2:
|
||||
/* Convert from natural logarithm to log base 2 */
|
||||
ans /= M_LN2;
|
||||
ans = log2(x);
|
||||
break;
|
||||
default:
|
||||
ans = log(x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -510,7 +510,7 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
#endif
|
||||
sqlite3_db_name,
|
||||
/* Version 3.40.0 and later */
|
||||
sqlite3_value_type
|
||||
sqlite3_value_encoding
|
||||
};
|
||||
|
||||
/* True if x is the directory separator character
|
||||
|
||||
+6
-1
@@ -793,9 +793,14 @@ char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
|
||||
*/
|
||||
char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
|
||||
int n;
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Because of the way the parser works, the span is guaranteed to contain
|
||||
** at least one non-space character */
|
||||
for(n=0; sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n]<zEnd ); }
|
||||
#endif
|
||||
while( sqlite3Isspace(zStart[0]) ) zStart++;
|
||||
n = (int)(zEnd - zStart);
|
||||
while( ALWAYS(n>0) && sqlite3Isspace(zStart[n-1]) ) n--;
|
||||
while( sqlite3Isspace(zStart[n-1]) ) n--;
|
||||
return sqlite3DbStrNDup(db, zStart, n);
|
||||
}
|
||||
|
||||
|
||||
@@ -857,6 +857,13 @@ end_deserialize:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the VFS is the memvfs.
|
||||
*/
|
||||
int sqlite3IsMemdb(const sqlite3_vfs *pVfs){
|
||||
return pVfs==&memdb_vfs;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called when the extension is loaded.
|
||||
** Register the new VFS.
|
||||
|
||||
+3
-2
@@ -4725,9 +4725,10 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){
|
||||
}
|
||||
|
||||
/*
|
||||
** If sqlite3_temp_directory is not, take the mutex and return true.
|
||||
** If sqlite3_temp_directory is defined, take the mutex and return true.
|
||||
**
|
||||
** If sqlite3_temp_directory is NULL, omit the mutex and return false.
|
||||
** If sqlite3_temp_directory is NULL (undefined), omit the mutex and
|
||||
** return false.
|
||||
*/
|
||||
static int winTempDirDefined(void){
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||
|
||||
+5
-1
@@ -7009,7 +7009,11 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
*/
|
||||
const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){
|
||||
static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename;
|
||||
if( nullIfMemDb && (pPager->memDb || sqlite3IsMemdb(pPager->pVfs)) ){
|
||||
return &zFake[4];
|
||||
}else{
|
||||
return pPager->zFilename;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -2170,6 +2170,11 @@ void sqlite3Pragma(
|
||||
aOp[1].p2 = iCookie;
|
||||
aOp[1].p3 = sqlite3Atoi(zRight);
|
||||
aOp[1].p5 = 1;
|
||||
if( iCookie==BTREE_SCHEMA_VERSION && (db->flags & SQLITE_Defensive)!=0 ){
|
||||
/* Do not allow the use of PRAGMA schema_version=VALUE in defensive
|
||||
** mode. Change the OP_SetCookie opcode into a no-op. */
|
||||
aOp[1].opcode = OP_Noop;
|
||||
}
|
||||
}else{
|
||||
/* Read the specified cookie value */
|
||||
static const VdbeOpList readCookie[] = {
|
||||
|
||||
+1
-1
@@ -520,8 +520,8 @@ static void schemaIsValid(Parse *pParse){
|
||||
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
|
||||
if( DbHasProperty(db, iDb, DB_SchemaLoaded) ) pParse->rc = SQLITE_SCHEMA;
|
||||
sqlite3ResetOneSchema(db, iDb);
|
||||
pParse->rc = SQLITE_SCHEMA;
|
||||
}
|
||||
|
||||
/* Close the transaction, if one was opened. */
|
||||
|
||||
+4
-4
@@ -31,10 +31,10 @@ static SQLITE_WSD struct sqlite3PrngType {
|
||||
/* The RFC-7539 ChaCha20 block function
|
||||
*/
|
||||
#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
|
||||
#define QR(a, b, c, d) ( \
|
||||
a += b, d ^= a, d = ROTL(d,16), \
|
||||
c += d, b ^= c, b = ROTL(b,12), \
|
||||
a += b, d ^= a, d = ROTL(d, 8), \
|
||||
#define QR(a, b, c, d) ( \
|
||||
a += b, d ^= a, d = ROTL(d,16), \
|
||||
c += d, b ^= c, b = ROTL(b,12), \
|
||||
a += b, d ^= a, d = ROTL(d, 8), \
|
||||
c += d, b ^= c, b = ROTL(b, 7))
|
||||
static void chacha_block(u32 *out, const u32 *in){
|
||||
int i;
|
||||
|
||||
+324
-129
@@ -2091,7 +2091,7 @@ void sqlite3GenerateColumnNames(
|
||||
if( pParse->colNamesSet ) return;
|
||||
/* Column names are determined by the left-most term of a compound select */
|
||||
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
|
||||
SELECTTRACE(1,pParse,pSelect,("generating column names\n"));
|
||||
TREETRACE(0x80,pParse,pSelect,("generating column names\n"));
|
||||
pTabList = pSelect->pSrc;
|
||||
pEList = pSelect->pEList;
|
||||
assert( v!=0 );
|
||||
@@ -2877,7 +2877,7 @@ static int multiSelect(
|
||||
pPrior->iLimit = p->iLimit;
|
||||
pPrior->iOffset = p->iOffset;
|
||||
pPrior->pLimit = p->pLimit;
|
||||
SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL left...\n"));
|
||||
TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n"));
|
||||
rc = sqlite3Select(pParse, pPrior, &dest);
|
||||
pPrior->pLimit = 0;
|
||||
if( rc ){
|
||||
@@ -2895,7 +2895,7 @@ static int multiSelect(
|
||||
}
|
||||
}
|
||||
ExplainQueryPlan((pParse, 1, "UNION ALL"));
|
||||
SELECTTRACE(1, pParse, p, ("multiSelect UNION ALL right...\n"));
|
||||
TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n"));
|
||||
rc = sqlite3Select(pParse, p, &dest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
@@ -2948,7 +2948,7 @@ static int multiSelect(
|
||||
*/
|
||||
assert( !pPrior->pOrderBy );
|
||||
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
|
||||
SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
|
||||
TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
|
||||
rc = sqlite3Select(pParse, pPrior, &uniondest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
@@ -2968,7 +2968,7 @@ static int multiSelect(
|
||||
uniondest.eDest = op;
|
||||
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
|
||||
sqlite3SelectOpName(p->op)));
|
||||
SELECTTRACE(1, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
|
||||
TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
|
||||
rc = sqlite3Select(pParse, p, &uniondest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
assert( p->pOrderBy==0 );
|
||||
@@ -3029,7 +3029,7 @@ static int multiSelect(
|
||||
/* Code the SELECTs to our left into temporary table "tab1".
|
||||
*/
|
||||
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
|
||||
SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT left...\n"));
|
||||
TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n"));
|
||||
rc = sqlite3Select(pParse, pPrior, &intersectdest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
@@ -3046,7 +3046,7 @@ static int multiSelect(
|
||||
intersectdest.iSDParm = tab2;
|
||||
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
|
||||
sqlite3SelectOpName(p->op)));
|
||||
SELECTTRACE(1, pParse, p, ("multiSelect INTERSECT right...\n"));
|
||||
TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n"));
|
||||
rc = sqlite3Select(pParse, p, &intersectdest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
@@ -3693,8 +3693,8 @@ static int multiSelectOrderBy(
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, labelEnd);
|
||||
|
||||
/* Reassemble the compound query so that it will be freed correctly
|
||||
** by the calling function */
|
||||
/* Make arrangements to free the 2nd and subsequent arms of the compound
|
||||
** after the parse has finished */
|
||||
if( pSplit->pPrior ){
|
||||
sqlite3ParserAddCleanup(pParse,
|
||||
(void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
|
||||
@@ -4050,6 +4050,34 @@ static ExprList *findLeftmostExprlist(Select *pSel){
|
||||
return pSel->pEList;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if any of the result-set columns in the compound query
|
||||
** have incompatible affinities on one or more arms of the compound.
|
||||
*/
|
||||
static int compoundHasDifferentAffinities(Select *p){
|
||||
int ii;
|
||||
ExprList *pList;
|
||||
assert( p!=0 );
|
||||
assert( p->pEList!=0 );
|
||||
assert( p->pPrior!=0 );
|
||||
pList = p->pEList;
|
||||
for(ii=0; ii<pList->nExpr; ii++){
|
||||
char aff;
|
||||
Select *pSub1;
|
||||
assert( pList->a[ii].pExpr!=0 );
|
||||
aff = sqlite3ExprAffinity(pList->a[ii].pExpr);
|
||||
for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){
|
||||
assert( pSub1->pEList!=0 );
|
||||
assert( pSub1->pEList->nExpr>ii );
|
||||
assert( pSub1->pEList->a[ii].pExpr!=0 );
|
||||
if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
/*
|
||||
** This routine attempts to flatten subqueries as a performance optimization.
|
||||
@@ -4153,7 +4181,8 @@ static ExprList *findLeftmostExprlist(Select *pSel){
|
||||
** query or there are no RIGHT or FULL JOINs in any arm
|
||||
** of the subquery. (This is a duplicate of condition (27b).)
|
||||
** (17h) The corresponding result set expressions in all arms of the
|
||||
** compound must have the same affinity.
|
||||
** compound must have the same affinity. (See restriction (9)
|
||||
** on the push-down optimization.)
|
||||
**
|
||||
** The parent and sub-query may contain WHERE clauses. Subject to
|
||||
** rules (11), (13) and (14), they may also contain ORDER BY,
|
||||
@@ -4372,19 +4401,7 @@ static int flattenSubquery(
|
||||
if( (p->selFlags & SF_Recursive) ) return 0;
|
||||
|
||||
/* Restriction (17h) */
|
||||
for(ii=0; ii<pSub->pEList->nExpr; ii++){
|
||||
char aff;
|
||||
assert( pSub->pEList->a[ii].pExpr!=0 );
|
||||
aff = sqlite3ExprAffinity(pSub->pEList->a[ii].pExpr);
|
||||
for(pSub1=pSub->pPrior; pSub1; pSub1=pSub1->pPrior){
|
||||
assert( pSub1->pEList!=0 );
|
||||
assert( pSub1->pEList->nExpr>ii );
|
||||
assert( pSub1->pEList->a[ii].pExpr!=0 );
|
||||
if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( compoundHasDifferentAffinities(pSub) ) return 0;
|
||||
|
||||
if( pSrc->nSrc>1 ){
|
||||
if( pParse->nSelect>500 ) return 0;
|
||||
@@ -4395,7 +4412,7 @@ static int flattenSubquery(
|
||||
}
|
||||
|
||||
/***** If we reach this point, flattening is permitted. *****/
|
||||
SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
|
||||
TREETRACE(0x4,pParse,p,("flatten %u.%p from term %d\n",
|
||||
pSub->selId, pSub, iFrom));
|
||||
|
||||
/* Authorize the subquery */
|
||||
@@ -4474,7 +4491,7 @@ static int flattenSubquery(
|
||||
if( pPrior ) pPrior->pNext = pNew;
|
||||
pNew->pNext = p;
|
||||
p->pPrior = pNew;
|
||||
SELECTTRACE(2,pParse,p,("compound-subquery flattener"
|
||||
TREETRACE(0x4,pParse,p,("compound-subquery flattener"
|
||||
" creates %u as peer\n",pNew->selId));
|
||||
}
|
||||
assert( pSubitem->pSelect==0 );
|
||||
@@ -4654,8 +4671,8 @@ static int flattenSubquery(
|
||||
sqlite3SelectDelete(db, pSub1);
|
||||
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
|
||||
if( sqlite3TreeTrace & 0x4 ){
|
||||
TREETRACE(0x4,pParse,p,("After flattening:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -5029,12 +5046,14 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
|
||||
** be materialized. (This restriction is implemented in the calling
|
||||
** routine.)
|
||||
**
|
||||
** (8) The subquery may not be a compound that uses UNION, INTERSECT,
|
||||
** or EXCEPT. (We could, perhaps, relax this restriction to allow
|
||||
** this case if none of the comparisons operators between left and
|
||||
** right arms of the compound use a collation other than BINARY.
|
||||
** But it is a lot of work to check that case for an obscure and
|
||||
** minor optimization, so we omit it for now.)
|
||||
** (8) If the subquery is a compound that uses UNION, INTERSECT,
|
||||
** or EXCEPT, then all of the result set columns for all arms of
|
||||
** the compound must use the BINARY collating sequence.
|
||||
**
|
||||
** (9) If the subquery is a compound, then all arms of the compound must
|
||||
** have the same affinity. (This is the same as restriction (17h)
|
||||
** for query flattening.)
|
||||
**
|
||||
**
|
||||
** Return 0 if no changes are made and non-zero if one or more WHERE clause
|
||||
** terms are duplicated into the subquery.
|
||||
@@ -5051,20 +5070,44 @@ static int pushDownWhereTerms(
|
||||
if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0;
|
||||
if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0;
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( pSubq->pPrior ){
|
||||
Select *pSel;
|
||||
int notUnionAll = 0;
|
||||
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
|
||||
u8 op = pSel->op;
|
||||
assert( op==TK_ALL || op==TK_SELECT
|
||||
|| op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT );
|
||||
if( op!=TK_ALL && op!=TK_SELECT ) return 0; /* restriction (8) */
|
||||
if( op!=TK_ALL && op!=TK_SELECT ){
|
||||
notUnionAll = 1;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( pSel->pWin ) return 0; /* restriction (6b) */
|
||||
#endif
|
||||
}
|
||||
if( compoundHasDifferentAffinities(pSubq) ){
|
||||
return 0; /* restriction (9) */
|
||||
}
|
||||
if( notUnionAll ){
|
||||
/* If any of the compound arms are connected using UNION, INTERSECT,
|
||||
** or EXCEPT, then we must ensure that none of the columns use a
|
||||
** non-BINARY collating sequence. */
|
||||
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
|
||||
int ii;
|
||||
const ExprList *pList = pSel->pEList;
|
||||
assert( pList!=0 );
|
||||
for(ii=0; ii<pList->nExpr; ii++){
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[ii].pExpr);
|
||||
if( !sqlite3IsBinary(pColl) ){
|
||||
return 0; /* Restriction (8) */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Only the first term of a compound can have a WITH clause. But make
|
||||
@@ -6068,8 +6111,8 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
}
|
||||
}
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After result-set wildcard expansion:\n"));
|
||||
if( sqlite3TreeTrace & 0x8 ){
|
||||
TREETRACE(0x8,pParse,p,("After result-set wildcard expansion:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -6204,6 +6247,172 @@ void sqlite3SelectPrep(
|
||||
sqlite3SelectAddTypeInfo(pParse, p);
|
||||
}
|
||||
|
||||
#if TREETRACE_ENABLED
|
||||
/*
|
||||
** Display all information about an AggInfo object
|
||||
*/
|
||||
static void printAggInfo(AggInfo *pAggInfo){
|
||||
int ii;
|
||||
for(ii=0; ii<pAggInfo->nColumn; ii++){
|
||||
struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
|
||||
sqlite3DebugPrintf(
|
||||
"agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d"
|
||||
" iSorterColumn=%d %s\n",
|
||||
ii, pCol->pTab ? pCol->pTab->zName : "NULL",
|
||||
pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii,
|
||||
pCol->iSorterColumn,
|
||||
ii>=pAggInfo->nAccumulator ? "" : " Accumulator");
|
||||
sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
|
||||
}
|
||||
for(ii=0; ii<pAggInfo->nFunc; ii++){
|
||||
sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
|
||||
ii, AggInfoFuncReg(pAggInfo,ii));
|
||||
sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0);
|
||||
}
|
||||
}
|
||||
#endif /* TREETRACE_ENABLED */
|
||||
|
||||
/*
|
||||
** Analyze the arguments to aggregate functions. Create new pAggInfo->aCol[]
|
||||
** entries for columns that are arguments to aggregate functions but which
|
||||
** are not otherwise used.
|
||||
**
|
||||
** The aCol[] entries in AggInfo prior to nAccumulator are columns that
|
||||
** are referenced outside of aggregate functions. These might be columns
|
||||
** that are part of the GROUP by clause, for example. Other database engines
|
||||
** would throw an error if there is a column reference that is not in the
|
||||
** GROUP BY clause and that is not part of an aggregate function argument.
|
||||
** But SQLite allows this.
|
||||
**
|
||||
** The aCol[] entries beginning with the aCol[nAccumulator] and following
|
||||
** are column references that are used exclusively as arguments to
|
||||
** aggregate functions. This routine is responsible for computing
|
||||
** (or recomputing) those aCol[] entries.
|
||||
*/
|
||||
static void analyzeAggFuncArgs(
|
||||
Parse *pParse,
|
||||
AggInfo *pAggInfo,
|
||||
NameContext *pNC
|
||||
){
|
||||
int i;
|
||||
assert( pAggInfo!=0 );
|
||||
assert( pAggInfo->iFirstReg==0 );
|
||||
pNC->ncFlags |= NC_InAggFunc;
|
||||
for(i=0; i<pAggInfo->nFunc; i++){
|
||||
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
|
||||
assert( ExprUseXList(pExpr) );
|
||||
sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
assert( !IsWindowFunc(pExpr) );
|
||||
if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
sqlite3ExprAnalyzeAggregates(pNC, pExpr->y.pWin->pFilter);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pNC->ncFlags &= ~NC_InAggFunc;
|
||||
}
|
||||
|
||||
/*
|
||||
** An index on expressions is being used in the inner loop of an
|
||||
** aggregate query with a GROUP BY clause. This routine attempts
|
||||
** to adjust the AggInfo object to take advantage of index and to
|
||||
** perhaps use the index as a covering index.
|
||||
**
|
||||
*/
|
||||
static void optimizeAggregateUseOfIndexedExpr(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Select *pSelect, /* The SELECT statement being processed */
|
||||
AggInfo *pAggInfo, /* The aggregate info */
|
||||
NameContext *pNC /* Name context used to resolve agg-func args */
|
||||
){
|
||||
assert( pAggInfo->iFirstReg==0 );
|
||||
pAggInfo->nColumn = pAggInfo->nAccumulator;
|
||||
if( ALWAYS(pAggInfo->nSortingColumn>0) ){
|
||||
if( pAggInfo->nColumn==0 ){
|
||||
pAggInfo->nSortingColumn = 0;
|
||||
}else{
|
||||
pAggInfo->nSortingColumn =
|
||||
pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1;
|
||||
}
|
||||
}
|
||||
analyzeAggFuncArgs(pParse, pAggInfo, pNC);
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x20 ){
|
||||
IndexedExpr *pIEpr;
|
||||
TREETRACE(0x20, pParse, pSelect,
|
||||
("AggInfo (possibly) adjusted for Indexed Exprs\n"));
|
||||
sqlite3TreeViewSelect(0, pSelect, 0);
|
||||
for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
|
||||
printf("data-cursor=%d index={%d,%d}\n",
|
||||
pIEpr->iDataCur, pIEpr->iIdxCur, pIEpr->iIdxCol);
|
||||
sqlite3TreeViewExpr(0, pIEpr->pExpr, 0);
|
||||
}
|
||||
printAggInfo(pAggInfo);
|
||||
}
|
||||
#else
|
||||
(void)pSelect; /* Suppress unused-parameter warnings */
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Walker callback for aggregateConvertIndexedExprRefToColumn().
|
||||
*/
|
||||
static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){
|
||||
AggInfo *pAggInfo;
|
||||
struct AggInfo_col *pCol;
|
||||
if( pExpr->pAggInfo==0 ) return WRC_Continue;
|
||||
if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue;
|
||||
if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue;
|
||||
if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue;
|
||||
pAggInfo = pExpr->pAggInfo;
|
||||
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
|
||||
pCol = &pAggInfo->aCol[pExpr->iAgg];
|
||||
pExpr->op = TK_AGG_COLUMN;
|
||||
pExpr->iTable = pCol->iTable;
|
||||
pExpr->iColumn = pCol->iColumn;
|
||||
return WRC_Prune;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert every pAggInfo->aFunc[].pExpr such that any node within
|
||||
** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN
|
||||
** opcode.
|
||||
*/
|
||||
static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){
|
||||
int i;
|
||||
Walker w;
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.xExprCallback = aggregateIdxEprRefToColCallback;
|
||||
for(i=0; i<pAggInfo->nFunc; i++){
|
||||
sqlite3WalkExpr(&w, pAggInfo->aFunc[i].pFExpr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Allocate a block of registers so that there is one register for each
|
||||
** pAggInfo->aCol[] and pAggInfo->aFunc[] entry in pAggInfo. The first
|
||||
** register in this block is stored in pAggInfo->iFirstReg.
|
||||
**
|
||||
** This routine may only be called once for each AggInfo object. Prior
|
||||
** to calling this routine:
|
||||
**
|
||||
** * The aCol[] and aFunc[] arrays may be modified
|
||||
** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used
|
||||
**
|
||||
** After clling this routine:
|
||||
**
|
||||
** * The aCol[] and aFunc[] arrays are fixed
|
||||
** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used
|
||||
**
|
||||
*/
|
||||
static void assignAggregateRegisters(Parse *pParse, AggInfo *pAggInfo){
|
||||
assert( pAggInfo!=0 );
|
||||
assert( pAggInfo->iFirstReg==0 );
|
||||
pAggInfo->iFirstReg = pParse->nMem + 1;
|
||||
pParse->nMem += pAggInfo->nColumn + pAggInfo->nFunc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset the aggregate accumulator.
|
||||
**
|
||||
@@ -6217,24 +6426,13 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
int i;
|
||||
struct AggInfo_func *pFunc;
|
||||
int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
|
||||
assert( pAggInfo->iFirstReg>0 );
|
||||
assert( pParse->db->pParse==pParse );
|
||||
assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
|
||||
if( nReg==0 ) return;
|
||||
if( pParse->nErr ) return;
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Verify that all AggInfo registers are within the range specified by
|
||||
** AggInfo.mnReg..AggInfo.mxReg */
|
||||
assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
|
||||
for(i=0; i<pAggInfo->nColumn; i++){
|
||||
assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg
|
||||
&& pAggInfo->aCol[i].iMem<=pAggInfo->mxReg );
|
||||
}
|
||||
for(i=0; i<pAggInfo->nFunc; i++){
|
||||
assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg
|
||||
&& pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg );
|
||||
}
|
||||
#endif
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg);
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->iFirstReg,
|
||||
pAggInfo->iFirstReg+nReg-1);
|
||||
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
|
||||
if( pFunc->iDistinct>=0 ){
|
||||
Expr *pE = pFunc->pFExpr;
|
||||
@@ -6266,15 +6464,16 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
|
||||
ExprList *pList;
|
||||
assert( ExprUseXList(pF->pFExpr) );
|
||||
pList = pF->pFExpr->x.pList;
|
||||
sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0);
|
||||
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
|
||||
pList ? pList->nExpr : 0);
|
||||
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Update the accumulator memory cells for an aggregate based on
|
||||
** the current cursor position.
|
||||
** Generate code that will update the accumulator memory cells for an
|
||||
** aggregate based on the current cursor position.
|
||||
**
|
||||
** If regAcc is non-zero and there are no min() or max() aggregates
|
||||
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
|
||||
@@ -6294,6 +6493,8 @@ static void updateAccumulator(
|
||||
struct AggInfo_func *pF;
|
||||
struct AggInfo_col *pC;
|
||||
|
||||
assert( pAggInfo->iFirstReg>0 );
|
||||
if( pParse->nErr ) return;
|
||||
pAggInfo->directMode = 1;
|
||||
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
|
||||
int nArg;
|
||||
@@ -6354,7 +6555,7 @@ static void updateAccumulator(
|
||||
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem);
|
||||
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
|
||||
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, (u8)nArg);
|
||||
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
|
||||
@@ -6369,7 +6570,7 @@ static void updateAccumulator(
|
||||
addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v);
|
||||
}
|
||||
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
|
||||
sqlite3ExprCode(pParse, pC->pCExpr, pC->iMem);
|
||||
sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i));
|
||||
}
|
||||
|
||||
pAggInfo->directMode = 0;
|
||||
@@ -6465,7 +6666,7 @@ static void havingToWhere(Parse *pParse, Select *p){
|
||||
sqlite3WalkExpr(&sWalker, p->pHaving);
|
||||
#if TREETRACE_ENABLED
|
||||
if( sWalker.eCode && (sqlite3TreeTrace & 0x100)!=0 ){
|
||||
SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
|
||||
TREETRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -6597,8 +6798,8 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
|
||||
p->selFlags &= ~SF_Aggregate;
|
||||
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x400 ){
|
||||
SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
|
||||
if( sqlite3TreeTrace & 0x200 ){
|
||||
TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -6674,8 +6875,8 @@ int sqlite3Select(
|
||||
assert( db->mallocFailed==0 );
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
|
||||
#if TREETRACE_ENABLED
|
||||
SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
|
||||
if( sqlite3TreeTrace & 0x10100 ){
|
||||
TREETRACE(0x1,pParse,p, ("begin processing:\n", pParse->addrExplain));
|
||||
if( sqlite3TreeTrace & 0x10000 ){
|
||||
if( (sqlite3TreeTrace & 0x10001)==0x10000 ){
|
||||
sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d",
|
||||
__FILE__, __LINE__);
|
||||
@@ -6695,8 +6896,8 @@ int sqlite3Select(
|
||||
/* All of these destinations are also able to ignore the ORDER BY clause */
|
||||
if( p->pOrderBy ){
|
||||
#if TREETRACE_ENABLED
|
||||
SELECTTRACE(1,pParse,p, ("dropping superfluous ORDER BY:\n"));
|
||||
if( sqlite3TreeTrace & 0x100 ){
|
||||
TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
|
||||
if( sqlite3TreeTrace & 0x800 ){
|
||||
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
|
||||
}
|
||||
#endif
|
||||
@@ -6716,8 +6917,8 @@ int sqlite3Select(
|
||||
assert( db->mallocFailed==0 );
|
||||
assert( p->pEList!=0 );
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x104 ){
|
||||
SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
|
||||
if( sqlite3TreeTrace & 0x10 ){
|
||||
TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -6758,8 +6959,8 @@ int sqlite3Select(
|
||||
goto select_end;
|
||||
}
|
||||
#if TREETRACE_ENABLED
|
||||
if( p->pWin && (sqlite3TreeTrace & 0x108)!=0 ){
|
||||
SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
|
||||
if( p->pWin && (sqlite3TreeTrace & 0x40)!=0 ){
|
||||
TREETRACE(0x40,pParse,p, ("after window rewrite:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -6790,7 +6991,7 @@ int sqlite3Select(
|
||||
&& sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
|
||||
&& OptimizationEnabled(db, SQLITE_SimplifyJoin)
|
||||
){
|
||||
SELECTTRACE(0x100,pParse,p,
|
||||
TREETRACE(0x1000,pParse,p,
|
||||
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
|
||||
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
|
||||
assert( pItem->iCursor>=0 );
|
||||
@@ -6846,7 +7047,7 @@ int sqlite3Select(
|
||||
&& (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */
|
||||
&& OptimizationEnabled(db, SQLITE_OmitOrderBy)
|
||||
){
|
||||
SELECTTRACE(0x100,pParse,p,
|
||||
TREETRACE(0x800,pParse,p,
|
||||
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
|
||||
sqlite3ParserAddCleanup(pParse,
|
||||
(void(*)(sqlite3*,void*))sqlite3ExprListDelete,
|
||||
@@ -6901,8 +7102,8 @@ int sqlite3Select(
|
||||
if( p->pPrior ){
|
||||
rc = multiSelect(pParse, p, pDest);
|
||||
#if TREETRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
|
||||
if( (sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
TREETRACE(0x400,pParse,p,("end compound-select processing\n"));
|
||||
if( (sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -6922,13 +7123,13 @@ int sqlite3Select(
|
||||
&& propagateConstants(pParse, p)
|
||||
){
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
|
||||
if( sqlite3TreeTrace & 0x2000 ){
|
||||
TREETRACE(0x2000,pParse,p,("After constant propagation:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n"));
|
||||
TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n"));
|
||||
}
|
||||
|
||||
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
|
||||
@@ -7001,15 +7202,15 @@ int sqlite3Select(
|
||||
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem)
|
||||
){
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x100 ){
|
||||
SELECTTRACE(0x100,pParse,p,
|
||||
if( sqlite3TreeTrace & 0x4000 ){
|
||||
TREETRACE(0x4000,pParse,p,
|
||||
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
|
||||
}else{
|
||||
SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
|
||||
TREETRACE(0x4000,pParse,p,("Push-down not possible\n"));
|
||||
}
|
||||
|
||||
zSavedAuthContext = pParse->zAuthContext;
|
||||
@@ -7124,8 +7325,8 @@ int sqlite3Select(
|
||||
sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
|
||||
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x400 ){
|
||||
SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
|
||||
if( sqlite3TreeTrace & 0x8000 ){
|
||||
TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -7161,8 +7362,8 @@ int sqlite3Select(
|
||||
sDistinct.isTnct = 2;
|
||||
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x400 ){
|
||||
SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
|
||||
if( sqlite3TreeTrace & 0x20000 ){
|
||||
TREETRACE(0x20000,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -7248,7 +7449,7 @@ int sqlite3Select(
|
||||
|
||||
|
||||
/* Begin the database scan. */
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereBegin\n"));
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
|
||||
p->pEList, p, wctrlFlags, p->nSelectRow);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
@@ -7265,7 +7466,7 @@ int sqlite3Select(
|
||||
sSort.pOrderBy = 0;
|
||||
}
|
||||
}
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
|
||||
|
||||
/* If sorting index that was created by a prior OP_OpenEphemeral
|
||||
** instruction ended up not being needed, then change the OP_OpenEphemeral
|
||||
@@ -7304,7 +7505,7 @@ int sqlite3Select(
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
}else{
|
||||
@@ -7390,7 +7591,6 @@ int sqlite3Select(
|
||||
sNC.pSrcList = pTabList;
|
||||
sNC.uNC.pAggInfo = pAggInfo;
|
||||
VVA_ONLY( sNC.ncFlags = NC_UAggInfo; )
|
||||
pAggInfo->mnReg = pParse->nMem+1;
|
||||
pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
|
||||
pAggInfo->pGroupBy = pGroupBy;
|
||||
sqlite3ExprAnalyzeAggList(&sNC, pEList);
|
||||
@@ -7411,45 +7611,17 @@ int sqlite3Select(
|
||||
}else{
|
||||
minMaxFlag = WHERE_ORDERBY_NORMAL;
|
||||
}
|
||||
for(i=0; i<pAggInfo->nFunc; i++){
|
||||
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
|
||||
assert( ExprUseXList(pExpr) );
|
||||
sNC.ncFlags |= NC_InAggFunc;
|
||||
sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
assert( !IsWindowFunc(pExpr) );
|
||||
if( ExprHasProperty(pExpr, EP_WinFunc) ){
|
||||
sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter);
|
||||
}
|
||||
#endif
|
||||
sNC.ncFlags &= ~NC_InAggFunc;
|
||||
}
|
||||
pAggInfo->mxReg = pParse->nMem;
|
||||
analyzeAggFuncArgs(pParse, pAggInfo, &sNC);
|
||||
if( db->mallocFailed ) goto select_end;
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x400 ){
|
||||
int ii;
|
||||
SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
|
||||
if( sqlite3TreeTrace & 0x20 ){
|
||||
TREETRACE(0x20,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
if( minMaxFlag ){
|
||||
sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag);
|
||||
sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY");
|
||||
}
|
||||
for(ii=0; ii<pAggInfo->nColumn; ii++){
|
||||
struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
|
||||
sqlite3DebugPrintf(
|
||||
"agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d"
|
||||
" iSorterColumn=%d\n",
|
||||
ii, pCol->pTab ? pCol->pTab->zName : "NULL",
|
||||
pCol->iTable, pCol->iColumn, pCol->iMem,
|
||||
pCol->iSorterColumn);
|
||||
sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
|
||||
}
|
||||
for(ii=0; ii<pAggInfo->nFunc; ii++){
|
||||
sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
|
||||
ii, pAggInfo->aFunc[ii].iMem);
|
||||
sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0);
|
||||
}
|
||||
printAggInfo(pAggInfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -7518,7 +7690,7 @@ int sqlite3Select(
|
||||
** in the right order to begin with.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereBegin\n"));
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct,
|
||||
p, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY)
|
||||
| (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0
|
||||
@@ -7527,8 +7699,12 @@ int sqlite3Select(
|
||||
sqlite3ExprListDelete(db, pDistinct);
|
||||
goto select_end;
|
||||
}
|
||||
if( pParse->pIdxEpr ){
|
||||
optimizeAggregateUseOfIndexedExpr(pParse, p, pAggInfo, &sNC);
|
||||
}
|
||||
assignAggregateRegisters(pParse, pAggInfo);
|
||||
eDist = sqlite3WhereIsDistinct(pWInfo);
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
|
||||
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
|
||||
/* The optimizer is able to deliver rows in group by order so
|
||||
** we do not have to sort. The OP_OpenEphemeral table will be
|
||||
@@ -7577,7 +7753,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
|
||||
sortOut = sqlite3GetTempReg(pParse);
|
||||
@@ -7587,6 +7763,23 @@ int sqlite3Select(
|
||||
pAggInfo->useSortingIdx = 1;
|
||||
}
|
||||
|
||||
/* If there entries in pAgggInfo->aFunc[] that contain subexpressions
|
||||
** that are indexed (and that were previously identified and tagged
|
||||
** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions
|
||||
** must now be converted into a TK_AGG_COLUMN node so that the value
|
||||
** is correctly pulled from the index rather than being recomputed. */
|
||||
if( pParse->pIdxEpr ){
|
||||
aggregateConvertIndexedExprRefToColumn(pAggInfo);
|
||||
#if TREETRACE_ENABLED
|
||||
if( sqlite3TreeTrace & 0x20 ){
|
||||
TREETRACE(0x20, pParse, p,
|
||||
("AggInfo function expressions converted to reference index\n"));
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
printAggInfo(pAggInfo);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If the index or temporary table used by the GROUP BY sort
|
||||
** will naturally deliver rows in the order required by the ORDER BY
|
||||
** clause, cancel the ephemeral table open coded earlier.
|
||||
@@ -7655,7 +7848,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
|
||||
}
|
||||
@@ -7765,7 +7958,8 @@ int sqlite3Select(
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem);
|
||||
assignAggregateRegisters(pParse, pAggInfo);
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0));
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
||||
explainSimpleCount(pParse, pTab, pBest);
|
||||
}else{
|
||||
@@ -7801,6 +7995,7 @@ int sqlite3Select(
|
||||
pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList;
|
||||
distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0;
|
||||
}
|
||||
assignAggregateRegisters(pParse, pAggInfo);
|
||||
|
||||
/* This case runs if the aggregate has no GROUP BY clause. The
|
||||
** processing is much simpler since there is only a single row
|
||||
@@ -7817,13 +8012,13 @@ int sqlite3Select(
|
||||
assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 );
|
||||
assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 );
|
||||
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereBegin\n"));
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
|
||||
pDistinct, p, minMaxFlag|distFlag, 0);
|
||||
if( pWInfo==0 ){
|
||||
goto select_end;
|
||||
}
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
|
||||
eDist = sqlite3WhereIsDistinct(pWInfo);
|
||||
updateAccumulator(pParse, regAcc, pAggInfo, eDist);
|
||||
if( eDist!=WHERE_DISTINCT_NOOP ){
|
||||
@@ -7837,7 +8032,7 @@ int sqlite3Select(
|
||||
if( minMaxFlag ){
|
||||
sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);
|
||||
}
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
TREETRACE(0x2,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
finalizeAggFunctions(pParse, pAggInfo);
|
||||
}
|
||||
@@ -7884,7 +8079,7 @@ select_end:
|
||||
if( pAggInfo && !db->mallocFailed ){
|
||||
for(i=0; i<pAggInfo->nColumn; i++){
|
||||
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
|
||||
assert( pExpr!=0 );
|
||||
if( pExpr==0 ) continue;
|
||||
assert( pExpr->pAggInfo==pAggInfo );
|
||||
assert( pExpr->iAgg==i );
|
||||
}
|
||||
@@ -7898,8 +8093,8 @@ select_end:
|
||||
#endif
|
||||
|
||||
#if TREETRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end processing\n"));
|
||||
if( (sqlite3TreeTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
TREETRACE(0x1,pParse,p,("end processing\n"));
|
||||
if( (sqlite3TreeTrace & 0x40000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
+82
-10
@@ -16,6 +16,8 @@
|
||||
/* This needs to come before any includes for MSVC compiler */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned short int u16;
|
||||
|
||||
/*
|
||||
** Optionally #include a user-defined header, whereby compilation options
|
||||
@@ -1064,9 +1066,14 @@ INCLUDE ../ext/expert/sqlite3expert.c
|
||||
#define SQLITE_SHELL_HAVE_RECOVER 0
|
||||
#endif
|
||||
#if SQLITE_SHELL_HAVE_RECOVER
|
||||
INCLUDE ../ext/recover/dbdata.c
|
||||
INCLUDE ../ext/recover/sqlite3recover.h
|
||||
# ifndef SQLITE_HAVE_SQLITE3R
|
||||
INCLUDE ../ext/recover/dbdata.c
|
||||
INCLUDE ../ext/recover/sqlite3recover.c
|
||||
# endif
|
||||
#endif
|
||||
#ifdef SQLITE_SHELL_EXTSRC
|
||||
# include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC)
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_ENABLE_SESSION)
|
||||
@@ -5113,6 +5120,7 @@ static void open_db(ShellState *p, int openFlags){
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
sqlite3_enable_load_extension(p->db, 1);
|
||||
#endif
|
||||
@@ -5135,6 +5143,34 @@ static void open_db(ShellState *p, int openFlags){
|
||||
sqlite3_sqlar_init(p->db, 0, 0);
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_SHELL_EXTFUNCS
|
||||
/* Create a preprocessing mechanism for extensions to make
|
||||
* their own provisions for being built into the shell.
|
||||
* This is a short-span macro. See further below for usage.
|
||||
*/
|
||||
#define SHELL_SUB_MACRO(base, variant) base ## _ ## variant
|
||||
#define SHELL_SUBMACRO(base, variant) SHELL_SUB_MACRO(base, variant)
|
||||
/* Let custom-included extensions get their ..._init() called.
|
||||
* The WHATEVER_INIT( db, pzErrorMsg, pApi ) macro should cause
|
||||
* the extension's sqlite3_*_init( db, pzErrorMsg, pApi )
|
||||
* inititialization routine to be called.
|
||||
*/
|
||||
{
|
||||
int irc = SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, INIT)(p->db);
|
||||
/* Let custom-included extensions expose their functionality.
|
||||
* The WHATEVER_EXPOSE( db, pzErrorMsg ) macro should cause
|
||||
* the SQL functions, virtual tables, collating sequences or
|
||||
* VFS's implemented by the extension to be registered.
|
||||
*/
|
||||
if( irc==SQLITE_OK
|
||||
|| irc==SQLITE_OK_LOAD_PERMANENTLY ){
|
||||
SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, EXPOSE)(p->db, 0);
|
||||
}
|
||||
#undef SHELL_SUB_MACRO
|
||||
#undef SHELL_SUBMACRO
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
|
||||
shellAddSchemaName, 0, 0);
|
||||
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
|
||||
@@ -5155,6 +5191,7 @@ static void open_db(ShellState *p, int openFlags){
|
||||
sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0,
|
||||
editFunc, 0, 0);
|
||||
#endif
|
||||
|
||||
if( p->openMode==SHELL_OPEN_ZIPFILE ){
|
||||
char *zSql = sqlite3_mprintf(
|
||||
"CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename);
|
||||
@@ -10654,7 +10691,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
}else{
|
||||
output_file_close(p->traceOut);
|
||||
p->traceOut = output_file_open(azArg[1], 0);
|
||||
p->traceOut = output_file_open(z, 0);
|
||||
}
|
||||
}
|
||||
if( p->traceOut==0 ){
|
||||
@@ -10957,13 +10994,11 @@ static int line_is_command_terminator(char *zLine){
|
||||
}
|
||||
|
||||
/*
|
||||
** We need a default sqlite3_complete() implementation to use in case
|
||||
** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes
|
||||
** any arbitrary text is a complete SQL statement. This is not very
|
||||
** user-friendly, but it does seem to work.
|
||||
** The CLI needs a working sqlite3_complete() to work properly. So error
|
||||
** out of the build if compiling with SQLITE_OMIT_COMPLETE.
|
||||
*/
|
||||
#ifdef SQLITE_OMIT_COMPLETE
|
||||
#define sqlite3_complete(x) 1
|
||||
# error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE.
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -11251,9 +11286,43 @@ static char *find_home_dir(int clearFlag){
|
||||
return home_dir;
|
||||
}
|
||||
|
||||
/*
|
||||
** On non-Windows platforms, look for $XDG_CONFIG_HOME.
|
||||
** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
|
||||
** the path to it, else return 0. The result is cached for
|
||||
** subsequent calls.
|
||||
*/
|
||||
static const char *find_xdg_config(void){
|
||||
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
|
||||
|| defined(__RTP__) || defined(_WRS_KERNEL)
|
||||
return 0;
|
||||
#else
|
||||
static int alreadyTried = 0;
|
||||
static char *zConfig = 0;
|
||||
const char *zXdgHome;
|
||||
|
||||
if( alreadyTried!=0 ){
|
||||
return zConfig;
|
||||
}
|
||||
alreadyTried = 1;
|
||||
zXdgHome = getenv("XDG_CONFIG_HOME");
|
||||
if( zXdgHome==0 ){
|
||||
return 0;
|
||||
}
|
||||
zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
|
||||
shell_check_oom(zConfig);
|
||||
if( access(zConfig,0)!=0 ){
|
||||
sqlite3_free(zConfig);
|
||||
zConfig = 0;
|
||||
}
|
||||
return zConfig;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Read input from the file given by sqliterc_override. Or if that
|
||||
** parameter is NULL, take input from ~/.sqliterc
|
||||
** parameter is NULL, take input from the first of find_xdg_config()
|
||||
** or ~/.sqliterc which is found.
|
||||
**
|
||||
** Returns the number of errors.
|
||||
*/
|
||||
@@ -11267,7 +11336,10 @@ static void process_sqliterc(
|
||||
FILE *inSaved = p->in;
|
||||
int savedLineno = p->lineno;
|
||||
|
||||
if (sqliterc == NULL) {
|
||||
if( sqliterc == NULL ){
|
||||
sqliterc = find_xdg_config();
|
||||
}
|
||||
if( sqliterc == NULL ){
|
||||
home_dir = find_home_dir(0);
|
||||
if( home_dir==0 ){
|
||||
raw_printf(stderr, "-- warning: cannot find home directory;"
|
||||
@@ -11730,7 +11802,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
|
||||
if( pVfs ){
|
||||
sqlite3_vfs_register(pVfs, 1);
|
||||
}else{
|
||||
utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]);
|
||||
utf8_printf(stderr, "no such VFS: \"%s\"\n", zVfs);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-13
@@ -2339,6 +2339,7 @@ struct sqlite3_mem_methods {
|
||||
** <ul>
|
||||
** <li> The [PRAGMA writable_schema=ON] statement.
|
||||
** <li> The [PRAGMA journal_mode=OFF] statement.
|
||||
** <li> The [PRAGMA schema_version=N] statement.
|
||||
** <li> Writes to the [sqlite_dbpage] virtual table.
|
||||
** <li> Direct writes to [shadow tables].
|
||||
** </ul>
|
||||
@@ -5541,16 +5542,6 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
|
||||
** then the conversion is performed. Otherwise no conversion occurs.
|
||||
** The [SQLITE_INTEGER | datatype] after conversion is returned.)^
|
||||
**
|
||||
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
|
||||
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current encoding
|
||||
** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
|
||||
** returns something other than SQLITE_TEXT, then the return value from
|
||||
** sqlite3_value_encoding(X) is meaningless. ^Calls to
|
||||
** sqlite3_value_text(X), sqlite3_value_text16(X), sqlite3_value_text16be(X),
|
||||
** sqlite3_value_text16le(X), sqlite3_value_bytes(X), or
|
||||
** sqlite3_value_bytes16(X) might change the encoding of the value X and
|
||||
** thus change the return from subsequent calls to sqlite3_value_encoding(X).
|
||||
**
|
||||
** ^Within the [xUpdate] method of a [virtual table], the
|
||||
** sqlite3_value_nochange(X) interface returns true if and only if
|
||||
** the column corresponding to X is unchanged by the UPDATE operation
|
||||
@@ -5615,6 +5606,27 @@ int sqlite3_value_type(sqlite3_value*);
|
||||
int sqlite3_value_numeric_type(sqlite3_value*);
|
||||
int sqlite3_value_nochange(sqlite3_value*);
|
||||
int sqlite3_value_frombind(sqlite3_value*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Report the internal text encoding state of an sqlite3_value object
|
||||
** METHOD: sqlite3_value
|
||||
**
|
||||
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
|
||||
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding
|
||||
** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
|
||||
** returns something other than SQLITE_TEXT, then the return value from
|
||||
** sqlite3_value_encoding(X) is meaningless. ^Calls to
|
||||
** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
|
||||
** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
|
||||
** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
|
||||
** thus change the return from subsequent calls to sqlite3_value_encoding(X).
|
||||
**
|
||||
** This routine is intended for used by applications that test and validate
|
||||
** the SQLite implementation. This routine is inquiring about the opaque
|
||||
** internal state of an [sqlite3_value] object. Ordinary applications should
|
||||
** not need to know what the internal state of an sqlite3_value object is and
|
||||
** hence should not need to use this interface.
|
||||
*/
|
||||
int sqlite3_value_encoding(sqlite3_value*);
|
||||
|
||||
/*
|
||||
@@ -5874,9 +5886,10 @@ typedef void (*sqlite3_destructor_type)(void*);
|
||||
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
|
||||
** ^SQLite takes the text result from the application from
|
||||
** the 2nd parameter of the sqlite3_result_text* interfaces.
|
||||
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
||||
** is negative, then SQLite takes result text from the 2nd parameter
|
||||
** through the first zero character.
|
||||
** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
|
||||
** other than sqlite3_result_text64() is negative, then SQLite computes
|
||||
** the string length itself by searching the 2nd parameter for the first
|
||||
** zero character.
|
||||
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
||||
** is non-negative, then as many bytes (not characters) of the text
|
||||
** pointed to by the 2nd parameter are taken as the application-defined
|
||||
|
||||
+42
-7
@@ -1029,15 +1029,38 @@ extern u32 sqlite3TreeTrace;
|
||||
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \
|
||||
|| defined(SQLITE_ENABLE_TREETRACE))
|
||||
# define TREETRACE_ENABLED 1
|
||||
# define SELECTTRACE(K,P,S,X) \
|
||||
# define TREETRACE(K,P,S,X) \
|
||||
if(sqlite3TreeTrace&(K)) \
|
||||
sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
|
||||
sqlite3DebugPrintf X
|
||||
#else
|
||||
# define SELECTTRACE(K,P,S,X)
|
||||
# define TREETRACE(K,P,S,X)
|
||||
# define TREETRACE_ENABLED 0
|
||||
#endif
|
||||
|
||||
/* TREETRACE flag meanings:
|
||||
**
|
||||
** 0x00000001 Beginning and end of SELECT processing
|
||||
** 0x00000002 WHERE clause processing
|
||||
** 0x00000004 Query flattener
|
||||
** 0x00000008 Result-set wildcard expansion
|
||||
** 0x00000010 Query name resolution
|
||||
** 0x00000020 Aggregate analysis
|
||||
** 0x00000040 Window functions
|
||||
** 0x00000080 Generated column names
|
||||
** 0x00000100 Move HAVING terms into WHERE
|
||||
** 0x00000200 Count-of-view optimization
|
||||
** 0x00000400 Compound SELECT processing
|
||||
** 0x00000800 Drop superfluous ORDER BY
|
||||
** 0x00001000 LEFT JOIN simplifies to JOIN
|
||||
** 0x00002000 Constant propagation
|
||||
** 0x00004000 Push-down optimization
|
||||
** 0x00008000 After all FROM-clause analysis
|
||||
** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing
|
||||
** 0x00020000 Transform DISTINCT into GROUP BY
|
||||
** 0x00040000 SELECT tree dump after all code has been generated
|
||||
*/
|
||||
|
||||
/*
|
||||
** Macros for "wheretrace"
|
||||
*/
|
||||
@@ -2716,16 +2739,15 @@ struct AggInfo {
|
||||
** from source tables rather than from accumulators */
|
||||
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
|
||||
** than the source table */
|
||||
u16 nSortingColumn; /* Number of columns in the sorting index */
|
||||
int sortingIdx; /* Cursor number of the sorting index */
|
||||
int sortingIdxPTab; /* Cursor number of pseudo-table */
|
||||
int nSortingColumn; /* Number of columns in the sorting index */
|
||||
int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */
|
||||
int iFirstReg; /* First register in range for aCol[] and aFunc[] */
|
||||
ExprList *pGroupBy; /* The group by clause */
|
||||
struct AggInfo_col { /* For each column used in source tables */
|
||||
Table *pTab; /* Source table */
|
||||
Expr *pCExpr; /* The original expression */
|
||||
int iTable; /* Cursor number of the source table */
|
||||
int iMem; /* Memory location that acts as accumulator */
|
||||
i16 iColumn; /* Column number within the source table */
|
||||
i16 iSorterColumn; /* Column number in the sorting index */
|
||||
} *aCol;
|
||||
@@ -2736,7 +2758,6 @@ struct AggInfo {
|
||||
struct AggInfo_func { /* For each aggregate function */
|
||||
Expr *pFExpr; /* Expression encoding the function */
|
||||
FuncDef *pFunc; /* The aggregate function implementation */
|
||||
int iMem; /* Memory location that acts as accumulator */
|
||||
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
|
||||
int iDistAddr; /* Address of OP_OpenEphemeral */
|
||||
} *aFunc;
|
||||
@@ -2744,6 +2765,17 @@ struct AggInfo {
|
||||
u32 selId; /* Select to which this AggInfo belongs */
|
||||
};
|
||||
|
||||
/*
|
||||
** Macros to compute aCol[] and aFunc[] register numbers.
|
||||
**
|
||||
** These macros should not be used prior to the call to
|
||||
** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg.
|
||||
** The assert()s that are part of this macro verify that constraint.
|
||||
*/
|
||||
#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I))
|
||||
#define AggInfoFuncReg(A,I) \
|
||||
(assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I))
|
||||
|
||||
/*
|
||||
** The datatype ynVar is a signed integer, either 16-bit or 32-bit.
|
||||
** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater
|
||||
@@ -3656,7 +3688,7 @@ struct Parse {
|
||||
int nLabelAlloc; /* Number of slots in aLabel */
|
||||
int *aLabel; /* Space to hold the labels */
|
||||
ExprList *pConstExpr;/* Constant expressions */
|
||||
IndexedExpr *pIdxExpr;/* List of expressions used by active indexes */
|
||||
IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
|
||||
Token constraintName;/* Name of the constraint currently being parsed */
|
||||
yDbMask writeMask; /* Start a write transaction on these databases */
|
||||
yDbMask cookieMask; /* Bitmask of schema verified databases */
|
||||
@@ -5003,6 +5035,9 @@ const char *sqlite3ErrName(int);
|
||||
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
int sqlite3MemdbInit(void);
|
||||
int sqlite3IsMemdb(const sqlite3_vfs*);
|
||||
#else
|
||||
# define sqlite3IsMemdb(X) 0
|
||||
#endif
|
||||
|
||||
const char *sqlite3ErrStr(int);
|
||||
|
||||
@@ -7709,6 +7709,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
|
||||
){
|
||||
extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_appendvfs_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_basexx_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_carray_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
extern int sqlite3_csv_init(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
@@ -7740,6 +7741,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
|
||||
} aExtension[] = {
|
||||
{ "amatch", sqlite3_amatch_init },
|
||||
{ "appendvfs", sqlite3_appendvfs_init },
|
||||
{ "basexx", sqlite3_basexx_init },
|
||||
{ "carray", sqlite3_carray_init },
|
||||
{ "closure", sqlite3_closure_init },
|
||||
{ "csv", sqlite3_csv_init },
|
||||
|
||||
@@ -521,6 +521,14 @@ static int SQLITE_TCLAPI fake_big_file(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
|
||||
#if defined(_WIN32)
|
||||
if( n>2 ){
|
||||
Tcl_AppendResult(interp, "cannot create ", argv[1],
|
||||
"MB file because Windows "
|
||||
"does not support sparse files", (void*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
pVfs = sqlite3_vfs_find(0);
|
||||
nFile = (int)strlen(argv[2]);
|
||||
|
||||
+4
-1
@@ -487,7 +487,7 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
sqlite3TreeViewPop(&pView);
|
||||
return;
|
||||
}
|
||||
if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){
|
||||
if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){
|
||||
StrAccum x;
|
||||
sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0);
|
||||
sqlite3_str_appendf(&x, " fg.af=%x.%c",
|
||||
@@ -504,6 +504,9 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
if( ExprHasVVAProperty(pExpr, EP_Immutable) ){
|
||||
sqlite3_str_appendf(&x, " IMMUTABLE");
|
||||
}
|
||||
if( pExpr->pAggInfo!=0 ){
|
||||
sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg);
|
||||
}
|
||||
sqlite3StrAccumFinish(&x);
|
||||
}else{
|
||||
zFlgs[0] = 0;
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
|
||||
if( pTrig->pTabSchema==pTab->pSchema
|
||||
&& pTrig->table
|
||||
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
|
||||
&& pTrig->pTabSchema!=pTmpSchema
|
||||
&& (pTrig->pTabSchema!=pTmpSchema || pTrig->bReturning)
|
||||
){
|
||||
pTrig->pNext = pList;
|
||||
pList = pTrig;
|
||||
|
||||
@@ -5579,6 +5579,7 @@ case OP_Insert: {
|
||||
x.nZero = 0;
|
||||
}
|
||||
x.pKey = 0;
|
||||
assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
|
||||
seekResult
|
||||
|
||||
+7
-2
@@ -4575,7 +4575,7 @@ int sqlite3VdbeRecordCompareWithSkip(
|
||||
assert( pPKey2->pKeyInfo->aSortFlags!=0 );
|
||||
assert( pPKey2->pKeyInfo->nKeyField>0 );
|
||||
assert( idx1<=szHdr1 || CORRUPT_DB );
|
||||
do{
|
||||
while( 1 /*exit-by-break*/ ){
|
||||
u32 serial_type;
|
||||
|
||||
/* RHS is an integer */
|
||||
@@ -4713,8 +4713,13 @@ int sqlite3VdbeRecordCompareWithSkip(
|
||||
if( i==pPKey2->nField ) break;
|
||||
pRhs++;
|
||||
d1 += sqlite3VdbeSerialTypeLen(serial_type);
|
||||
if( d1>(unsigned)nKey1 ) break;
|
||||
idx1 += sqlite3VarintLen(serial_type);
|
||||
}while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 );
|
||||
if( idx1>=(unsigned)szHdr1 ){
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corrupt index */
|
||||
}
|
||||
}
|
||||
|
||||
/* No memory allocation is ever used on mem1. Prove this using
|
||||
** the following assert(). If the assert() fails, it indicates a
|
||||
|
||||
+11
-11
@@ -4591,8 +4591,8 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
if( pOBExpr->iTable!=iCur ) continue;
|
||||
if( pOBExpr->iColumn!=iColumn ) continue;
|
||||
}else{
|
||||
Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr;
|
||||
if( sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){
|
||||
Expr *pIxExpr = pIndex->aColExpr->a[j].pExpr;
|
||||
if( sqlite3ExprCompareSkip(pOBExpr, pIxExpr, iCur) ){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -5461,13 +5461,13 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
|
||||
|
||||
/*
|
||||
** This is an sqlite3ParserAddCleanup() callback that is invoked to
|
||||
** free the Parse->pIdxExpr list when the Parse object is destroyed.
|
||||
** free the Parse->pIdxEpr list when the Parse object is destroyed.
|
||||
*/
|
||||
static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
|
||||
Parse *pParse = (Parse*)pObject;
|
||||
while( pParse->pIdxExpr!=0 ){
|
||||
IndexedExpr *p = pParse->pIdxExpr;
|
||||
pParse->pIdxExpr = p->pIENext;
|
||||
while( pParse->pIdxEpr!=0 ){
|
||||
IndexedExpr *p = pParse->pIdxEpr;
|
||||
pParse->pIdxEpr = p->pIENext;
|
||||
sqlite3ExprDelete(db, p->pExpr);
|
||||
sqlite3DbFreeNN(db, p);
|
||||
}
|
||||
@@ -5479,13 +5479,13 @@ static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
|
||||
** number for the index and iDataCur is the cursor number for the corresponding
|
||||
** table.
|
||||
**
|
||||
** This routine adds IndexedExpr entries to the Parse->pIdxExpr field for
|
||||
** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for
|
||||
** each of the expressions in the index so that the expression code generator
|
||||
** will know to replace occurrences of the indexed expression with
|
||||
** references to the corresponding column of the index.
|
||||
*/
|
||||
static SQLITE_NOINLINE void whereAddIndexedExpr(
|
||||
Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxExpr */
|
||||
Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxEpr */
|
||||
Index *pIdx, /* The index-on-expression that contains the expressions */
|
||||
int iIdxCur, /* Cursor number for pIdx */
|
||||
SrcItem *pTabItem /* The FROM clause entry for the table */
|
||||
@@ -5514,7 +5514,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
|
||||
if( sqlite3ExprIsConstant(pExpr) ) continue;
|
||||
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
|
||||
if( p==0 ) break;
|
||||
p->pIENext = pParse->pIdxExpr;
|
||||
p->pIENext = pParse->pIdxEpr;
|
||||
p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
|
||||
p->iDataCur = pTabItem->iCursor;
|
||||
p->iIdxCur = iIdxCur;
|
||||
@@ -5523,7 +5523,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
p->zIdxName = pIdx->zName;
|
||||
#endif
|
||||
pParse->pIdxExpr = p;
|
||||
pParse->pIdxEpr = p;
|
||||
if( p->pIENext==0 ){
|
||||
sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
|
||||
}
|
||||
@@ -6474,7 +6474,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
last = pWInfo->iEndWhere;
|
||||
}
|
||||
if( pIdx->bHasExpr ){
|
||||
IndexedExpr *p = pParse->pIdxExpr;
|
||||
IndexedExpr *p = pParse->pIdxEpr;
|
||||
while( p ){
|
||||
if( p->iIdxCur==pLevel->iIdxCur ){
|
||||
p->iDataCur = -1;
|
||||
|
||||
+1
-1
@@ -1069,7 +1069,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
|
||||
pSub = sqlite3SelectNew(
|
||||
pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
|
||||
);
|
||||
SELECTTRACE(1,pParse,pSub,
|
||||
TREETRACE(0x40,pParse,pSub,
|
||||
("New window-function subquery in FROM clause of (%u/%p)\n",
|
||||
p->selId, p));
|
||||
p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
# 2022 November 22
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix basexx
|
||||
|
||||
if {[catch {load_static_extension db basexx} error]} {
|
||||
puts "Skipping basexx tests, hit load error: $error"
|
||||
finish_test; return
|
||||
}
|
||||
|
||||
# Empty blobs encode to empty strings.
|
||||
do_execsql_test 100 {
|
||||
SELECT base64(x'')||base85(x'');
|
||||
} {{}}
|
||||
|
||||
# Empty strings decode to empty blobs.
|
||||
do_execsql_test 101 {
|
||||
SELECT hex(x'01'||base64('')||base85('')||x'02');
|
||||
} {0102}
|
||||
|
||||
# Basic base64 encoding
|
||||
do_execsql_test 102 {
|
||||
SELECT base64(x'000102030405');
|
||||
SELECT base64(x'0001020304');
|
||||
SELECT base64(x'00010203');
|
||||
} {{AAECAwQF
|
||||
} {AAECAwQ=
|
||||
} {AAECAw==
|
||||
}}
|
||||
|
||||
# Basic base64 decoding with pad chars
|
||||
do_execsql_test 103 {
|
||||
SELECT hex(base64('AAECAwQF'));
|
||||
SELECT hex(base64('AAECAwQ='));
|
||||
SELECT hex(base64('AAECAw=='));
|
||||
} {000102030405 0001020304 00010203}
|
||||
|
||||
# Basic base64 decoding without pad chars and with whitespace
|
||||
do_execsql_test 104 {
|
||||
SELECT hex(base64(' AAECAwQF '));
|
||||
SELECT hex(base64(' AAECAwQ'));
|
||||
SELECT hex(base64('AAECAw '));
|
||||
} {000102030405 0001020304 00010203}
|
||||
|
||||
# Basic base85 encoding
|
||||
do_execsql_test 105 {
|
||||
SELECT base85(x'000102030405');
|
||||
SELECT base85(x'0001020304');
|
||||
SELECT base85(x'00010203');
|
||||
} {{##/2,#2/
|
||||
} {##/2,#*
|
||||
} {##/2,
|
||||
}}
|
||||
|
||||
# Basic base85 decoding with and without whitespace
|
||||
do_execsql_test 106 {
|
||||
SELECT hex(base85('##/2,#2/'));
|
||||
SELECT hex(base85('##/2,#*'));
|
||||
SELECT hex(base85('##/2,'));
|
||||
SELECT hex(base85(' ##/2,#2/ '));
|
||||
SELECT hex(base85(' ##/2,#*'));
|
||||
SELECT hex(base85('##/2, '));
|
||||
} {000102030405 0001020304 00010203 000102030405 0001020304 00010203}
|
||||
|
||||
# Round-trip some random blobs.
|
||||
do_execsql_test 107 {
|
||||
CREATE TEMP TABLE rb( len int, b blob ) STRICT;
|
||||
INSERT INTO rb(len) VALUES (1),(2),(3),(4),(5),(150),(151),(152),(153),(1054);
|
||||
UPDATE rb SET b = randomblob(len);
|
||||
SELECT len, base64(base64(b))=b, base85(base85(b))=b
|
||||
FROM rb ORDER BY len;
|
||||
} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
|
||||
|
||||
# Same round-trip but with space or junk prepended and/or appended or not.
|
||||
do_execsql_test 108 {
|
||||
CREATE TEMP TABLE junk(j text, rank int);
|
||||
INSERT INTO junk VALUES ('',0),(' ',1),('~',2);
|
||||
SELECT len, base64(j.j||base64(b)||j.j)=b, base85(j.j||base85(b)||j.j)=b
|
||||
FROM rb r, junk j WHERE j.rank=(r.len+r.len/25)%3 ORDER BY len;
|
||||
} {1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 150 1 1 151 1 1 152 1 1 153 1 1 1054 1 1}
|
||||
|
||||
# Exercise the fail-on-too-large result feature.
|
||||
|
||||
set inLimit [sqlite3_limit db SQLITE_LIMIT_LENGTH -1]
|
||||
sqlite3_limit db SQLITE_LIMIT_LENGTH 1300
|
||||
|
||||
do_catchsql_test 109 {
|
||||
SELECT len, base64(b) FROM rb WHERE len>200;
|
||||
} {1 {blob expanded to base64 too big}}
|
||||
|
||||
do_catchsql_test 110 {
|
||||
SELECT len, base85(b) FROM rb WHERE len>200;
|
||||
} {1 {blob expanded to base85 too big}}
|
||||
|
||||
do_catchsql_test 111 {
|
||||
SELECT length(base85(b))=1335 FROM rb WHERE len=1054;
|
||||
} {1 {blob expanded to base85 too big}}
|
||||
|
||||
sqlite3_limit db SQLITE_LIMIT_LENGTH $inLimit
|
||||
|
||||
# Exercise is_base85(t)
|
||||
|
||||
do_execsql_test 112 {
|
||||
SELECT is_base85(' '||base85(x'123456')||char(10)),
|
||||
is_base85('#$%&*+,-./0123456789:;<=>?@'
|
||||
||'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
||'[\]^_`'
|
||||
||'abcdefghijklmnopqrstuvwxyz'),
|
||||
is_base85('!'), is_base85('"'), is_base85(''''), is_base85('('),
|
||||
is_base85(')'), is_base85(char(123)), is_base85('|'), is_base85(char(125)),
|
||||
is_base85('~'), is_base85(char(127));
|
||||
} {1 1 0 0 0 0 0 0 0 0 0 0}
|
||||
|
||||
do_execsql_test 113 {
|
||||
SELECT is_base85(NULL) IS NULL;
|
||||
} {1}
|
||||
|
||||
do_catchsql_test 114 {
|
||||
SELECT is_base85(1);
|
||||
} {1 {is_base85 accepts only text or NULL}}
|
||||
|
||||
do_catchsql_test 115 {
|
||||
SELECT is_base85(1.1);
|
||||
} {1 {is_base85 accepts only text or NULL}}
|
||||
|
||||
do_catchsql_test 116 {
|
||||
SELECT is_base85(x'00');
|
||||
} {1 {is_base85 accepts only text or NULL}}
|
||||
|
||||
# Round-trip many bigger random blobs.
|
||||
|
||||
do_execsql_test 117 {
|
||||
CREATE TABLE bs(b blob, num);
|
||||
INSERT INTO bs SELECT randomblob(4000 + n%3), n
|
||||
FROM (
|
||||
WITH RECURSIVE seq(n) AS (
|
||||
VALUES(1) UNION ALL SELECT n+1
|
||||
FROM seq WHERE n<100
|
||||
) SELECT n FROM seq);
|
||||
SELECT num FROM bs WHERE base64(base64(b))!=b;
|
||||
SELECT num FROM bs WHERE base85(base85(b))!=b;
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
+5
-1
@@ -52,7 +52,11 @@ do_execsql_test 1.0 {
|
||||
}
|
||||
|
||||
for {set i 1} {$i < 8} {incr i} {
|
||||
fake_big_file [expr $i*1024] [get_pwd]/test.db
|
||||
if {[catch {fake_big_file [expr $i*1024] [get_pwd]/test.db}]} {
|
||||
puts "Cannot create ${i}MB sparse file"
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
hexio_write test.db 28 [format %.8x [expr ($i*1024*1024*1024/4096) - 5]]
|
||||
|
||||
do_execsql_test 1.$i "
|
||||
|
||||
@@ -24,10 +24,17 @@ set testprefix bigsort
|
||||
# memory. Make sure the host has at least 8GB available before running
|
||||
# this test.
|
||||
#
|
||||
# Update: https://sqlite.org/src/info/7c96a56 adds assert() statements
|
||||
# that make this test too slow to run with SQLITE_DEBUG builds.
|
||||
#
|
||||
if {[catch {exec free | grep Mem:} out] || [lindex $out 1]<8000000} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
ifcapable debug {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA page_size = 1024;
|
||||
|
||||
+19
-6
@@ -1479,13 +1479,26 @@ do_test 19.0 {
|
||||
do_execsql_test 19.1 {
|
||||
PRAGMA writable_schema=ON;
|
||||
}
|
||||
|
||||
set err "UNIQUE constraint failed: index 'a'"
|
||||
ifcapable oversize_cell_check {
|
||||
set err "database disk image is malformed"
|
||||
}
|
||||
do_catchsql_test 19.2 {
|
||||
UPDATE t1 SET a=1;
|
||||
} [list 1 $err]
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 19.3 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c INTEGER, d TEXT);
|
||||
CREATE INDEX i1 ON t1((NULL));
|
||||
INSERT INTO t1 VALUES(1, NULL, 1, 'text value');
|
||||
PRAGMA writable_schema = on;
|
||||
UPDATE sqlite_schema SET
|
||||
sql = 'CREATE INDEX i1 ON t1(b, c, d)',
|
||||
tbl_name = 't1',
|
||||
type='index'
|
||||
WHERE name='i1';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_catchsql_test 19.4 {
|
||||
PRAGMA integrity_check;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
+23
-8
@@ -20,6 +20,11 @@ if {[permutation] == "inmemory_journal"} {
|
||||
return
|
||||
}
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix dbpagefault
|
||||
|
||||
faultsim_save_and_close
|
||||
@@ -57,15 +62,25 @@ do_execsql_test 3.0 {
|
||||
END;
|
||||
}
|
||||
|
||||
do_faultsim_test 3 -prep {
|
||||
catch { db close }
|
||||
sqlite3 db test.db
|
||||
execsql { PRAGMA trusted_schema = true }
|
||||
} -body {
|
||||
execsql { INSERT INTO x1 DEFAULT VALUES; }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
# This test case no longer works, as it is no longer possible to use
|
||||
# virtual table sqlite_dbpage from within a trigger.
|
||||
#
|
||||
do_execsql_test 3.1 {
|
||||
PRAGMA trusted_schema = 1;
|
||||
}
|
||||
do_catchsql_test 3.2 {
|
||||
PRAGMA trusted_schema = 1;
|
||||
INSERT INTO x1 DEFAULT VALUES;
|
||||
} {1 {unsafe use of virtual table "sqlite_dbpage"}}
|
||||
#do_faultsim_test 3 -prep {
|
||||
# catch { db close }
|
||||
# sqlite3 db test.db
|
||||
# execsql { PRAGMA trusted_schema = 1 }
|
||||
#} -body {
|
||||
# execsql { INSERT INTO x1 DEFAULT VALUES; }
|
||||
#} -test {
|
||||
# faultsim_test_result {0 {}}
|
||||
#}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+10
-1
@@ -50,7 +50,16 @@ do_icu_expr_test 1.6 { "(x OR y)" } {PHRASE 3 0 ( x or y )}
|
||||
# is passed to the tokenizer.
|
||||
#
|
||||
do_icu_expr_test 1.7 {a:word} {PHRASE 0 0 word}
|
||||
do_icu_expr_test 1.8 {d:word} {PHRASE 3 0 d:word}
|
||||
# do_icu_expr_test 1.8 {d:word} {PHRASE 3 0 d:word}
|
||||
do_test 1.8 {
|
||||
set res [
|
||||
db one {SELECT fts3_exprtest('icu en_US', 'd:word', 'a', 'b', 'c')}
|
||||
]
|
||||
expr {
|
||||
$res=="PHRASE 3 0 d:word" ||
|
||||
$res=="AND {AND {PHRASE 3 0 d} {PHRASE 3 0 :}} {PHRASE 3 0 word}"
|
||||
}
|
||||
} 1
|
||||
|
||||
set sqlite_fts3_enable_parentheses 0
|
||||
|
||||
|
||||
@@ -82,6 +82,8 @@ do_faultsim_test 1 -prep {
|
||||
multiplex_restore_db
|
||||
sqlite3 db file:test.db?8_3_names=1
|
||||
sqlite3_multiplex_control db main chunk_size [expr 256*1024]
|
||||
execsql { PRAGMA journal_mode = truncate }
|
||||
execsql { PRAGMA synchronous = off }
|
||||
} -body {
|
||||
execsql {
|
||||
UPDATE t1 SET a=randomblob(12), b=randomblob(1500) WHERE (rowid%32)=0
|
||||
|
||||
@@ -806,6 +806,8 @@ test_suite "inmemory_journal" -description {
|
||||
# This test depends on a successful recovery from the pager error
|
||||
# state. Which is not possible with an in-memory journal
|
||||
fts5fault1.test
|
||||
|
||||
recoverpgsz.test
|
||||
}]
|
||||
|
||||
ifcapable mem3 {
|
||||
|
||||
+9
-9
@@ -904,15 +904,15 @@ do_test pragma-8.1.2 {
|
||||
PRAGMA schema_version;
|
||||
}
|
||||
} {schema_version 105}
|
||||
do_test pragma-8.1.3 {
|
||||
execsql {
|
||||
PRAGMA schema_version = 106;
|
||||
}
|
||||
} {}
|
||||
do_test pragma-8.1.4 {
|
||||
execsql {
|
||||
PRAGMA schema_version;
|
||||
}
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
do_execsql_test pragma-8.1.3 {
|
||||
PRAGMA schema_version = 106;
|
||||
PRAGMA schema_version;
|
||||
} 105
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_execsql_test pragma-8.1.4 {
|
||||
PRAGMA schema_version = 106;
|
||||
PRAGMA schema_version;
|
||||
} 106
|
||||
|
||||
# Check that creating a table modifies the schema-version (this is really
|
||||
|
||||
+26
-1
@@ -86,6 +86,31 @@ do_test 2.2 {
|
||||
set L
|
||||
} {three}
|
||||
|
||||
|
||||
# 2022-11-25 dbsqlfuzz crash-3a548de406a50e896c1bf7142692d35d339d697f
|
||||
# Disable the push-down optimization for compound subqueries if any
|
||||
# arm of the compound has an incompatible affinity.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t0(c0 INT);
|
||||
INSERT INTO t0 VALUES(0);
|
||||
CREATE TABLE t1_a(a INTEGER PRIMARY KEY, b TEXT);
|
||||
INSERT INTO t1_a VALUES(1,'one'); --,(4,'four');
|
||||
CREATE TABLE t1_b(c INTEGER PRIMARY KEY, d TEXT);
|
||||
INSERT INTO t1_b VALUES(2,'two'); --,(5,'five');
|
||||
CREATE VIEW v0 AS SELECT CAST(t0.c0 AS INTEGER) AS c0 FROM t0;
|
||||
CREATE VIEW t1 AS SELECT a, b FROM t1_a UNION ALL SELECT c, 0 FROM t1_b;
|
||||
SELECT t1.a, quote(t1.b), t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1;
|
||||
} {
|
||||
1 'one' 0
|
||||
2 '0' 0
|
||||
}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT a, quote(b), cd FROM (
|
||||
SELECT t1.a, t1.b, t0.c0 AS cd FROM t0 LEFT JOIN v0 ON v0.c0!=0,t1
|
||||
) WHERE a=2 AND b='0' AND cd=0;
|
||||
} {
|
||||
2 '0' 0
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -141,6 +141,8 @@ do_recover_test 3.0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
execsql { PRAGMA secure_delete = 0 }
|
||||
execsql { PRAGMA auto_vacuum = 0 }
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t2(d, e, f);
|
||||
|
||||
@@ -303,6 +303,33 @@ do_execsql_test regexp1-6.7 {SELECT 'xabc' REGEXP '(^abc|def)';} {0}
|
||||
do_execsql_test regexp1-6.8 {SELECT 'def' REGEXP '(^abc|def)';} {1}
|
||||
do_execsql_test regexp1-6.9 {SELECT 'xdef' REGEXP '(^abc|def)';} {1}
|
||||
|
||||
# 2022-11-17
|
||||
# https://sqlite.org/forum/forumpost/3ffe058b04
|
||||
#
|
||||
do_execsql_test regexp1-7.1 {
|
||||
SELECT char(0x61,0x7ff,0x62) REGEXP char(0x7ff);
|
||||
} 1
|
||||
do_execsql_test regexp1-7.2 {
|
||||
SELECT char(0x61,0x800,0x62) REGEXP char(0x800);
|
||||
} 1
|
||||
do_execsql_test regexp1-7.3 {
|
||||
SELECT char(0x61,0xabc,0x62) REGEXP char(0xabc);
|
||||
} 1
|
||||
do_execsql_test regexp1-7.4 {
|
||||
SELECT char(0x61,0xfff,0x62) REGEXP char(0xfff);
|
||||
} 1
|
||||
do_execsql_test regexp1-7.5 {
|
||||
SELECT char(0x61,0x1000,0x62) REGEXP char(0x1000);
|
||||
} 1
|
||||
do_execsql_test regexp1-7.10 {
|
||||
SELECT char(0x61,0xffff,0x62) REGEXP char(0xffff);
|
||||
} 1
|
||||
do_execsql_test regexp1-7.11 {
|
||||
SELECT char(0x61,0x10000,0x62) REGEXP char(0x10000);
|
||||
} 1
|
||||
do_execsql_test regexp1-7.12 {
|
||||
SELECT char(0x61,0x10ffff,0x62) REGEXP char(0x10ffff);
|
||||
} 1
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -51,6 +51,7 @@ array set ::Configs [strip_comments {
|
||||
-O2
|
||||
--disable-amalgamation --disable-shared
|
||||
--enable-session
|
||||
-DSQLITE_ENABLE_RBU
|
||||
}
|
||||
"Sanitize" {
|
||||
CC=clang -fsanitize=address,undefined
|
||||
|
||||
@@ -376,4 +376,28 @@ do_execsql_test 16.1 {
|
||||
SELECT * FROM t2;
|
||||
} {1 2 3 a b c}
|
||||
|
||||
|
||||
foreach {tn temp} {
|
||||
1 ""
|
||||
2 TEMP
|
||||
} {
|
||||
reset_db
|
||||
do_execsql_test 17.$tn.0 "
|
||||
CREATE $temp TABLE foo (
|
||||
fooid INTEGER PRIMARY KEY,
|
||||
fooval INTEGER NOT NULL UNIQUE,
|
||||
refcnt INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
"
|
||||
do_execsql_test 17.$tn.1 {
|
||||
INSERT INTO foo (fooval) VALUES (17), (4711), (17)
|
||||
ON CONFLICT DO
|
||||
UPDATE SET refcnt = refcnt+1
|
||||
RETURNING fooid;
|
||||
} {
|
||||
1 2 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -125,6 +125,12 @@ SELECT * FROM t1;}}
|
||||
do_test shell4-2.5 {
|
||||
catchcmd ":memory:" "CREATE TABLE t1(x);\n.trace stdout\nSELECT * FROM t1;"
|
||||
} {0 {SELECT * FROM t1;}}
|
||||
do_test shell4-2.6 {
|
||||
catchcmd ":memory:" {
|
||||
CREATE TABLE t1(x);
|
||||
.trace --stmt stdout
|
||||
SELECT * FROM t1;}
|
||||
} {0 {SELECT * FROM t1;}}
|
||||
}
|
||||
|
||||
do_test shell4-3.1 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user