mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Merge all the latest trunk fixes and enhancements into the jsonb branch.
FossilOrigin-Name: b089bf46374b374d02d7654c65eb3e75d1777638b398061e47644af0dab48c9b
This commit is contained in:
@@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
|
||||
# so that we create the export library with the dll.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
AC_INIT([sqlite],[3.44.0])
|
||||
AC_INIT([sqlite],[3.45.0])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
|
18
configure
vendored
18
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.44.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.45.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.44.0'
|
||||
PACKAGE_STRING='sqlite 3.44.0'
|
||||
PACKAGE_VERSION='3.45.0'
|
||||
PACKAGE_STRING='sqlite 3.45.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1472,7 +1472,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.44.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.45.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1537,7 +1537,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.44.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.45.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1668,7 +1668,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.44.0
|
||||
sqlite configure 3.45.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2087,7 +2087,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.44.0, which was
|
||||
It was created by sqlite $as_me 3.45.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -12481,7 +12481,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.44.0, which was
|
||||
This file was extended by sqlite $as_me 3.45.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12547,7 +12547,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.44.0
|
||||
sqlite config.status 3.45.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@@ -74,7 +74,7 @@
|
||||
# you don't need (for example BLT) by erasing or commenting out
|
||||
# the corresponding code.
|
||||
#
|
||||
AC_INIT([sqlite],[m4_esyscmd(cat VERSION | tr -d '\n')])
|
||||
AC_INIT([sqlite],m4_esyscmd(cat VERSION | tr -d '\n'))
|
||||
|
||||
dnl Make sure the local VERSION file matches this configure script
|
||||
sqlite_version_sanity_check=`cat $srcdir/VERSION | tr -d '\n'`
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Notes On Compiling SQLite On Windows 11
|
||||
|
||||
Here are step-by-step instructions on how to build SQLite from
|
||||
canonical source on a new Windows 11 PC, as of 2023-08-16:
|
||||
canonical source on a new Windows 11 PC, as of 2023-11-01:
|
||||
|
||||
1. Install Microsoft Visual Studio. The free "community edition"
|
||||
will work fine. Do a standard install for C++ development.
|
||||
@@ -84,6 +84,18 @@ following minor changes:
|
||||
<li> `set PATH=c:\tcl32\bin;%PATH%`
|
||||
</ul>
|
||||
|
||||
## Building a DLL
|
||||
|
||||
The command the developers use for building the deliverable DLL on the
|
||||
[download page](https://sqlite.org/download.html) is as follows:
|
||||
|
||||
> ~~~~
|
||||
nmake /f Makefile.msc sqlite3.dll USE_NATIVE_LIBPATHS=1 "OPTS=-DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_JSON1=1 -DSQLITE_ENABLE_GEOPOLY=1 -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 -DSQLITE_ENABLE_SERIALIZE=1 -DSQLITE_ENABLE_MATH_FUNCTIONS=1"
|
||||
~~~~
|
||||
|
||||
That command generates both the sqlite3.dll and sqlite3.def files. The same
|
||||
command works for both 32-bit and 64-bit builds.
|
||||
|
||||
## Statically Linking The TCL Library
|
||||
|
||||
Some utility programs associated with SQLite need to be linked
|
||||
|
@@ -229,6 +229,12 @@ static const unsigned char sqlite3Utf8Trans1[] = {
|
||||
|
||||
#endif /* ifndef SQLITE_AMALGAMATION */
|
||||
|
||||
#define FTS5_SKIP_UTF8(zIn) { \
|
||||
if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \
|
||||
while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \
|
||||
} \
|
||||
}
|
||||
|
||||
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
|
||||
struct Unicode61Tokenizer {
|
||||
unsigned char aTokenChar[128]; /* ASCII range token characters */
|
||||
@@ -1264,6 +1270,7 @@ static int fts5PorterTokenize(
|
||||
typedef struct TrigramTokenizer TrigramTokenizer;
|
||||
struct TrigramTokenizer {
|
||||
int bFold; /* True to fold to lower-case */
|
||||
int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1290,6 +1297,7 @@ static int fts5TriCreate(
|
||||
}else{
|
||||
int i;
|
||||
pNew->bFold = 1;
|
||||
pNew->iFoldParam = 0;
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
|
||||
@@ -1298,10 +1306,21 @@ static int fts5TriCreate(
|
||||
}else{
|
||||
pNew->bFold = (zArg[0]=='0');
|
||||
}
|
||||
}else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5TriDelete((Fts5Tokenizer*)pNew);
|
||||
pNew = 0;
|
||||
@@ -1324,40 +1343,62 @@ static int fts5TriTokenize(
|
||||
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
|
||||
int rc = SQLITE_OK;
|
||||
char aBuf[32];
|
||||
char *zOut = aBuf;
|
||||
int ii;
|
||||
const unsigned char *zIn = (const unsigned char*)pText;
|
||||
const unsigned char *zEof = &zIn[nText];
|
||||
u32 iCode;
|
||||
int aStart[3]; /* Input offset of each character in aBuf[] */
|
||||
|
||||
UNUSED_PARAM(unusedFlags);
|
||||
while( 1 ){
|
||||
char *zOut = aBuf;
|
||||
int iStart = zIn - (const unsigned char*)pText;
|
||||
const unsigned char *zNext;
|
||||
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
zNext = zIn;
|
||||
if( zIn<zEof ){
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
/* Populate aBuf[] with the characters for the first trigram. */
|
||||
for(ii=0; ii<3; ii++){
|
||||
do {
|
||||
aStart[ii] = zIn - (const unsigned char*)pText;
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) return SQLITE_OK;
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
|
||||
}while( iCode==0 );
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
}
|
||||
|
||||
/* At the start of each iteration of this loop:
|
||||
**
|
||||
** aBuf: Contains 3 characters. The 3 characters of the next trigram.
|
||||
** zOut: Points to the byte following the last character in aBuf.
|
||||
** aStart[3]: Contains the byte offset in the input text corresponding
|
||||
** to the start of each of the three characters in the buffer.
|
||||
*/
|
||||
assert( zIn<=zEof );
|
||||
while( 1 ){
|
||||
int iNext; /* Start of character following current tri */
|
||||
const char *z1;
|
||||
|
||||
/* Read characters from the input up until the first non-diacritic */
|
||||
do {
|
||||
iNext = zIn - (const unsigned char*)pText;
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
if( zIn<zEof ){
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
zIn = zNext;
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
|
||||
}while( iCode==0 );
|
||||
|
||||
/* Pass the current trigram back to fts5 */
|
||||
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
|
||||
if( iCode==0 || rc!=SQLITE_OK ) break;
|
||||
|
||||
/* Remove the first character from buffer aBuf[]. Append the character
|
||||
** with codepoint iCode. */
|
||||
z1 = aBuf;
|
||||
FTS5_SKIP_UTF8(z1);
|
||||
memmove(aBuf, z1, zOut - z1);
|
||||
zOut -= (z1 - aBuf);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
|
||||
/* Update the aStart[] array */
|
||||
aStart[0] = aStart[1];
|
||||
aStart[1] = aStart[2];
|
||||
aStart[2] = iNext;
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -1380,7 +1421,9 @@ int sqlite3Fts5TokenizerPattern(
|
||||
){
|
||||
if( xCreate==fts5TriCreate ){
|
||||
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
|
||||
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
|
||||
if( p->iFoldParam==0 ){
|
||||
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
|
||||
}
|
||||
}
|
||||
return FTS5_PATTERN_NONE;
|
||||
}
|
||||
|
109
ext/fts5/test/fts5trigram2.test
Normal file
109
ext/fts5/test/fts5trigram2.test
Normal file
@@ -0,0 +1,109 @@
|
||||
# 2023 October 24
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
# Tests for the fts5 "trigram" tokenizer.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
ifcapable !fts5 { finish_test ; return }
|
||||
set ::testprefix fts5trigram2
|
||||
|
||||
do_execsql_test 1.0 "
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize='trigram remove_diacritics 1');
|
||||
INSERT INTO t1 VALUES('abc\u0303defghijklm');
|
||||
INSERT INTO t1 VALUES('a\u0303b\u0303c\u0303defghijklm');
|
||||
"
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('abc');
|
||||
} [list \
|
||||
"(abc\u0303)defghijklm" \
|
||||
"(a\u0303b\u0303c\u0303)defghijklm" \
|
||||
]
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('bcde');
|
||||
} [list \
|
||||
"a(bc\u0303de)fghijklm" \
|
||||
"a\u0303(b\u0303c\u0303de)fghijklm" \
|
||||
]
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('cdef');
|
||||
} [list \
|
||||
"ab(c\u0303def)ghijklm" \
|
||||
"a\u0303b\u0303(c\u0303def)ghijklm" \
|
||||
]
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('def');
|
||||
} [list \
|
||||
"abc\u0303(def)ghijklm" \
|
||||
"a\u0303b\u0303c\u0303(def)ghijklm" \
|
||||
]
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_catchsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(
|
||||
z, tokenize='trigram case_sensitive 1 remove_diacritics 1'
|
||||
);
|
||||
} {1 {error in tokenizer constructor}}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(
|
||||
z, tokenize='trigram case_sensitive 0 remove_diacritics 1'
|
||||
);
|
||||
}
|
||||
do_execsql_test 2.2 "
|
||||
INSERT INTO t2 VALUES('\u00E3bcdef');
|
||||
INSERT INTO t2 VALUES('b\u00E3cdef');
|
||||
INSERT INTO t2 VALUES('bc\u00E3def');
|
||||
INSERT INTO t2 VALUES('bcd\u00E3ef');
|
||||
"
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('abc');
|
||||
} "(\u00E3bc)def"
|
||||
do_execsql_test 2.4 {
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('bac');
|
||||
} "(b\u00E3c)def"
|
||||
do_execsql_test 2.5 {
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('bca');
|
||||
} "(bc\u00E3)def"
|
||||
do_execsql_test 2.6 "
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('\u00E3bc');
|
||||
" "(\u00E3bc)def"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(
|
||||
z, tokenize='trigram remove_diacritics 1'
|
||||
);
|
||||
} {}
|
||||
do_execsql_test 3.1 "
|
||||
INSERT INTO t3 VALUES ('\u0303abc\u0303');
|
||||
"
|
||||
do_execsql_test 3.2 {
|
||||
SELECT highlight(t3, 0, '(', ')') FROM t3('abc');
|
||||
} "\u0303(abc\u0303)"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(z, tokenize=trigram);
|
||||
} {}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 4.1 {
|
||||
INSERT INTO t4 VALUES('ABCD');
|
||||
} {}
|
||||
|
||||
finish_test
|
@@ -120,6 +120,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
|
||||
Sqlite.java \
|
||||
SqliteException.java \
|
||||
ValueHolder.java \
|
||||
WindowFunction.java \
|
||||
)
|
||||
|
||||
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
|
||||
|
@@ -90,12 +90,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* SQLITE_M... */
|
||||
#ifndef SQLITE_MAX_ALLOCATION_SIZE
|
||||
# define SQLITE_MAX_ALLOCATION_SIZE 0x1fffffff
|
||||
#endif
|
||||
|
||||
/**********************************************************************/
|
||||
/* SQLITE_O... */
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
@@ -1670,8 +1664,9 @@ static int encodingTypeIsValid(int eTextRep){
|
||||
}
|
||||
}
|
||||
|
||||
/* For use with sqlite3_result/value_pointer() */
|
||||
static const char * const ResultJavaValuePtrStr = "org.sqlite.jni.capi.ResultJavaVal";
|
||||
/* For use with sqlite3_result_pointer(), sqlite3_value_pointer(),
|
||||
sqlite3_bind_java_object(), and sqlite3_column_java_object(). */
|
||||
static const char * const s3jni__value_jref_key = "org.sqlite.jni.capi.ResultJavaVal";
|
||||
|
||||
/*
|
||||
** If v is not NULL, it must be a jobject global reference. Its
|
||||
@@ -2181,7 +2176,9 @@ S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)(
|
||||
return S3JniCast_P2L(p);
|
||||
}
|
||||
|
||||
/* Central auto-extension handler. */
|
||||
/*
|
||||
** Central auto-extension runner for auto-extensions created in Java.
|
||||
*/
|
||||
static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
|
||||
const struct sqlite3_api_routines *ignored){
|
||||
int rc = 0;
|
||||
@@ -2418,7 +2415,7 @@ S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)(
|
||||
if(pStmt){
|
||||
jobject const rv = S3JniRefGlobal(val);
|
||||
if( rv ){
|
||||
rc = sqlite3_bind_pointer(pStmt, ndx, rv, ResultJavaValuePtrStr,
|
||||
rc = sqlite3_bind_pointer(pStmt, ndx, rv, s3jni__value_jref_key,
|
||||
S3Jni_jobject_finalizer);
|
||||
}else if(val){
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -2870,6 +2867,26 @@ S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)(
|
||||
return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
|
||||
}
|
||||
|
||||
S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)(
|
||||
JniArgsEnvClass, jlong jpStmt, jint ndx
|
||||
){
|
||||
sqlite3_stmt * const stmt = S3JniLongPtr_sqlite3_stmt(jpStmt);
|
||||
jobject rv = 0;
|
||||
if( stmt ){
|
||||
sqlite3 * const db = sqlite3_db_handle(stmt);
|
||||
sqlite3_value * sv;
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
sv = sqlite3_column_value(stmt, (int)ndx);
|
||||
if( sv ){
|
||||
rv = S3JniRefLocal(
|
||||
sqlite3_value_pointer(sv, s3jni__value_jref_key)
|
||||
);
|
||||
}
|
||||
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text)(
|
||||
JniArgsEnvClass, jobject jpStmt, jint ndx
|
||||
){
|
||||
@@ -3516,9 +3533,16 @@ S3JniApi(sqlite3_errmsg(),jstring,1errmsg)(
|
||||
S3JniApi(sqlite3_errstr(),jstring,1errstr)(
|
||||
JniArgsEnvClass, jint rcCode
|
||||
){
|
||||
jstring const rv = (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode))
|
||||
/* We know these values to be plain ASCII, so pose no MUTF-8
|
||||
** incompatibility */;
|
||||
jstring rv;
|
||||
const char * z = sqlite3_errstr((int)rcCode);
|
||||
if( !z ){
|
||||
/* This hypothetically cannot happen, but we'll behave like the
|
||||
low-level library would in such a case... */
|
||||
z = "unknown error";
|
||||
}
|
||||
rv = (*env)->NewStringUTF(env, z)
|
||||
/* We know these values to be plain ASCII, so pose no MUTF-8
|
||||
** incompatibility */;
|
||||
s3jni_oom_check( rv );
|
||||
return rv;
|
||||
}
|
||||
@@ -4351,7 +4375,7 @@ S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)(
|
||||
jobject const rjv = S3JniRefGlobal(v);
|
||||
if( rjv ){
|
||||
sqlite3_result_pointer(pCx, rjv,
|
||||
ResultJavaValuePtrStr, S3Jni_jobject_finalizer);
|
||||
s3jni__value_jref_key, S3Jni_jobject_finalizer);
|
||||
}else{
|
||||
sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx));
|
||||
}
|
||||
@@ -4366,6 +4390,13 @@ S3JniApi(sqlite3_result_null(),void,1result_1null)(
|
||||
sqlite3_result_null(PtrGet_sqlite3_context(jpCx));
|
||||
}
|
||||
|
||||
S3JniApi(sqlite3_result_subtype(),void,1result_1subtype)(
|
||||
JniArgsEnvClass, jobject jpCx, jint v
|
||||
){
|
||||
sqlite3_result_subtype(PtrGet_sqlite3_context(jpCx), (unsigned int)v);
|
||||
}
|
||||
|
||||
|
||||
S3JniApi(sqlite3_result_text(),void,1result_1text)(
|
||||
JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax
|
||||
){
|
||||
@@ -4594,7 +4625,7 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
|
||||
jbyteArray baG, jbyteArray baT, jint escLike){
|
||||
int rc = 0;
|
||||
jbyte * const pG = s3jni_jbyteArray_bytes(baG);
|
||||
jbyte * const pT = pG ? s3jni_jbyteArray_bytes(baT) : 0;
|
||||
jbyte * const pT = s3jni_jbyteArray_bytes(baT);
|
||||
|
||||
/* Note that we're relying on the byte arrays having been
|
||||
NUL-terminated on the Java side. */
|
||||
@@ -4889,7 +4920,7 @@ S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)(
|
||||
){
|
||||
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
|
||||
return sv
|
||||
? sqlite3_value_pointer(sv, ResultJavaValuePtrStr)
|
||||
? sqlite3_value_pointer(sv, s3jni__value_jref_key)
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
@@ -1119,6 +1119,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1int
|
||||
JNIEXPORT jlong JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1int64
|
||||
(JNIEnv *, jclass, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_column_java_object
|
||||
* Signature: (JI)Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1column_1java_1object
|
||||
(JNIEnv *, jclass, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_column_name
|
||||
@@ -1679,14 +1687,6 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1error_1nom
|
||||
JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1error_1code
|
||||
(JNIEnv *, jclass, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_result_null
|
||||
* Signature: (Lorg/sqlite/jni/capi/sqlite3_context;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1null
|
||||
(JNIEnv *, jclass, jobject);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_result_int
|
||||
@@ -1711,6 +1711,22 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1int64
|
||||
JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1java_1object
|
||||
(JNIEnv *, jclass, jobject, jobject);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_result_null
|
||||
* Signature: (Lorg/sqlite/jni/capi/sqlite3_context;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1null
|
||||
(JNIEnv *, jclass, jobject);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_result_subtype
|
||||
* Signature: (Lorg/sqlite/jni/capi/sqlite3_context;I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1subtype
|
||||
(JNIEnv *, jclass, jobject, jint);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_result_value
|
||||
|
@@ -42,9 +42,75 @@ public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
managing their accumulator state across calls to the UDF's
|
||||
callbacks.
|
||||
|
||||
<p>T must be of a type which can be legally stored as a value in
|
||||
java.util.HashMap<KeyType,T>.
|
||||
|
||||
<p>If a given aggregate or window function is called multiple times
|
||||
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
|
||||
then the clients need some way of knowing which call is which so
|
||||
that they can map their state between their various UDF callbacks
|
||||
and reset it via xFinal(). This class takes care of such
|
||||
mappings.
|
||||
|
||||
<p>This class works by mapping
|
||||
sqlite3_context.getAggregateContext() to a single piece of
|
||||
state, of a client-defined type (the T part of this class), which
|
||||
persists across a "matching set" of the UDF's callbacks.
|
||||
|
||||
<p>This class is a helper providing commonly-needed functionality
|
||||
- it is not required for use with aggregate or window functions.
|
||||
Client UDFs are free to perform such mappings using custom
|
||||
approaches. The provided {@link AggregateFunction} and {@link
|
||||
WindowFunction} classes use this.
|
||||
*/
|
||||
public static final class PerContextState<T> {
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||
methods, passing it that method's first argument and an initial
|
||||
value for the persistent state. If there is currently no
|
||||
mapping for the given context within the map, one is created
|
||||
using the given initial value, else the existing one is used
|
||||
and the 2nd argument is ignored. It returns a ValueHolder<T>
|
||||
which can be used to modify that state directly without
|
||||
requiring that the client update the underlying map's entry.
|
||||
|
||||
<p>The caller is obligated to eventually call
|
||||
takeAggregateState() to clear the mapping.
|
||||
*/
|
||||
public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
final Long key = cx.getAggregateContext(true);
|
||||
ValueHolder<T> rc = null==key ? null : map.get(key);
|
||||
if( null==rc ){
|
||||
map.put(key, rc = new ValueHolder<>(initialValue));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xFinal() method and passed that
|
||||
method's first argument. This function removes the value
|
||||
associated with cx.getAggregateContext() from the map and
|
||||
returns it, returning null if no other UDF method has been
|
||||
called to set up such a mapping. The latter condition will be
|
||||
the case if a UDF is used in a statement which has no result
|
||||
rows.
|
||||
*/
|
||||
public T takeAggregateState(sqlite3_context cx){
|
||||
final ValueHolder<T> h = map.remove(cx.getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final SQLFunction.PerContextState<T> map =
|
||||
new SQLFunction.PerContextState<>();
|
||||
private final PerContextState<T> map = new PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
|
@@ -32,15 +32,6 @@ import java.util.Arrays;
|
||||
|
||||
<p>The C-side part can be found in sqlite3-jni.c.
|
||||
|
||||
<p>This class is package-private in order to keep Java clients from
|
||||
having direct access to the low-level C-style APIs, a design
|
||||
decision made by Java developers based on the C-style API being
|
||||
riddled with opportunities for Java developers to proverbially shoot
|
||||
themselves in the foot with. Third-party copies of this code may
|
||||
eliminate that guard by simply changing this class from
|
||||
package-private to public. Its methods which are intended to be
|
||||
exposed that way are all public.
|
||||
|
||||
<p>Only functions which materially differ from their C counterparts
|
||||
are documented here, and only those material differences are
|
||||
documented. The C documentation is otherwise applicable for these
|
||||
@@ -595,6 +586,36 @@ public final class CApi {
|
||||
@NotNull sqlite3_stmt stmt, int ndx
|
||||
);
|
||||
|
||||
static native Object sqlite3_column_java_object(
|
||||
@NotNull long ptrToStmt, int ndx
|
||||
);
|
||||
|
||||
/**
|
||||
If the given result column was bound with
|
||||
sqlite3_bind_java_object() or sqlite3_result_java_object() then
|
||||
that object is returned, else null is returned. This routine
|
||||
requires locking the owning database's mutex briefly in order to
|
||||
extract the object in a thread-safe way.
|
||||
*/
|
||||
public static Object sqlite3_column_java_object(
|
||||
@NotNull sqlite3_stmt stmt, int ndx
|
||||
){
|
||||
return sqlite3_column_java_object( stmt.getNativePointer(), ndx );
|
||||
}
|
||||
|
||||
/**
|
||||
If the two-parameter overload of sqlite3_column_java_object()
|
||||
returns non-null and the returned value is an instance of T then
|
||||
that object is returned, else null is returned.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T sqlite3_column_java_object(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @NotNull Class<T> type
|
||||
){
|
||||
final Object o = sqlite3_column_java_object(stmt, ndx);
|
||||
return type.isInstance(o) ? (T)o : null;
|
||||
}
|
||||
|
||||
static native String sqlite3_column_name(@NotNull long ptrToStmt, int ndx);
|
||||
|
||||
public static String sqlite3_column_name(@NotNull sqlite3_stmt stmt, int ndx){
|
||||
@@ -1432,10 +1453,6 @@ public final class CApi {
|
||||
@NotNull sqlite3_context cx, int c
|
||||
);
|
||||
|
||||
public static native void sqlite3_result_null(
|
||||
@NotNull sqlite3_context cx
|
||||
);
|
||||
|
||||
public static native void sqlite3_result_int(
|
||||
@NotNull sqlite3_context cx, int v
|
||||
);
|
||||
@@ -1464,6 +1481,10 @@ public final class CApi {
|
||||
@NotNull sqlite3_context cx, @NotNull Object o
|
||||
);
|
||||
|
||||
public static native void sqlite3_result_null(
|
||||
@NotNull sqlite3_context cx
|
||||
);
|
||||
|
||||
public static void sqlite3_result_set(
|
||||
@NotNull sqlite3_context cx, @NotNull Boolean v
|
||||
){
|
||||
@@ -1524,6 +1545,10 @@ public final class CApi {
|
||||
else sqlite3_result_blob(cx, blob, blob.length);
|
||||
}
|
||||
|
||||
public static native void sqlite3_result_subtype(
|
||||
@NotNull sqlite3_context cx, int val
|
||||
);
|
||||
|
||||
public static native void sqlite3_result_value(
|
||||
@NotNull sqlite3_context cx, @NotNull sqlite3_value v
|
||||
);
|
||||
@@ -1938,7 +1963,7 @@ public final class CApi {
|
||||
given Class, else it returns null.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T sqlite3_value_java_casted(@NotNull sqlite3_value v,
|
||||
public static <T> T sqlite3_value_java_object(@NotNull sqlite3_value v,
|
||||
@NotNull Class<T> type){
|
||||
final Object o = sqlite3_value_java_object(v);
|
||||
return type.isInstance(o) ? (T)o : null;
|
||||
|
@@ -33,71 +33,4 @@ package org.sqlite.jni.capi;
|
||||
*/
|
||||
public interface SQLFunction {
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
managing their accumulator state across calls to the UDF's
|
||||
callbacks.
|
||||
|
||||
<p>T must be of a type which can be legally stored as a value in
|
||||
java.util.HashMap<KeyType,T>.
|
||||
|
||||
<p>If a given aggregate or window function is called multiple times
|
||||
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
|
||||
then the clients need some way of knowing which call is which so
|
||||
that they can map their state between their various UDF callbacks
|
||||
and reset it via xFinal(). This class takes care of such
|
||||
mappings.
|
||||
|
||||
<p>This class works by mapping
|
||||
sqlite3_context.getAggregateContext() to a single piece of
|
||||
state, of a client-defined type (the T part of this class), which
|
||||
persists across a "matching set" of the UDF's callbacks.
|
||||
|
||||
<p>This class is a helper providing commonly-needed functionality
|
||||
- it is not required for use with aggregate or window functions.
|
||||
Client UDFs are free to perform such mappings using custom
|
||||
approaches. The provided {@link AggregateFunction} and {@link
|
||||
WindowFunction} classes use this.
|
||||
*/
|
||||
public static final class PerContextState<T> {
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||
methods, passing it that method's first argument and an initial
|
||||
value for the persistent state. If there is currently no
|
||||
mapping for the given context within the map, one is created
|
||||
using the given initial value, else the existing one is used
|
||||
and the 2nd argument is ignored. It returns a ValueHolder<T>
|
||||
which can be used to modify that state directly without
|
||||
requiring that the client update the underlying map's entry.
|
||||
|
||||
<p>The caller is obligated to eventually call
|
||||
takeAggregateState() to clear the mapping.
|
||||
*/
|
||||
public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
final Long key = cx.getAggregateContext(true);
|
||||
ValueHolder<T> rc = null==key ? null : map.get(key);
|
||||
if( null==rc ){
|
||||
map.put(key, rc = new ValueHolder<>(initialValue));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xFinal() method and passed that
|
||||
method's first argument. This function removes the value
|
||||
associated with cx.getAggregateContext() from the map and
|
||||
returns it, returning null if no other UDF method has been
|
||||
called to set up such a mapping. The latter condition will be
|
||||
the case if a UDF is used in a statement which has no result
|
||||
rows.
|
||||
*/
|
||||
public T takeAggregateState(sqlite3_context cx){
|
||||
final ValueHolder<T> h = map.remove(cx.getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -803,13 +803,17 @@ public class Tester1 implements Runnable {
|
||||
affirm( 0==rc );
|
||||
int n = 0;
|
||||
if( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
affirm( testResult.value == sqlite3_column_java_object(stmt, 0) );
|
||||
affirm( testResult.value == sqlite3_column_java_object(stmt, 0, sqlite3.class) );
|
||||
affirm( null == sqlite3_column_java_object(stmt, 0, sqlite3_stmt.class) );
|
||||
affirm( null == sqlite3_column_java_object(stmt,1) );
|
||||
final sqlite3_value v = sqlite3_column_value(stmt, 0);
|
||||
affirm( testResult.value == sqlite3_value_java_object(v) );
|
||||
affirm( testResult.value == sqlite3_value_java_casted(v, sqlite3.class) );
|
||||
affirm( testResult.value == sqlite3_value_java_object(v, sqlite3.class) );
|
||||
affirm( testResult.value ==
|
||||
sqlite3_value_java_casted(v, testResult.value.getClass()) );
|
||||
affirm( testResult.value == sqlite3_value_java_casted(v, Object.class) );
|
||||
affirm( null == sqlite3_value_java_casted(v, String.class) );
|
||||
sqlite3_value_java_object(v, testResult.value.getClass()) );
|
||||
affirm( testResult.value == sqlite3_value_java_object(v, Object.class) );
|
||||
affirm( null == sqlite3_value_java_object(v, String.class) );
|
||||
++n;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
@@ -925,7 +929,6 @@ public class Tester1 implements Runnable {
|
||||
"ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+
|
||||
") AS sum_y "+
|
||||
"FROM twin ORDER BY x;");
|
||||
affirm( 0 == rc );
|
||||
int n = 0;
|
||||
while( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
final String s = sqlite3_column_text16(stmt, 0);
|
||||
|
@@ -51,9 +51,75 @@ public abstract class AggregateFunction<T> implements SqlFunction {
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
managing their accumulator state across calls to the UDF's
|
||||
callbacks.
|
||||
|
||||
<p>T must be of a type which can be legally stored as a value in
|
||||
java.util.HashMap<KeyType,T>.
|
||||
|
||||
<p>If a given aggregate or window function is called multiple times
|
||||
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
|
||||
then the clients need some way of knowing which call is which so
|
||||
that they can map their state between their various UDF callbacks
|
||||
and reset it via xFinal(). This class takes care of such
|
||||
mappings.
|
||||
|
||||
<p>This class works by mapping
|
||||
sqlite3_context.getAggregateContext() to a single piece of
|
||||
state, of a client-defined type (the T part of this class), which
|
||||
persists across a "matching set" of the UDF's callbacks.
|
||||
|
||||
<p>This class is a helper providing commonly-needed functionality
|
||||
- it is not required for use with aggregate or window functions.
|
||||
Client UDFs are free to perform such mappings using custom
|
||||
approaches. The provided {@link AggregateFunction} and {@link
|
||||
WindowFunction} classes use this.
|
||||
*/
|
||||
public static final class PerContextState<T> {
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||
methods, passing it that method's first argument and an initial
|
||||
value for the persistent state. If there is currently no
|
||||
mapping for the given context within the map, one is created
|
||||
using the given initial value, else the existing one is used
|
||||
and the 2nd argument is ignored. It returns a ValueHolder<T>
|
||||
which can be used to modify that state directly without
|
||||
requiring that the client update the underlying map's entry.
|
||||
|
||||
<p>The caller is obligated to eventually call
|
||||
takeAggregateState() to clear the mapping.
|
||||
*/
|
||||
public ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
|
||||
final Long key = args.getContext().getAggregateContext(true);
|
||||
ValueHolder<T> rc = null==key ? null : map.get(key);
|
||||
if( null==rc ){
|
||||
map.put(key, rc = new ValueHolder<>(initialValue));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xFinal() method and passed that
|
||||
method's first argument. This function removes the value
|
||||
associated with with the arguments' aggregate context from the
|
||||
map and returns it, returning null if no other UDF method has
|
||||
been called to set up such a mapping. The latter condition will
|
||||
be the case if a UDF is used in a statement which has no result
|
||||
rows.
|
||||
*/
|
||||
public T takeAggregateState(SqlFunction.Arguments args){
|
||||
final ValueHolder<T> h = map.remove(args.getContext().getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final SqlFunction.PerContextState<T> map =
|
||||
new SqlFunction.PerContextState<>();
|
||||
private final PerContextState<T> map = new PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
|
@@ -22,6 +22,17 @@ import org.sqlite.jni.capi.sqlite3_value;
|
||||
*/
|
||||
public interface SqlFunction {
|
||||
|
||||
public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC;
|
||||
public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS;
|
||||
public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY;
|
||||
public static final int UTF8 = CApi.SQLITE_UTF8;
|
||||
public static final int UTF16 = CApi.SQLITE_UTF16;
|
||||
// /**
|
||||
// For Window functions only and is not currently bound because
|
||||
// doing so may require exposing sqlite3_value for effective use.
|
||||
// */
|
||||
// public static final int SUBTYPE = CApi.SQLITE_SUBTYPE;
|
||||
|
||||
/**
|
||||
The Arguments type is an abstraction on top of the lower-level
|
||||
UDF function argument types. It provides _most_ of the functionality
|
||||
@@ -49,6 +60,83 @@ public interface SqlFunction {
|
||||
this.length = this.args.length;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the sqlite3_value at the given argument index or throws
|
||||
an IllegalArgumentException exception if ndx is out of range.
|
||||
*/
|
||||
private sqlite3_value valueAt(int ndx){
|
||||
if(ndx<0 || ndx>=args.length){
|
||||
throw new IllegalArgumentException(
|
||||
"SQL function argument index "+ndx+" is out of range."
|
||||
);
|
||||
}
|
||||
return args[ndx];
|
||||
}
|
||||
|
||||
//! Returns the underlying sqlite3_context for these arguments.
|
||||
sqlite3_context getContext(){return cx;}
|
||||
|
||||
public int getArgCount(){ return args.length; }
|
||||
|
||||
public int getInt(int argNdx){return CApi.sqlite3_value_int(valueAt(argNdx));}
|
||||
public long getInt64(int argNdx){return CApi.sqlite3_value_int64(valueAt(argNdx));}
|
||||
public double getDouble(int argNdx){return CApi.sqlite3_value_double(valueAt(argNdx));}
|
||||
public byte[] getBlob(int argNdx){return CApi.sqlite3_value_blob(valueAt(argNdx));}
|
||||
public byte[] getText(int argNdx){return CApi.sqlite3_value_text(valueAt(argNdx));}
|
||||
public String getText16(int argNdx){return CApi.sqlite3_value_text16(valueAt(argNdx));}
|
||||
public int getBytes(int argNdx){return CApi.sqlite3_value_bytes(valueAt(argNdx));}
|
||||
public int getBytes16(int argNdx){return CApi.sqlite3_value_bytes16(valueAt(argNdx));}
|
||||
public Object getObject(int argNdx){return CApi.sqlite3_value_java_object(valueAt(argNdx));}
|
||||
public <T> T getObject(int argNdx, Class<T> type){
|
||||
return CApi.sqlite3_value_java_object(valueAt(argNdx), type);
|
||||
}
|
||||
|
||||
public int getType(int argNdx){return CApi.sqlite3_value_type(valueAt(argNdx));}
|
||||
public int getSubtype(int argNdx){return CApi.sqlite3_value_subtype(valueAt(argNdx));}
|
||||
public int getNumericType(int argNdx){return CApi.sqlite3_value_numeric_type(valueAt(argNdx));}
|
||||
public int getNoChange(int argNdx){return CApi.sqlite3_value_nochange(valueAt(argNdx));}
|
||||
public boolean getFromBind(int argNdx){return CApi.sqlite3_value_frombind(valueAt(argNdx));}
|
||||
public int getEncoding(int argNdx){return CApi.sqlite3_value_encoding(valueAt(argNdx));}
|
||||
|
||||
public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); }
|
||||
public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); }
|
||||
public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); }
|
||||
public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);}
|
||||
public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);}
|
||||
public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);}
|
||||
public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);}
|
||||
public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);}
|
||||
public void resultNull(){CApi.sqlite3_result_null(cx);}
|
||||
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
|
||||
public void resultSubtype(int subtype){CApi.sqlite3_result_subtype(cx, subtype);}
|
||||
public void resultZeroBlob(long n){
|
||||
// Throw on error? If n is too big,
|
||||
// sqlite3_result_error_toobig() is automatically called.
|
||||
CApi.sqlite3_result_zeroblob64(cx, n);
|
||||
}
|
||||
|
||||
public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);}
|
||||
public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);}
|
||||
public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);}
|
||||
public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);}
|
||||
public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);}
|
||||
|
||||
public void setAuxData(int argNdx, Object o){
|
||||
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
|
||||
|
||||
The value of the N parameter to these interfaces should be
|
||||
non-negative. Future enhancements may make use of negative N
|
||||
values to define new kinds of function caching behavior.
|
||||
*/
|
||||
valueAt(argNdx);
|
||||
CApi.sqlite3_set_auxdata(cx, argNdx, o);
|
||||
}
|
||||
|
||||
public Object getAuxData(int argNdx){
|
||||
valueAt(argNdx);
|
||||
return CApi.sqlite3_get_auxdata(cx, argNdx);
|
||||
}
|
||||
|
||||
/**
|
||||
Wrapper for a single SqlFunction argument. Primarily intended
|
||||
for use with the Arguments class's Iterable interface.
|
||||
@@ -72,7 +160,7 @@ public interface SqlFunction {
|
||||
public int getBytes(){return a.getBytes(ndx);}
|
||||
public int getBytes16(){return a.getBytes16(ndx);}
|
||||
public Object getObject(){return a.getObject(ndx);}
|
||||
public <T> T getObjectCasted(Class<T> type){ return a.getObjectCasted(ndx, type); }
|
||||
public <T> T getObject(Class<T> type){ return a.getObject(ndx, type); }
|
||||
public int getType(){return a.getType(ndx);}
|
||||
public Object getAuxData(){return a.getAuxData(ndx);}
|
||||
public void setAuxData(Object o){a.setAuxData(ndx, o);}
|
||||
@@ -87,147 +175,6 @@ public interface SqlFunction {
|
||||
return java.util.Arrays.stream(proxies).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the sqlite3_value at the given argument index or throws
|
||||
an IllegalArgumentException exception if ndx is out of range.
|
||||
*/
|
||||
private sqlite3_value valueAt(int ndx){
|
||||
if(ndx<0 || ndx>=args.length){
|
||||
throw new IllegalArgumentException(
|
||||
"SQL function argument index "+ndx+" is out of range."
|
||||
);
|
||||
}
|
||||
return args[ndx];
|
||||
}
|
||||
|
||||
sqlite3_context getContext(){return cx;}
|
||||
|
||||
public int getArgCount(){ return args.length; }
|
||||
|
||||
public int getInt(int arg){return CApi.sqlite3_value_int(valueAt(arg));}
|
||||
public long getInt64(int arg){return CApi.sqlite3_value_int64(valueAt(arg));}
|
||||
public double getDouble(int arg){return CApi.sqlite3_value_double(valueAt(arg));}
|
||||
public byte[] getBlob(int arg){return CApi.sqlite3_value_blob(valueAt(arg));}
|
||||
public byte[] getText(int arg){return CApi.sqlite3_value_text(valueAt(arg));}
|
||||
public String getText16(int arg){return CApi.sqlite3_value_text16(valueAt(arg));}
|
||||
public int getBytes(int arg){return CApi.sqlite3_value_bytes(valueAt(arg));}
|
||||
public int getBytes16(int arg){return CApi.sqlite3_value_bytes16(valueAt(arg));}
|
||||
public Object getObject(int arg){return CApi.sqlite3_value_java_object(valueAt(arg));}
|
||||
public <T> T getObjectCasted(int arg, Class<T> type){
|
||||
return CApi.sqlite3_value_java_casted(valueAt(arg), type);
|
||||
}
|
||||
|
||||
public int getType(int arg){return CApi.sqlite3_value_type(valueAt(arg));}
|
||||
public int getSubtype(int arg){return CApi.sqlite3_value_subtype(valueAt(arg));}
|
||||
public int getNumericType(int arg){return CApi.sqlite3_value_numeric_type(valueAt(arg));}
|
||||
public int getNoChange(int arg){return CApi.sqlite3_value_nochange(valueAt(arg));}
|
||||
public boolean getFromBind(int arg){return CApi.sqlite3_value_frombind(valueAt(arg));}
|
||||
public int getEncoding(int arg){return CApi.sqlite3_value_encoding(valueAt(arg));}
|
||||
|
||||
public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); }
|
||||
public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); }
|
||||
public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); }
|
||||
public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);}
|
||||
public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);}
|
||||
public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);}
|
||||
public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);}
|
||||
public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);}
|
||||
public void resultNull(){CApi.sqlite3_result_null(cx);}
|
||||
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
|
||||
public void resultZeroBlob(long n){
|
||||
// Throw on error? If n is too big,
|
||||
// sqlite3_result_error_toobig() is automatically called.
|
||||
CApi.sqlite3_result_zeroblob64(cx, n);
|
||||
}
|
||||
|
||||
public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);}
|
||||
public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);}
|
||||
public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);}
|
||||
public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);}
|
||||
public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);}
|
||||
|
||||
public void setAuxData(int arg, Object o){
|
||||
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
|
||||
|
||||
The value of the N parameter to these interfaces should be
|
||||
non-negative. Future enhancements may make use of negative N
|
||||
values to define new kinds of function caching behavior.
|
||||
*/
|
||||
valueAt(arg);
|
||||
CApi.sqlite3_set_auxdata(cx, arg, o);
|
||||
}
|
||||
|
||||
public Object getAuxData(int arg){
|
||||
valueAt(arg);
|
||||
return CApi.sqlite3_get_auxdata(cx, arg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
managing their accumulator state across calls to the UDF's
|
||||
callbacks.
|
||||
|
||||
<p>T must be of a type which can be legally stored as a value in
|
||||
java.util.HashMap<KeyType,T>.
|
||||
|
||||
<p>If a given aggregate or window function is called multiple times
|
||||
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
|
||||
then the clients need some way of knowing which call is which so
|
||||
that they can map their state between their various UDF callbacks
|
||||
and reset it via xFinal(). This class takes care of such
|
||||
mappings.
|
||||
|
||||
<p>This class works by mapping
|
||||
sqlite3_context.getAggregateContext() to a single piece of
|
||||
state, of a client-defined type (the T part of this class), which
|
||||
persists across a "matching set" of the UDF's callbacks.
|
||||
|
||||
<p>This class is a helper providing commonly-needed functionality
|
||||
- it is not required for use with aggregate or window functions.
|
||||
Client UDFs are free to perform such mappings using custom
|
||||
approaches. The provided {@link AggregateFunction} and {@link
|
||||
WindowFunction} classes use this.
|
||||
*/
|
||||
public static final class PerContextState<T> {
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||
methods, passing it that method's first argument and an initial
|
||||
value for the persistent state. If there is currently no
|
||||
mapping for the given context within the map, one is created
|
||||
using the given initial value, else the existing one is used
|
||||
and the 2nd argument is ignored. It returns a ValueHolder<T>
|
||||
which can be used to modify that state directly without
|
||||
requiring that the client update the underlying map's entry.
|
||||
|
||||
<p>The caller is obligated to eventually call
|
||||
takeAggregateState() to clear the mapping.
|
||||
*/
|
||||
public ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
|
||||
final Long key = args.getContext().getAggregateContext(true);
|
||||
ValueHolder<T> rc = null==key ? null : map.get(key);
|
||||
if( null==rc ){
|
||||
map.put(key, rc = new ValueHolder<>(initialValue));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xFinal() method and passed that
|
||||
method's first argument. This function removes the value
|
||||
associated with with the arguments' aggregate context from the
|
||||
map and returns it, returning null if no other UDF method has
|
||||
been called to set up such a mapping. The latter condition will
|
||||
be the case if a UDF is used in a statement which has no result
|
||||
rows.
|
||||
*/
|
||||
public T takeAggregateState(SqlFunction.Arguments args){
|
||||
final ValueHolder<T> h = map.remove(args.getContext().getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,7 +182,7 @@ public interface SqlFunction {
|
||||
for use with the org.sqlite.jni.capi.ScalarFunction interface.
|
||||
*/
|
||||
static final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction {
|
||||
final ScalarFunction impl;
|
||||
private final ScalarFunction impl;
|
||||
ScalarAdapter(ScalarFunction impl){
|
||||
this.impl = impl;
|
||||
}
|
||||
@@ -261,8 +208,9 @@ public interface SqlFunction {
|
||||
Internal-use adapter for wrapping this package's AggregateFunction
|
||||
for use with the org.sqlite.jni.capi.AggregateFunction interface.
|
||||
*/
|
||||
static final class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction {
|
||||
final AggregateFunction impl;
|
||||
static /*cannot be final without duplicating the whole body in WindowAdapter*/
|
||||
class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction {
|
||||
private final AggregateFunction impl;
|
||||
AggregateAdapter(AggregateFunction impl){
|
||||
this.impl = impl;
|
||||
}
|
||||
@@ -282,8 +230,9 @@ public interface SqlFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
As for the xFinal() argument of the C API's sqlite3_create_function().
|
||||
If the proxied function throws, it is translated into a sqlite3_result_error().
|
||||
As for the xFinal() argument of the C API's
|
||||
sqlite3_create_function(). If the proxied function throws, it
|
||||
is translated into a sqlite3_result_error().
|
||||
*/
|
||||
public void xFinal(sqlite3_context cx){
|
||||
try{
|
||||
@@ -298,4 +247,46 @@ public interface SqlFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Internal-use adapter for wrapping this package's WindowFunction
|
||||
for use with the org.sqlite.jni.capi.WindowFunction interface.
|
||||
*/
|
||||
static final class WindowAdapter extends AggregateAdapter {
|
||||
private final WindowFunction impl;
|
||||
WindowAdapter(WindowFunction impl){
|
||||
super(impl);
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
Proxies this.impl.xInverse(), adapting the call arguments to that
|
||||
function's signature. If the proxied function throws, it is
|
||||
translated to sqlite_result_error() with the exception's
|
||||
message.
|
||||
*/
|
||||
public void xInverse(sqlite3_context cx, sqlite3_value[] args){
|
||||
try{
|
||||
impl.xInverse( new SqlFunction.Arguments(cx, args) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
As for the xValue() argument of the C API's sqlite3_create_window_function().
|
||||
If the proxied function throws, it is translated into a sqlite3_result_error().
|
||||
*/
|
||||
public void xValue(sqlite3_context cx){
|
||||
try{
|
||||
impl.xValue( new SqlFunction.Arguments(cx, null) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void xDestroy(){
|
||||
impl.xDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -29,6 +29,38 @@ import org.sqlite.jni.capi.OutputPointer;
|
||||
public final class Sqlite implements AutoCloseable {
|
||||
private sqlite3 db;
|
||||
|
||||
public static final int OPEN_READWRITE = CApi.SQLITE_OPEN_READWRITE;
|
||||
public static final int OPEN_CREATE = CApi.SQLITE_OPEN_CREATE;
|
||||
public static final int OPEN_EXRESCODE = CApi.SQLITE_OPEN_EXRESCODE;
|
||||
public static final int TXN_NONE = CApi.SQLITE_TXN_NONE;
|
||||
public static final int TXN_READ = CApi.SQLITE_TXN_READ;
|
||||
public static final int TXN_WRITE = CApi.SQLITE_TXN_WRITE;
|
||||
|
||||
public static final int STATUS_MEMORY_USED = CApi.SQLITE_STATUS_MEMORY_USED;
|
||||
public static final int STATUS_PAGECACHE_USED = CApi.SQLITE_STATUS_PAGECACHE_USED;
|
||||
public static final int STATUS_PAGECACHE_OVERFLOW = CApi.SQLITE_STATUS_PAGECACHE_OVERFLOW;
|
||||
public static final int STATUS_MALLOC_SIZE = CApi.SQLITE_STATUS_MALLOC_SIZE;
|
||||
public static final int STATUS_PARSER_STACK = CApi.SQLITE_STATUS_PARSER_STACK;
|
||||
public static final int STATUS_PAGECACHE_SIZE = CApi.SQLITE_STATUS_PAGECACHE_SIZE;
|
||||
public static final int STATUS_MALLOC_COUNT = CApi.SQLITE_STATUS_MALLOC_COUNT;
|
||||
|
||||
public static final int LIMIT_LENGTH = CApi.SQLITE_LIMIT_LENGTH;
|
||||
public static final int LIMIT_SQL_LENGTH = CApi.SQLITE_LIMIT_SQL_LENGTH;
|
||||
public static final int LIMIT_COLUMN = CApi.SQLITE_LIMIT_COLUMN;
|
||||
public static final int LIMIT_EXPR_DEPTH = CApi.SQLITE_LIMIT_EXPR_DEPTH;
|
||||
public static final int LIMIT_COMPOUND_SELECT = CApi.SQLITE_LIMIT_COMPOUND_SELECT;
|
||||
public static final int LIMIT_VDBE_OP = CApi.SQLITE_LIMIT_VDBE_OP;
|
||||
public static final int LIMIT_FUNCTION_ARG = CApi.SQLITE_LIMIT_FUNCTION_ARG;
|
||||
public static final int LIMIT_ATTACHED = CApi.SQLITE_LIMIT_ATTACHED;
|
||||
public static final int LIMIT_LIKE_PATTERN_LENGTH = CApi.SQLITE_LIMIT_LIKE_PATTERN_LENGTH;
|
||||
public static final int LIMIT_VARIABLE_NUMBER = CApi.SQLITE_LIMIT_VARIABLE_NUMBER;
|
||||
public static final int LIMIT_TRIGGER_DEPTH = CApi.SQLITE_LIMIT_TRIGGER_DEPTH;
|
||||
public static final int LIMIT_WORKER_THREADS = CApi.SQLITE_LIMIT_WORKER_THREADS;
|
||||
|
||||
public static final int PREPARE_PERSISTENT = CApi.SQLITE_PREPARE_PERSISTENT;
|
||||
public static final int PREPARE_NORMALIZE = CApi.SQLITE_PREPARE_NORMALIZE;
|
||||
public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB;
|
||||
|
||||
//! Used only by the open() factory functions.
|
||||
private Sqlite(sqlite3 db){
|
||||
this.db = db;
|
||||
@@ -63,6 +95,33 @@ public final class Sqlite implements AutoCloseable {
|
||||
return open(filename, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, null);
|
||||
}
|
||||
|
||||
public static String libVersion(){
|
||||
return CApi.sqlite3_libversion();
|
||||
}
|
||||
|
||||
public static int libVersionNumber(){
|
||||
return CApi.sqlite3_libversion_number();
|
||||
}
|
||||
|
||||
public static String libSourceId(){
|
||||
return CApi.sqlite3_sourceid();
|
||||
}
|
||||
|
||||
/**
|
||||
As per sqlite3_status64(), but returns its current and high-water
|
||||
results as a two-element array. Throws if the first argument is
|
||||
not one of the STATUS_... constants.
|
||||
*/
|
||||
public long[] libStatus(int op, boolean resetStats){
|
||||
org.sqlite.jni.capi.OutputPointer.Int64 pCurrent =
|
||||
new org.sqlite.jni.capi.OutputPointer.Int64();
|
||||
org.sqlite.jni.capi.OutputPointer.Int64 pHighwater =
|
||||
new org.sqlite.jni.capi.OutputPointer.Int64();
|
||||
final int rc = CApi.sqlite3_status64(op, pCurrent, pHighwater, resetStats);
|
||||
checkRc(rc);
|
||||
return new long[] {pCurrent.value, pHighwater.value};
|
||||
}
|
||||
|
||||
@Override public void close(){
|
||||
if(null!=this.db){
|
||||
this.db.close();
|
||||
@@ -70,6 +129,22 @@ public final class Sqlite implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value of the native library's build-time value of the
|
||||
SQLITE_THREADSAFE build option.
|
||||
*/
|
||||
public static int libThreadsafe(){
|
||||
return CApi.sqlite3_threadsafe();
|
||||
}
|
||||
|
||||
public static boolean strglob(String glob, String txt){
|
||||
return 0==CApi.sqlite3_strglob(glob, txt);
|
||||
}
|
||||
|
||||
public static boolean strlike(String glob, String txt, char escChar){
|
||||
return 0==CApi.sqlite3_strlike(glob, txt, escChar);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns this object's underlying native db handle, or null if
|
||||
this instance has been closed. This is very specifically not
|
||||
@@ -77,7 +152,7 @@ public final class Sqlite implements AutoCloseable {
|
||||
*/
|
||||
sqlite3 nativeHandle(){ return this.db; }
|
||||
|
||||
private sqlite3 affirmOpen(){
|
||||
private sqlite3 thisDb(){
|
||||
if( null==db || 0==db.getNativePointer() ){
|
||||
throw new IllegalArgumentException("This database instance is closed.");
|
||||
}
|
||||
@@ -88,12 +163,261 @@ public final class Sqlite implements AutoCloseable {
|
||||
// return s==null ? null : s.getBytes(StandardCharsets.UTF_8);
|
||||
// }
|
||||
|
||||
private void affirmRcOk(int rc){
|
||||
/**
|
||||
If rc!=0, throws an SqliteException. If this db is currently
|
||||
opened and has non-0 sqlite3_errcode(), the error state is
|
||||
extracted from it, else only the string form of rc is used. It is
|
||||
the caller's responsibility to filter out non-error codes such as
|
||||
SQLITE_ROW and SQLITE_DONE before calling this.
|
||||
*/
|
||||
private void checkRc(int rc){
|
||||
if( 0!=rc ){
|
||||
throw new SqliteException(db);
|
||||
if( null==db || 0==sqlite3_errcode(db)) throw new SqliteException(rc);
|
||||
else throw new SqliteException(db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
prepFlags must be 0 or a bitmask of the PREPARE_... constants.
|
||||
|
||||
prepare() TODOs include:
|
||||
|
||||
- overloads taking byte[] and ByteBuffer.
|
||||
|
||||
- multi-statement processing, like CApi.sqlite3_prepare_multi()
|
||||
but using a callback specific to the higher-level Stmt class
|
||||
rather than the sqlite3_stmt class.
|
||||
*/
|
||||
public Stmt prepare(String sql, int prepFlags){
|
||||
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
|
||||
final int rc = sqlite3_prepare_v3(thisDb(), sql, prepFlags, out);
|
||||
checkRc(rc);
|
||||
final sqlite3_stmt q = out.take();
|
||||
if( null==q ){
|
||||
/* The C-level API treats input which is devoid of SQL
|
||||
statements (e.g. all comments or an empty string) as success
|
||||
but returns a NULL sqlite3_stmt object. In higher-level APIs,
|
||||
wrapping a "successful NULL" object that way is tedious to
|
||||
use because it forces clients and/or wrapper-level code to
|
||||
check for that unusual case. In practice, higher-level
|
||||
bindings are generally better-served by treating empty SQL
|
||||
input as an error. */
|
||||
throw new IllegalArgumentException("Input contains no SQL statements.");
|
||||
}
|
||||
return new Stmt(this, q);
|
||||
}
|
||||
|
||||
public Stmt prepare(String sql){
|
||||
return prepare(sql, 0);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f){
|
||||
int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep,
|
||||
new SqlFunction.ScalarAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, ScalarFunction f){
|
||||
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f){
|
||||
int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep,
|
||||
new SqlFunction.AggregateAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, AggregateFunction f){
|
||||
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, WindowFunction f){
|
||||
int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep,
|
||||
new SqlFunction.WindowAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, WindowFunction f){
|
||||
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
|
||||
}
|
||||
|
||||
public long changes(){
|
||||
return CApi.sqlite3_changes64(thisDb());
|
||||
}
|
||||
|
||||
public long totalChanges(){
|
||||
return CApi.sqlite3_total_changes64(thisDb());
|
||||
}
|
||||
|
||||
public long lastInsertRowId(){
|
||||
return CApi.sqlite3_last_insert_rowid(thisDb());
|
||||
}
|
||||
|
||||
public void setLastInsertRowId(long rowId){
|
||||
CApi.sqlite3_set_last_insert_rowid(thisDb(), rowId);
|
||||
}
|
||||
|
||||
public void interrupt(){
|
||||
CApi.sqlite3_interrupt(thisDb());
|
||||
}
|
||||
|
||||
public boolean isInterrupted(){
|
||||
return CApi.sqlite3_is_interrupted(thisDb());
|
||||
}
|
||||
|
||||
public boolean isAutoCommit(){
|
||||
return CApi.sqlite3_get_autocommit(thisDb());
|
||||
}
|
||||
|
||||
public void setBusyTimeout(int ms){
|
||||
checkRc(CApi.sqlite3_busy_timeout(thisDb(), ms));
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_txn_state(). Returns one of TXN_NONE, TXN_READ,
|
||||
or TXN_WRITE to denote this database's current transaction state
|
||||
for the given schema name (or the most restrictive state of any
|
||||
schema if zSchema is null).
|
||||
*/
|
||||
public int transactionState(String zSchema){
|
||||
return CApi.sqlite3_txn_state(thisDb(), zSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_db_name(). Returns null if passed an unknown
|
||||
index.
|
||||
*/
|
||||
public String dbName(int dbNdx){
|
||||
return CApi.sqlite3_db_name(thisDb(), dbNdx);
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_db_filename(). Returns null if passed an
|
||||
unknown db name.
|
||||
*/
|
||||
public String dbFileName(String dbName){
|
||||
return CApi.sqlite3_db_filename(thisDb(), dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to the variant of sqlite3_db_config() for configuring the
|
||||
SQLITE_DBCONFIG_MAINDBNAME option. Throws on error.
|
||||
*/
|
||||
public void setMainDbName(String name){
|
||||
checkRc(
|
||||
CApi.sqlite3_db_config(thisDb(), CApi.SQLITE_DBCONFIG_MAINDBNAME,
|
||||
name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_db_readonly() but throws an SqliteException
|
||||
with result code SQLITE_NOTFOUND if given an unknown database
|
||||
name.
|
||||
*/
|
||||
public boolean readOnly(String dbName){
|
||||
final int rc = CApi.sqlite3_db_readonly(thisDb(), dbName);
|
||||
if( 0==rc ) return false;
|
||||
else if( rc>0 ) return true;
|
||||
throw new SqliteException(CApi.SQLITE_NOTFOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_db_release_memory().
|
||||
*/
|
||||
public void releaseMemory(){
|
||||
CApi.sqlite3_db_release_memory(thisDb());
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_release_memory().
|
||||
*/
|
||||
public static int releaseMemory(int n){
|
||||
return CApi.sqlite3_release_memory(n);
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_limit(). limitId must be one of the
|
||||
LIMIT_... constants.
|
||||
|
||||
Returns the old limit for the given option. If newLimit is
|
||||
negative, it returns the old limit without modifying the limit.
|
||||
|
||||
If sqlite3_limit() returns a negative value, this function throws
|
||||
an SqliteException with the SQLITE_RANGE result code but no
|
||||
further error info (because that case does not qualify as a
|
||||
db-level error). Such errors may indicate an invalid argument
|
||||
value or an invalid range for newLimit (the underlying function
|
||||
does not differentiate between those).
|
||||
*/
|
||||
public int limit(int limitId, int newLimit){
|
||||
final int rc = CApi.sqlite3_limit(thisDb(), limitId, newLimit);
|
||||
if( rc<0 ){
|
||||
throw new SqliteException(CApi.SQLITE_RANGE);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_errstr().
|
||||
*/
|
||||
static String errstr(int resultCode){
|
||||
return CApi.sqlite3_errstr(resultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
A wrapper object for use with tableColumnMetadata(). They are
|
||||
created and populated only via that interface.
|
||||
*/
|
||||
public final class TableColumnMetadata {
|
||||
Boolean pNotNull = null;
|
||||
Boolean pPrimaryKey = null;
|
||||
Boolean pAutoinc = null;
|
||||
String pzCollSeq = null;
|
||||
String pzDataType = null;
|
||||
|
||||
private TableColumnMetadata(){}
|
||||
|
||||
public String getDataType(){ return pzDataType; }
|
||||
public String getCollation(){ return pzCollSeq; }
|
||||
public boolean isNotNull(){ return pNotNull; }
|
||||
public boolean isPrimaryKey(){ return pPrimaryKey; }
|
||||
public boolean isAutoincrement(){ return pAutoinc; }
|
||||
}
|
||||
|
||||
/**
|
||||
Returns data about a database, table, and (optionally) column
|
||||
(which may be null), as per sqlite3_table_column_metadata().
|
||||
Throws if passed invalid arguments, else returns the result as a
|
||||
new TableColumnMetadata object.
|
||||
*/
|
||||
TableColumnMetadata tableColumnMetadata(
|
||||
String zDbName, String zTableName, String zColumnName
|
||||
){
|
||||
org.sqlite.jni.capi.OutputPointer.String pzDataType
|
||||
= new org.sqlite.jni.capi.OutputPointer.String();
|
||||
org.sqlite.jni.capi.OutputPointer.String pzCollSeq
|
||||
= new org.sqlite.jni.capi.OutputPointer.String();
|
||||
org.sqlite.jni.capi.OutputPointer.Bool pNotNull
|
||||
= new org.sqlite.jni.capi.OutputPointer.Bool();
|
||||
org.sqlite.jni.capi.OutputPointer.Bool pPrimaryKey
|
||||
= new org.sqlite.jni.capi.OutputPointer.Bool();
|
||||
org.sqlite.jni.capi.OutputPointer.Bool pAutoinc
|
||||
= new org.sqlite.jni.capi.OutputPointer.Bool();
|
||||
final int rc = CApi.sqlite3_table_column_metadata(
|
||||
thisDb(), zDbName, zTableName, zColumnName,
|
||||
pzDataType, pzCollSeq, pNotNull, pPrimaryKey, pAutoinc
|
||||
);
|
||||
checkRc(rc);
|
||||
TableColumnMetadata rv = new TableColumnMetadata();
|
||||
rv.pzDataType = pzDataType.value;
|
||||
rv.pzCollSeq = pzCollSeq.value;
|
||||
rv.pNotNull = pNotNull.value;
|
||||
rv.pPrimaryKey = pPrimaryKey.value;
|
||||
rv.pAutoinc = pAutoinc.value;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to
|
||||
create new instances.
|
||||
@@ -101,23 +425,44 @@ public final class Sqlite implements AutoCloseable {
|
||||
public final class Stmt implements AutoCloseable {
|
||||
private Sqlite _db = null;
|
||||
private sqlite3_stmt stmt = null;
|
||||
/**
|
||||
We save the result column count in order to prevent having to
|
||||
call into C to fetch that value every time we need to check
|
||||
that value for the columnXyz() methods.
|
||||
*/
|
||||
private final int resultColCount;
|
||||
|
||||
/** Only called by the prepare() factory functions. */
|
||||
Stmt(Sqlite db, sqlite3_stmt stmt){
|
||||
this._db = db;
|
||||
this.stmt = stmt;
|
||||
this.resultColCount = CApi.sqlite3_column_count(stmt);
|
||||
}
|
||||
|
||||
sqlite3_stmt nativeHandle(){
|
||||
return stmt;
|
||||
}
|
||||
|
||||
private sqlite3_stmt affirmOpen(){
|
||||
/**
|
||||
If this statement is still opened, its low-level handle is
|
||||
returned, eelse an IllegalArgumentException is thrown.
|
||||
*/
|
||||
private sqlite3_stmt thisStmt(){
|
||||
if( null==stmt || 0==stmt.getNativePointer() ){
|
||||
throw new IllegalArgumentException("This Stmt has been finalized.");
|
||||
}
|
||||
return stmt;
|
||||
}
|
||||
|
||||
/** Throws if n is out of range of this.resultColCount. Intended
|
||||
to be used by the columnXyz() methods. */
|
||||
private sqlite3_stmt checkColIndex(int n){
|
||||
if(n<0 || n>=this.resultColCount){
|
||||
throw new IllegalArgumentException("Column index "+n+" is out of range.");
|
||||
}
|
||||
return thisStmt();
|
||||
}
|
||||
|
||||
/**
|
||||
Corresponds to sqlite3_finalize(), but we cannot override the
|
||||
name finalize() here because this one requires a different
|
||||
@@ -140,7 +485,9 @@ public final class Sqlite implements AutoCloseable {
|
||||
|
||||
/**
|
||||
Throws if rc is any value other than 0, SQLITE_ROW, or
|
||||
SQLITE_DONE, else returns rc.
|
||||
SQLITE_DONE, else returns rc. Error state for the exception is
|
||||
extracted from this statement object (if it's opened) or the
|
||||
string form of rc.
|
||||
*/
|
||||
private int checkRc(int rc){
|
||||
switch(rc){
|
||||
@@ -148,16 +495,33 @@ public final class Sqlite implements AutoCloseable {
|
||||
case SQLITE_ROW:
|
||||
case SQLITE_DONE: return rc;
|
||||
default:
|
||||
throw new SqliteException(this);
|
||||
if( null==stmt ) throw new SqliteException(rc);
|
||||
else throw new SqliteException(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Works like sqlite3_step() but throws SqliteException for any
|
||||
result other than 0, SQLITE_ROW, or SQLITE_DONE.
|
||||
Works like sqlite3_step() but returns true for SQLITE_ROW,
|
||||
false for SQLITE_DONE, and throws SqliteException for any other
|
||||
result.
|
||||
*/
|
||||
public int step(){
|
||||
return checkRc(sqlite3_step(affirmOpen()));
|
||||
public boolean step(){
|
||||
switch(checkRc(sqlite3_step(thisStmt()))){
|
||||
case CApi.SQLITE_ROW: return true;
|
||||
case CApi.SQLITE_DONE: return false;
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"This \"cannot happen\": all possible result codes were checked already."
|
||||
);
|
||||
}
|
||||
/*
|
||||
Potential signature change TODO:
|
||||
|
||||
boolean step()
|
||||
|
||||
Returning true for SQLITE_ROW and false for anything else.
|
||||
Those semantics have proven useful in the WASM/JS bindings.
|
||||
*/
|
||||
}
|
||||
|
||||
public Sqlite db(){ return this._db; }
|
||||
@@ -166,53 +530,109 @@ public final class Sqlite implements AutoCloseable {
|
||||
Works like sqlite3_reset() but throws on error.
|
||||
*/
|
||||
public void reset(){
|
||||
checkRc(sqlite3_reset(affirmOpen()));
|
||||
checkRc(CApi.sqlite3_reset(thisStmt()));
|
||||
}
|
||||
|
||||
public void clearBindings(){
|
||||
sqlite3_clear_bindings( affirmOpen() );
|
||||
CApi.sqlite3_clear_bindings( thisStmt() );
|
||||
}
|
||||
public void bindInt(int ndx, int val){
|
||||
checkRc(CApi.sqlite3_bind_int(thisStmt(), ndx, val));
|
||||
}
|
||||
public void bindInt64(int ndx, long val){
|
||||
checkRc(CApi.sqlite3_bind_int64(thisStmt(), ndx, val));
|
||||
}
|
||||
public void bindDouble(int ndx, double val){
|
||||
checkRc(CApi.sqlite3_bind_double(thisStmt(), ndx, val));
|
||||
}
|
||||
public void bindObject(int ndx, Object o){
|
||||
checkRc(CApi.sqlite3_bind_java_object(thisStmt(), ndx, o));
|
||||
}
|
||||
public void bindNull(int ndx){
|
||||
checkRc(CApi.sqlite3_bind_null(thisStmt(), ndx));
|
||||
}
|
||||
public int bindParameterCount(){
|
||||
return CApi.sqlite3_bind_parameter_count(thisStmt());
|
||||
}
|
||||
public int bindParameterIndex(String paramName){
|
||||
return CApi.sqlite3_bind_parameter_index(thisStmt(), paramName);
|
||||
}
|
||||
public String bindParameterName(int ndx){
|
||||
return CApi.sqlite3_bind_parameter_name(thisStmt(), ndx);
|
||||
}
|
||||
public void bindText(int ndx, byte[] utf8){
|
||||
checkRc(CApi.sqlite3_bind_text(thisStmt(), ndx, utf8));
|
||||
}
|
||||
public void bindText(int ndx, String asUtf8){
|
||||
checkRc(CApi.sqlite3_bind_text(thisStmt(), ndx, asUtf8));
|
||||
}
|
||||
public void bindText16(int ndx, byte[] utf16){
|
||||
checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, utf16));
|
||||
}
|
||||
public void bindText16(int ndx, String txt){
|
||||
checkRc(CApi.sqlite3_bind_text16(thisStmt(), ndx, txt));
|
||||
}
|
||||
public void bindZeroBlob(int ndx, int n){
|
||||
checkRc(CApi.sqlite3_bind_zeroblob(thisStmt(), ndx, n));
|
||||
}
|
||||
public void bindBlob(int ndx, byte[] bytes){
|
||||
checkRc(CApi.sqlite3_bind_blob(thisStmt(), ndx, bytes));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
prepare() TODOs include:
|
||||
|
||||
- overloads taking byte[] and ByteBuffer.
|
||||
|
||||
- multi-statement processing, like CApi.sqlite3_prepare_multi()
|
||||
but using a callback specific to the higher-level Stmt class
|
||||
rather than the sqlite3_stmt class.
|
||||
*/
|
||||
public Stmt prepare(String sql, int prepFlags){
|
||||
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
|
||||
final int rc = sqlite3_prepare_v3(affirmOpen(), sql, prepFlags, out);
|
||||
affirmRcOk(rc);
|
||||
return new Stmt(this, out.take());
|
||||
}
|
||||
|
||||
public Stmt prepare(String sql){
|
||||
return prepare(sql, 0);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){
|
||||
int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
|
||||
new SqlFunction.ScalarAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, ScalarFunction f){
|
||||
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f ){
|
||||
int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
|
||||
new SqlFunction.AggregateAdapter(f));
|
||||
if( 0!=rc ) throw new SqliteException(db);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, AggregateFunction f){
|
||||
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
|
||||
}
|
||||
public byte[] columnBlob(int ndx){
|
||||
return CApi.sqlite3_column_blob( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public byte[] columnText(int ndx){
|
||||
return CApi.sqlite3_column_text( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public String columnText16(int ndx){
|
||||
return CApi.sqlite3_column_text16( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public int columnBytes(int ndx){
|
||||
return CApi.sqlite3_column_bytes( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public int columnBytes16(int ndx){
|
||||
return CApi.sqlite3_column_bytes16( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public int columnInt(int ndx){
|
||||
return CApi.sqlite3_column_int( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public long columnInt64(int ndx){
|
||||
return CApi.sqlite3_column_int64( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public double columnDouble(int ndx){
|
||||
return CApi.sqlite3_column_double( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public int columnType(int ndx){
|
||||
return CApi.sqlite3_column_type( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public String columnDeclType(int ndx){
|
||||
return CApi.sqlite3_column_decltype( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public int columnCount(){
|
||||
return resultColCount;
|
||||
}
|
||||
public int columnDataCount(){
|
||||
return CApi.sqlite3_data_count( thisStmt() );
|
||||
}
|
||||
public Object columnObject(int ndx){
|
||||
return CApi.sqlite3_column_java_object( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public <T> T columnObject(int ndx, Class<T> type){
|
||||
return CApi.sqlite3_column_java_object( checkColIndex(ndx), ndx, type );
|
||||
}
|
||||
public String columnName(int ndx){
|
||||
return CApi.sqlite3_column_name( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public String columnDatabaseName(int ndx){
|
||||
return CApi.sqlite3_column_database_name( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public String columnOriginName(int ndx){
|
||||
return CApi.sqlite3_column_origin_name( checkColIndex(ndx), ndx );
|
||||
}
|
||||
public String columnTableName(int ndx){
|
||||
return CApi.sqlite3_column_table_name( checkColIndex(ndx), ndx );
|
||||
}
|
||||
} /* Stmt class */
|
||||
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
** This file is part of the wrapper1 interface for sqlite3.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
import static org.sqlite.jni.capi.CApi.*;
|
||||
import org.sqlite.jni.capi.CApi;
|
||||
import org.sqlite.jni.capi.sqlite3;
|
||||
|
||||
/**
|
||||
@@ -22,10 +22,10 @@ import org.sqlite.jni.capi.sqlite3;
|
||||
and C via JNI.
|
||||
*/
|
||||
public final class SqliteException extends java.lang.RuntimeException {
|
||||
int errCode = SQLITE_ERROR;
|
||||
int xerrCode = SQLITE_ERROR;
|
||||
int errOffset = -1;
|
||||
int sysErrno = 0;
|
||||
private int errCode = CApi.SQLITE_ERROR;
|
||||
private int xerrCode = CApi.SQLITE_ERROR;
|
||||
private int errOffset = -1;
|
||||
private int sysErrno = 0;
|
||||
|
||||
/**
|
||||
Records the given error string and uses SQLITE_ERROR for both the
|
||||
@@ -38,10 +38,13 @@ public final class SqliteException extends java.lang.RuntimeException {
|
||||
/**
|
||||
Uses sqlite3_errstr(sqlite3ResultCode) for the error string and
|
||||
sets both the error code and extended error code to the given
|
||||
value.
|
||||
value. This approach includes no database-level information and
|
||||
systemErrno() will be 0, so is intended only for use with sqlite3
|
||||
APIs for which a result code is not an error but which the
|
||||
higher-level wrapper should treat as one.
|
||||
*/
|
||||
public SqliteException(int sqlite3ResultCode){
|
||||
super(sqlite3_errstr(sqlite3ResultCode));
|
||||
super(CApi.sqlite3_errstr(sqlite3ResultCode));
|
||||
errCode = xerrCode = sqlite3ResultCode;
|
||||
}
|
||||
|
||||
@@ -50,16 +53,16 @@ public final class SqliteException extends java.lang.RuntimeException {
|
||||
must refer to an opened db object). Note that this does NOT close
|
||||
the db.
|
||||
|
||||
Design note: closing the db on error is likely only useful during
|
||||
Design note: closing the db on error is really only useful during
|
||||
a failed db-open operation, and the place(s) where that can
|
||||
happen are inside this library, not client-level code.
|
||||
*/
|
||||
SqliteException(sqlite3 db){
|
||||
super(sqlite3_errmsg(db));
|
||||
errCode = sqlite3_errcode(db);
|
||||
xerrCode = sqlite3_extended_errcode(db);
|
||||
errOffset = sqlite3_error_offset(db);
|
||||
sysErrno = sqlite3_system_errno(db);
|
||||
super(CApi.sqlite3_errmsg(db));
|
||||
errCode = CApi.sqlite3_errcode(db);
|
||||
xerrCode = CApi.sqlite3_extended_errcode(db);
|
||||
errOffset = CApi.sqlite3_error_offset(db);
|
||||
sysErrno = CApi.sqlite3_system_errno(db);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +74,7 @@ public final class SqliteException extends java.lang.RuntimeException {
|
||||
}
|
||||
|
||||
public SqliteException(Sqlite.Stmt stmt){
|
||||
this( stmt.db() );
|
||||
this(stmt.db());
|
||||
}
|
||||
|
||||
public int errcode(){ return errCode; }
|
||||
|
@@ -129,6 +129,11 @@ public class Tester2 implements Runnable {
|
||||
execSql(db, String.join("", sql));
|
||||
}
|
||||
|
||||
/**
|
||||
Executes all SQL statements in the given string. If throwOnError
|
||||
is true then it will throw for any prepare/step errors, else it
|
||||
will return the corresponding non-0 result code.
|
||||
*/
|
||||
public static int execSql(Sqlite dbw, boolean throwOnError, String sql){
|
||||
final sqlite3 db = dbw.nativeHandle();
|
||||
OutputPointer.Int32 oTail = new OutputPointer.Int32();
|
||||
@@ -145,7 +150,7 @@ public class Tester2 implements Runnable {
|
||||
}
|
||||
if( 0==sqlChunk.length ) break;
|
||||
rc = CApi.sqlite3_prepare_v2(db, sqlChunk, outStmt, oTail);
|
||||
if(throwOnError) affirm(0 == rc);
|
||||
if( throwOnError ) affirm(0 == rc);
|
||||
else if( 0!=rc ) break;
|
||||
pos = oTail.value;
|
||||
stmt = outStmt.take();
|
||||
@@ -158,7 +163,7 @@ public class Tester2 implements Runnable {
|
||||
}
|
||||
CApi.sqlite3_finalize(stmt);
|
||||
affirm(0 == stmt.getNativePointer());
|
||||
if(0!=rc && CApi.SQLITE_ROW!=rc && CApi.SQLITE_DONE!=rc){
|
||||
if(CApi.SQLITE_DONE!=rc){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -194,9 +199,9 @@ public class Tester2 implements Runnable {
|
||||
}
|
||||
|
||||
Sqlite openDb(String name){
|
||||
final Sqlite db = Sqlite.open(name, CApi.SQLITE_OPEN_READWRITE|
|
||||
CApi.SQLITE_OPEN_CREATE|
|
||||
CApi.SQLITE_OPEN_EXRESCODE);
|
||||
final Sqlite db = Sqlite.open(name, Sqlite.OPEN_READWRITE|
|
||||
Sqlite.OPEN_CREATE|
|
||||
Sqlite.OPEN_EXRESCODE);
|
||||
++metrics.dbOpen;
|
||||
return db;
|
||||
}
|
||||
@@ -224,19 +229,50 @@ public class Tester2 implements Runnable {
|
||||
|
||||
void testPrepare1(){
|
||||
try (Sqlite db = openDb()) {
|
||||
Sqlite.Stmt stmt = db.prepare("SELECT 1");
|
||||
Sqlite.Stmt stmt = db.prepare("SELECT ?1");
|
||||
Exception e = null;
|
||||
affirm( null!=stmt.nativeHandle() );
|
||||
affirm( CApi.SQLITE_ROW == stmt.step() );
|
||||
affirm( CApi.SQLITE_DONE == stmt.step() );
|
||||
affirm( 1==stmt.bindParameterCount() );
|
||||
affirm( "?1".equals(stmt.bindParameterName(1)) );
|
||||
affirm( null==stmt.bindParameterName(2) );
|
||||
stmt.bindInt64(1, 1);
|
||||
stmt.bindDouble(1, 1.1);
|
||||
stmt.bindObject(1, db);
|
||||
stmt.bindNull(1);
|
||||
stmt.bindText(1, new byte[] {32,32,32});
|
||||
stmt.bindText(1, "123");
|
||||
stmt.bindText16(1, "123".getBytes(StandardCharsets.UTF_16));
|
||||
stmt.bindText16(1, "123");
|
||||
stmt.bindZeroBlob(1, 8);
|
||||
stmt.bindBlob(1, new byte[] {1,2,3,4});
|
||||
stmt.bindInt(1, 17);
|
||||
try{ stmt.bindInt(2,1); }
|
||||
catch(Exception ex){ e = ex; }
|
||||
affirm( null!=e );
|
||||
e = null;
|
||||
affirm( stmt.step() );
|
||||
try{ stmt.columnInt(1); }
|
||||
catch(Exception ex){ e = ex; }
|
||||
affirm( null!=e );
|
||||
e = null;
|
||||
affirm( 17 == stmt.columnInt(0) );
|
||||
affirm( 17L == stmt.columnInt64(0) );
|
||||
affirm( 17.0 == stmt.columnDouble(0) );
|
||||
affirm( "17".equals(stmt.columnText16(0)) );
|
||||
affirm( !stmt.step() );
|
||||
stmt.reset();
|
||||
affirm( CApi.SQLITE_ROW == stmt.step() );
|
||||
affirm( CApi.SQLITE_DONE == stmt.step() );
|
||||
affirm( stmt.step() );
|
||||
affirm( !stmt.step() );
|
||||
affirm( 0 == stmt.finalizeStmt() );
|
||||
affirm( null==stmt.nativeHandle() );
|
||||
|
||||
stmt = db.prepare("SELECT 1");
|
||||
affirm( CApi.SQLITE_ROW == stmt.step() );
|
||||
affirm( 0 == stmt.finalizeStmt() )
|
||||
stmt = db.prepare("SELECT ?");
|
||||
stmt.bindObject(1, db);
|
||||
affirm( stmt.step() );
|
||||
affirm( db==stmt.columnObject(0) );
|
||||
affirm( db==stmt.columnObject(0, Sqlite.class ) );
|
||||
affirm( null==stmt.columnObject(0, Sqlite.Stmt.class ) );
|
||||
affirm( 0==stmt.finalizeStmt() )
|
||||
/* getting a non-0 out of sqlite3_finalize() is tricky */;
|
||||
affirm( null==stmt.nativeHandle() );
|
||||
}
|
||||
@@ -270,7 +306,7 @@ public class Tester2 implements Runnable {
|
||||
|
||||
void testUdfAggregate(){
|
||||
final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0);
|
||||
final ValueHolder<Integer> vh = new ValueHolder<>(0);
|
||||
Sqlite.Stmt q = null;
|
||||
try (Sqlite db = openDb()) {
|
||||
execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)");
|
||||
final AggregateFunction f = new AggregateFunction<Integer>(){
|
||||
@@ -284,18 +320,96 @@ public class Tester2 implements Runnable {
|
||||
final Integer v = this.takeAggregateState(args);
|
||||
if( null==v ) args.resultNull();
|
||||
else args.resultInt(v);
|
||||
vh.value = v;
|
||||
}
|
||||
public void xDestroy(){
|
||||
++xDestroyCalled.value;
|
||||
}
|
||||
};
|
||||
db.createFunction("myagg", -1, f);
|
||||
execSql(db, "select myagg(a) from t");
|
||||
affirm( 6 == vh.value );
|
||||
db.createFunction("summer", 1, f);
|
||||
q = db.prepare(
|
||||
"with cte(v) as ("+
|
||||
"select 3 union all select 5 union all select 7"+
|
||||
") select summer(v), summer(v+1) from cte"
|
||||
/* ------------------^^^^^^^^^^^ ensures that we're handling
|
||||
sqlite3_aggregate_context() properly. */
|
||||
);
|
||||
affirm( q.step() );
|
||||
affirm( 15==q.columnInt(0) );
|
||||
q.finalizeStmt();
|
||||
q = null;
|
||||
affirm( 0 == xDestroyCalled.value );
|
||||
db.createFunction("summerN", -1, f);
|
||||
|
||||
q = db.prepare("select summerN(1,8,9), summerN(2,3,4)");
|
||||
affirm( q.step() );
|
||||
affirm( 18==q.columnInt(0) );
|
||||
affirm( 9==q.columnInt(1) );
|
||||
q.finalizeStmt();
|
||||
q = null;
|
||||
|
||||
}/*db*/
|
||||
finally{
|
||||
if( null!=q ) q.finalizeStmt();
|
||||
}
|
||||
affirm( 1 == xDestroyCalled.value );
|
||||
affirm( 2 == xDestroyCalled.value
|
||||
/* because we've bound the same instance twice */ );
|
||||
}
|
||||
|
||||
private void testUdfWindow(){
|
||||
final Sqlite db = openDb();
|
||||
/* Example window function, table, and results taken from:
|
||||
https://sqlite.org/windowfunctions.html#udfwinfunc */
|
||||
final WindowFunction func = new WindowFunction<Integer>(){
|
||||
//! Impl of xStep() and xInverse()
|
||||
private void xStepInverse(SqlFunction.Arguments args, int v){
|
||||
this.getAggregateState(args,0).value += v;
|
||||
}
|
||||
@Override public void xStep(SqlFunction.Arguments args){
|
||||
this.xStepInverse(args, args.getInt(0));
|
||||
}
|
||||
@Override public void xInverse(SqlFunction.Arguments args){
|
||||
this.xStepInverse(args, -args.getInt(0));
|
||||
}
|
||||
//! Impl of xFinal() and xValue()
|
||||
private void xFinalValue(SqlFunction.Arguments args, Integer v){
|
||||
if(null == v) args.resultNull();
|
||||
else args.resultInt(v);
|
||||
}
|
||||
@Override public void xFinal(SqlFunction.Arguments args){
|
||||
xFinalValue(args, this.takeAggregateState(args));
|
||||
affirm( null == this.getAggregateState(args,null).value );
|
||||
}
|
||||
@Override public void xValue(SqlFunction.Arguments args){
|
||||
xFinalValue(args, this.getAggregateState(args,null).value);
|
||||
}
|
||||
};
|
||||
db.createFunction("winsumint", 1, func);
|
||||
execSql(db, new String[] {
|
||||
"CREATE TEMP TABLE twin(x, y); INSERT INTO twin VALUES",
|
||||
"('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)"
|
||||
});
|
||||
final Sqlite.Stmt stmt = db.prepare(
|
||||
"SELECT x, winsumint(y) OVER ("+
|
||||
"ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+
|
||||
") AS sum_y "+
|
||||
"FROM twin ORDER BY x;"
|
||||
);
|
||||
int n = 0;
|
||||
while( stmt.step() ){
|
||||
final String s = stmt.columnText16(0);
|
||||
final int i = stmt.columnInt(1);
|
||||
switch(++n){
|
||||
case 1: affirm( "a".equals(s) && 9==i ); break;
|
||||
case 2: affirm( "b".equals(s) && 12==i ); break;
|
||||
case 3: affirm( "c".equals(s) && 16==i ); break;
|
||||
case 4: affirm( "d".equals(s) && 12==i ); break;
|
||||
case 5: affirm( "e".equals(s) && 9==i ); break;
|
||||
default: affirm( false /* cannot happen */ );
|
||||
}
|
||||
}
|
||||
stmt.close();
|
||||
affirm( 5 == n );
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void runTests(boolean fromThread) throws Exception {
|
||||
|
46
ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java
Normal file
46
ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
** 2023-10-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 file is part of the wrapper1 interface for sqlite3.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
import org.sqlite.jni.capi.CApi;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
import org.sqlite.jni.capi.sqlite3_context;
|
||||
import org.sqlite.jni.capi.sqlite3_value;
|
||||
|
||||
/**
|
||||
A SqlFunction implementation for window functions. The T type
|
||||
represents the type of data accumulated by this function while it
|
||||
works. e.g. a SUM()-like UDF might use Integer or Long and a
|
||||
CONCAT()-like UDF might use a StringBuilder or a List<String>.
|
||||
*/
|
||||
public abstract class WindowFunction<T> extends AggregateFunction<T> {
|
||||
|
||||
/**
|
||||
As for the xInverse() argument of the C API's
|
||||
sqlite3_create_window_function(). If this function throws, the
|
||||
exception is reported via sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xInverse(SqlFunction.Arguments args);
|
||||
|
||||
/**
|
||||
As for the xValue() argument of the C API's
|
||||
sqlite3_create_window_function(). If this function throws, it is
|
||||
translated into sqlite3_result_error().
|
||||
|
||||
Note that the passed-in object will not actually contain any
|
||||
arguments for xValue() but will contain the context object needed
|
||||
for setting the call's result or error state.
|
||||
*/
|
||||
public abstract void xValue(SqlFunction.Arguments args);
|
||||
|
||||
}
|
@@ -63,6 +63,7 @@ _sqlite3_file_control
|
||||
_sqlite3_finalize
|
||||
_sqlite3_free
|
||||
_sqlite3_get_auxdata
|
||||
_sqlite3_get_autocommit
|
||||
_sqlite3_initialize
|
||||
_sqlite3_keyword_count
|
||||
_sqlite3_keyword_name
|
||||
|
@@ -188,6 +188,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"],
|
||||
["sqlite3_finalize", "int", "sqlite3_stmt*"],
|
||||
["sqlite3_free", undefined,"*"],
|
||||
["sqlite3_get_autocommit", "int", "sqlite3*"],
|
||||
["sqlite3_get_auxdata", "*", "sqlite3_context*", "int"],
|
||||
["sqlite3_initialize", undefined],
|
||||
/*["sqlite3_interrupt", undefined, "sqlite3*"
|
||||
|
@@ -62,7 +62,7 @@
|
||||
|
||||
```
|
||||
{
|
||||
type: string, // one of: 'open', 'close', 'exec', 'config-get'
|
||||
type: string, // one of: 'open', 'close', 'exec', 'export', 'config-get'
|
||||
|
||||
messageId: OPTIONAL arbitrary value. The worker will copy it as-is
|
||||
into response messages to assist in client-side dispatching.
|
||||
@@ -325,6 +325,37 @@
|
||||
passed only a string), noting that options.resultRows and
|
||||
options.columnNames may be populated by the call to db.exec().
|
||||
|
||||
|
||||
====================================================================
|
||||
"export" the current db
|
||||
|
||||
To export the underlying database as a byte array...
|
||||
|
||||
Message format:
|
||||
|
||||
```
|
||||
{
|
||||
type: "export",
|
||||
messageId: ...as above...,
|
||||
dbId: ...as above...
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```
|
||||
{
|
||||
type: "export",
|
||||
messageId: ...as above...,
|
||||
dbId: ...as above...
|
||||
result: {
|
||||
byteArray: Uint8Array (as per sqlite3_js_db_export()),
|
||||
filename: the db filename,
|
||||
mimetype: "application/x-sqlite3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*/
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
sqlite3.initWorker1API = function(){
|
||||
|
@@ -2644,13 +2644,17 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
T.assert( 0 === rc /*void pointer*/ );
|
||||
|
||||
// Commit hook...
|
||||
T.assert( 0!=capi.sqlite3_get_autocommit(db) );
|
||||
db.exec("BEGIN; SELECT 1; COMMIT");
|
||||
T.assert(0 === countCommit,
|
||||
"No-op transactions (mostly) do not trigger commit hook.");
|
||||
db.exec("BEGIN EXCLUSIVE; SELECT 1; COMMIT");
|
||||
T.assert(1 === countCommit,
|
||||
"But EXCLUSIVE transactions do.");
|
||||
db.transaction((d)=>{d.exec("create table t(a)");});
|
||||
db.transaction((d)=>{
|
||||
T.assert( 0==capi.sqlite3_get_autocommit(db) );
|
||||
d.exec("create table t(a)");
|
||||
});
|
||||
T.assert(2 === countCommit);
|
||||
|
||||
// Rollback hook:
|
||||
|
78
manifest
78
manifest
@@ -1,5 +1,5 @@
|
||||
C Bring\sthe\sjsonb\sbranch\sup-to-date\swith\sversion\s3.44.0\schanges.
|
||||
D 2023-11-01T14:12:07.551
|
||||
C Merge\sall\sthe\slatest\strunk\sfixes\sand\senhancements\sinto\sthe\sjsonb\sbranch.
|
||||
D 2023-11-03T11:35:33.434
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -7,7 +7,7 @@ F Makefile.in 8b59912fc1538f96a08555605c5886cdcc733696ae7f22e374b2a4752196ca20
|
||||
F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
|
||||
F Makefile.msc f0cf219350d9af4fba411b4f6306dce2adc897484e8f446de1fb4f40de674d00
|
||||
F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8
|
||||
F VERSION 4c09b629c03b8ae32317cb336a32f3aa3252841d6dcd51184cecc4278d08f21e
|
||||
F VERSION 73573d4545343f001bf5dc5461173a7c78c203dd046cabcf99153878cf25d3a6
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
|
||||
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
@@ -22,7 +22,7 @@ F autoconf/configure.ac ec7fa914c5e74ff212fe879f9bb6918e1234497e05facfb641f30c4d
|
||||
F autoconf/tea/Makefile.in 106a96f2f745d41a0f6193f1de98d7355830b65d45032c18cd7c90295ec24196
|
||||
F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
|
||||
F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
|
||||
F autoconf/tea/configure.ac 1ee185f2f8c2e9bed69d0113222724202f962d04eafd57197e65c662d2d9b180
|
||||
F autoconf/tea/configure.ac 4c32b08691a5b296206b38422b53b92b65be3d3f6b3dd6552a50981a61f5acda
|
||||
F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb
|
||||
F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
|
||||
F autoconf/tea/pkgIndex.tcl.in b9eb6dd37f64e08e637d576b3c83259814b9cddd78bec4af2e5abfc6c5c750ce
|
||||
@@ -33,11 +33,11 @@ F autoconf/tea/win/nmakehlp.c b01f822eabbe1ed2b64e70882d97d48402b42d2689a1ea0034
|
||||
F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
|
||||
F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6
|
||||
F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559
|
||||
F configure 1d9cbcb416cb5387b3f750ae6db63cfedb703a2e46bf8ca61daecff31d208252 x
|
||||
F configure.ac de31fea7d975bb7ebafbe0e2190a855cc80d48558bf0c9a6578a1836daf1cd3a
|
||||
F configure bcb1042e92775424a1021d2f4c89c78a699a6225df01fa8c593df7df0be6ad10 x
|
||||
F configure.ac f25bd7843120f2c2b8bc9db5a92b0502bbdd28e68907415c3d42fc8e57c657b9
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
|
||||
F doc/compile-for-windows.md 922ba580d210a1f1bd3ef9d0413121556f9b5714fb5c01e664d980f85fa4ac8c
|
||||
F doc/compile-for-windows.md 50b27d77be96195c66031a3181cb8684ed822327ea834e07f9c014213e5e3bcf
|
||||
F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f
|
||||
F doc/lemon.html 44a53a1d2b42d7751f7b2f478efb23c978e258d794bfd172442307a755b9fa44
|
||||
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
|
||||
@@ -100,7 +100,7 @@ F ext/fts5/fts5_storage.c 5d10b9bdcce5b90656cad13c7d12ad4148677d4b9e3fca0481fca5
|
||||
F ext/fts5/fts5_tcl.c b1445cbe69908c411df8084a10b2485500ac70a9c747cdc8cda175a3da59d8ae
|
||||
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
|
||||
F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b
|
||||
F ext/fts5/fts5_tokenize.c 5e251efb0f1af99a25ed50010ba6b1ad1250aca5921af1988fdcabe5ebc3cb43
|
||||
F ext/fts5/fts5_tokenize.c 83cfcede3898001cab84432a36ce1503e3080cf9b1c682b022ec82e267ea4c13
|
||||
F ext/fts5/fts5_unicode2.c eca63dbc797f8ff0572e97caf4631389c0ab900d6364861b915bdd4735973f00
|
||||
F ext/fts5/fts5_varint.c e64d2113f6e1bfee0032972cffc1207b77af63319746951bf1d09885d1dadf80
|
||||
F ext/fts5/fts5_vocab.c aed56169ae5c1aa9b8189c779ffeef04ed516d3c712c06914e6d91a6759f4e4a
|
||||
@@ -217,6 +217,7 @@ F ext/fts5/test/fts5tok1.test 1f7817499f5971450d8c4a652114b3d833393c8134e32422d0
|
||||
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
|
||||
F ext/fts5/test/fts5tokenizer.test ac3c9112b263a639fb0508ae73a3ee886bf4866d2153771a8e8a20c721305a43
|
||||
F ext/fts5/test/fts5trigram.test 6c4e37864f3e7d90673db5563d9736d7e40080ab94d10ebdffa94c1b77941da0
|
||||
F ext/fts5/test/fts5trigram2.test 9fe4207f8a4241747aff1005258b564958588d21bfd240d6cd4c2e955d31c156
|
||||
F ext/fts5/test/fts5ubsan.test 783d5a8d13ebfa169e634940228db54540780e3ba7a87ad1e4510e61440bf64b
|
||||
F ext/fts5/test/fts5umlaut.test a42fe2fe6387c40c49ab27ccbd070e1ae38e07f38d05926482cc0bccac9ad602
|
||||
F ext/fts5/test/fts5unicode.test 17056f4efe6b0a5d4f41fdf7a7dc9af2873004562eaa899d40633b93dc95f5a9
|
||||
@@ -236,20 +237,20 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
|
||||
F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
|
||||
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
|
||||
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
|
||||
F ext/jni/GNUmakefile 5c3ac326bf3853486ebe0d70819abc790cc65c412182ce4ebd5012b008d9b059
|
||||
F ext/jni/GNUmakefile 36919b7c4fb8447da4330df9996c7b064b766957f8b7be214a30eab55a8b8072
|
||||
F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4
|
||||
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
|
||||
F ext/jni/src/c/sqlite3-jni.c 6f6df9657989e9ca2cfdcc2fe9a71c279de56d5c941adfd09a0f24256de35c8f
|
||||
F ext/jni/src/c/sqlite3-jni.h b4c413a0d0c734683da1049cfcf89e35ae2719759d0656ec0f8c57188f18cab8
|
||||
F ext/jni/src/c/sqlite3-jni.c afe9c25b82279a28fe2c81f869070fa0d434b0a8ccd7f8aca0e8173db410d14a
|
||||
F ext/jni/src/c/sqlite3-jni.h 1c45fd4689cec42f3d84d2fee41bb494016a12fcb5fd80291095590666a14015
|
||||
F ext/jni/src/org/sqlite/jni/annotation/NotNull.java a99341e88154e70447596b1af6a27c586317df41a7e0f246fd41370cd7b723b2
|
||||
F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba
|
||||
F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca
|
||||
F ext/jni/src/org/sqlite/jni/capi/AbstractCollationCallback.java 1afa90d3f236f79cc7fcd2497e111992644f7596fbc8e8bcf7f1908ae00acd6c
|
||||
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java bc29e986c866c2ddbbb9f935f5b7264c1c1026864e50a4a735192864f75e37c0
|
||||
F ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java 0b72cdff61533b564d65b63418129656daa9a9f30e7e7be982bd5ab394b1dbd0
|
||||
F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java 7ed409d5449684616cc924534e22ff6b07d361f12ad904b69ecb10e0568a8013
|
||||
F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759
|
||||
F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca
|
||||
F ext/jni/src/org/sqlite/jni/capi/CApi.java bccb442ca81cd4decb1adae99006a60b7a9f54e5153842e738c01104e97d1de0
|
||||
F ext/jni/src/org/sqlite/jni/capi/CApi.java 24aba7b14b11da52cd47083608d37c186122c2e2072e75b2ff923d1f15bfb9e5
|
||||
F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 0bfd6e56e8265c2f05c9207665707285534d78f8466ef0e0430c65677f00943d
|
||||
F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a
|
||||
F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java f81cf10b79c52f9b2e9247d523d29ae48863935f60420eae35f257c38c80ce95
|
||||
@@ -263,11 +264,11 @@ F ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java 819d938e26208adde17
|
||||
F ext/jni/src/org/sqlite/jni/capi/ProgressHandlerCallback.java 01bc0c238eed2d5f93c73522cb7849a445cc9098c2ed1e78248fa20ed1cfde5b
|
||||
F ext/jni/src/org/sqlite/jni/capi/ResultCode.java 8141171f1bcf9f46eef303b9d3c5dc2537a25ad1628f3638398d8a60cacefa7f
|
||||
F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java 105e324d09c207100485e7667ad172e64322c62426bb49b547e9b0dc9c33f5f0
|
||||
F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java fef556adbc3624292423083a648bdf97fa8a4f6b3b6577c9660dd7bd6a6d3c4a
|
||||
F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385155fa3b8011a5cca0bb3c28468c7131c1a5
|
||||
F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1
|
||||
F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615
|
||||
F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f
|
||||
F ext/jni/src/org/sqlite/jni/capi/Tester1.java ca195521b6bda3e0cd00e76bb71ec8060d1fab76a2f13b1af9feea40789f44bb
|
||||
F ext/jni/src/org/sqlite/jni/capi/Tester1.java b6b2f3354ba68956a6bcd1c586b8eb25a0bd66eed2b58b340405e1129da15de9
|
||||
F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723
|
||||
F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4
|
||||
F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 9f9e151f1da017b706c0ee5f40f4c86b54e773d6ae4339723e0cc85a456251ab
|
||||
@@ -291,13 +292,14 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_api.java a8e88c3783d21cec51b0748568a96653
|
||||
F ext/jni/src/org/sqlite/jni/fts5/fts5_extension_function.java 9e2b954d210d572552b28aca523b272fae14bd41e318921b22f65b728d5bf978
|
||||
F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ade23d329843f809cd0d0f4f1a2856da6e6b4d90
|
||||
F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java 5ad99bd74c85f56bbef324d9ec29b4048f4620547c9a80093d8586c3557f9f9a
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 004394eeb944baa56e36cd7ae69ba6d4a52b52db3c49439db16e98270b861421
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java a9ddc6a9e8c113168cc67592ae24c0e56d30dd06226eeab012f2761a0889d7bb
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java c24b510ebe801c30533cc62efdf69a4a5e2da9ec4b49f8d403f2060693f060a0
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 9be20f3ba6edbe63285224d5d640dbfb0d84fb8cf5134084e3ae40004ec9d6d9
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java fae71b7454fa3d9243ad26c80e55b590c044a0f0a23d18cae21f0cfa3a92a969
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 13008f8d3c34c1dd55c3afe6dd18dcf94316874cde893ab0661a973fc51a87a4
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af
|
||||
F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4
|
||||
F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745
|
||||
F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70
|
||||
F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0
|
||||
@@ -569,7 +571,7 @@ F ext/wasm/SQLTester/SQLTester.mjs ec2f6ba63a0f2f0562941a0fb8e46b7dc55589711513f
|
||||
F ext/wasm/SQLTester/SQLTester.run.mjs c72b7fe2072d05992f7a3d8c6a1d34e95712513ceabe40849784e24e41c84638
|
||||
F ext/wasm/SQLTester/index.html 3f8a016df0776be76605abf20e815ecaafbe055abac0e1fe5ea080e7846b760d
|
||||
F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api f442460ed9a109e637dd3ea1caa4489553ad9414e8988118b208bb7a4bbece6b
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
|
||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||
F ext/wasm/api/README.md 5eb44fa02e9c693a1884a3692428647894b0380b24bca120866b7a24c8786134
|
||||
@@ -579,10 +581,10 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08
|
||||
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
|
||||
F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e
|
||||
F ext/wasm/api/sqlite3-api-glue.js 26aedfb27915f4f316f6eac84078443e4f0d2dfe5f012310014923ed4b77b2b6
|
||||
F ext/wasm/api/sqlite3-api-glue.js 119b91c8a7ce6648679eb66fcdd1ed07ef7fd892eb501d658fbfefcc962012d9
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 9aeba7b45cf41b3a26d34d7fb2525633cd1adfc544888c1ea8dbb077496f4ce9
|
||||
F ext/wasm/api/sqlite3-api-worker1.js f941382f21006b4a817754184e2661b0a63ce650201f3419cd60f4758b6fd60e
|
||||
F ext/wasm/api/sqlite3-api-worker1.js 88770ac01fc756f89a3e060eec17111d6c1688e89ebfd34cb9d9e54d25affbb9
|
||||
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
|
||||
F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25
|
||||
@@ -633,7 +635,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
||||
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
|
||||
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
|
||||
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
|
||||
F ext/wasm/tester1.c-pp.js fb20d9e1c308ea34a29d8afdda1a6c5edc406241b5560fa23c19c14747ee41bc
|
||||
F ext/wasm/tester1.c-pp.js d628826e936bd143d64e0fa3089752abeeeea38a34a7e2b18d364f090d4e99c6
|
||||
F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1
|
||||
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
||||
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
||||
@@ -719,14 +721,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
|
||||
F src/prepare.c bde74add20fc0e8ce0c4e937a1f70a36d17413afe4f71d3e103f5cb74b17c8d9
|
||||
F src/printf.c 9da63b9ae1c14789bcae12840f5d800fd9302500cd2d62733fac77f0041b4750
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c 31229276a8eb5b5de1428cd2d80f6f1cf8ffc5248be25e47cf575df12f1b8f23
|
||||
F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c a19daa26e95f7245106a31f288b2f50c72d1f2cc156703f04c8c91450e111515
|
||||
F src/shell.c.in 3e9371ca6a93294931a8ed8b098bc3cb15d57567aa9a1f2ade72db7f5c572795
|
||||
F src/select.c 8ebbc67478e2d43f0b24c79258597646e526c5001d07801bba7d4059088c8b12
|
||||
F src/shell.c.in aebfbedaa7706d2c73ab7366a19fc8bc40ea68b70d2529f7feef54e6eb077ea2
|
||||
F src/sqlite.h.in ef0e41e83ad1ac0dcc9ec9939bf541a44b1c5de821bee2d6c61754c3252f3276
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
|
||||
F src/sqliteInt.h 567e317f8631883897b7d3da43fce778b7c30dd0dd7f714558c9725fc1c1196c
|
||||
F src/sqliteInt.h 707095a0591e02f4866ed9798cbdd97a8ae8cf4d98f061808944c2cd1c95d1a9
|
||||
F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6
|
||||
F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -794,7 +796,7 @@ F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
|
||||
F src/vdbe.c 4c76111311e06e74d2519f9cfee5caaaee722bf04559e16f4758a18a0c723b6c
|
||||
F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0
|
||||
F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
|
||||
F src/vdbeapi.c 1cb879087bb33db50e524c7c9385ca8f330384136a2880b0c15e39b2ac4b520f
|
||||
F src/vdbeapi.c 22a2661a2886f6b142fce91e95533a1841135e8217f65297d7df353a0eddf650
|
||||
F src/vdbeaux.c dffcf79e7e415fcd6e4c8ac1ec7124cae5257018443adf09551c807655b04993
|
||||
F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5
|
||||
F src/vdbemem.c c936e9002af4993b84c4eb7133d6b1190efe46d391cc86117ecd67ba17b1a04b
|
||||
@@ -816,7 +818,7 @@ F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867d
|
||||
F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggfault.test 777f269d0da5b0c2524c7ff6d99ae9a93db4f1b1839a914dd2a12e3035c29829
|
||||
F test/aggnested.test 2e738bfe2980df301a782f6e7bbf9459266f64f7e72f58f3b5c843bf897c568c
|
||||
F test/aggnested.test 7929208e173f5dbdbe8f67afbc59c07db99199d39ba5ce2d8a16be2c63600f53
|
||||
F test/aggorderby.test e6b98dbbf3ababa96892435d387de2dcf602ef02c2b848d2d817473066f154ba
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test 2ecb8bbd52416642e41c9081182a8df05d42c75637afd4488aace78cc4b69e13
|
||||
@@ -1648,7 +1650,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163
|
||||
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
|
||||
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
|
||||
F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede
|
||||
F test/testrunner.tcl 14c8b8ece841b1dd17516a0dc9c7ad9b5f4d4db7987974d3fdf66ae56b2a71fa
|
||||
F test/testrunner.tcl 8a6721213bce1cfd3b33e1588cc6431143d96b98819206bf91f5a205fbb150d4
|
||||
F test/testrunner_data.tcl 7f73f93634d32dafc857ed491b840f371113d09fde6a8bfb9e47b938d47b8c85
|
||||
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
|
||||
F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502
|
||||
@@ -1983,7 +1985,7 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2
|
||||
F test/win32lock.test e0924eb8daac02bf80e9da88930747bd44dd9b230b7759fed927b1655b467c9c
|
||||
F test/win32longpath.test 4baffc3acb2e5188a5e3a895b2b543ed09e62f7c72d713c1feebf76222fe9976
|
||||
F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
|
||||
F test/window1.test ccfeaf116afc6a8f748a8122a4f1ee6b69e6bbc5acee61197d3c17167338b100
|
||||
F test/window1.test 5e8abe56a7d667eeddbba6de180086dcf69ed528d046447a25464f945ece101f
|
||||
F test/window2.tcl 492c125fa550cda1dd3555768a2303b3effbeceee215293adf8871efc25f1476
|
||||
F test/window2.test e466a88bd626d66edc3d352d7d7e1d5531e0079b549ba44efb029d1fbff9fd3c
|
||||
F test/window3.tcl acea6e86a4324a210fd608d06741010ca83ded9fde438341cb978c49928faf03
|
||||
@@ -2060,13 +2062,13 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
|
||||
F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
|
||||
F tool/mkautoconfamal.sh cbdcf993fa83dccbef7fb77b39cdeb31ef9f77d9d88c9e343b58d35ca3898a6a
|
||||
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
|
||||
F tool/mkctimec.tcl 372452e24267dfe1b496eec3992d10c6e5e7d7870a152560cdcfe5404bc8cc04 x
|
||||
F tool/mkctimec.tcl a16682eae5f01f85e5861b2aa215ca0d46b4230658ee25977e02b4508566fb75 x
|
||||
F tool/mkkeywordhash.c b9faa0ae7e14e4dbbcd951cddd786bf46b8a65bb07b129ba8c0cfade723aaffd
|
||||
F tool/mkmsvcmin.tcl 8897d515ef7f94772322db95a3b6fce6c614d84fe0bdd06ba5a1c786351d5a1d
|
||||
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
|
||||
F tool/mkopcodeh.tcl 769d9e6a8b462323150dc13a8539d6064664b72974f7894befe2491cc73e05cd
|
||||
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
|
||||
F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d
|
||||
F tool/mkpragmatab.tcl 32e359ccb21011958a821955254bd7a5fa7915d01a8c16fed91ffc8b40cb4adf
|
||||
F tool/mkshellc.tcl b7adf08b82de60811d2cb6af05ff59fc17e5cd6f3e98743c14eaaa3f8971fed0
|
||||
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
@@ -2107,7 +2109,7 @@ F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaa
|
||||
F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
|
||||
F tool/src-verify.c 41c586dee84d0b190ad13e0282ed83d4a65ec9fefde9adf4943efdf6558eea7f
|
||||
F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f
|
||||
F tool/srctree-check.tcl cef630bc4ff21a460d72479c43a42bf1c1ed61897659305c35c8d72e91bcb176
|
||||
F tool/srctree-check.tcl 9f1098d439159692ece45cc9e701b35df4956269399fe0c770e41f235d1ce5e6
|
||||
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
|
||||
F tool/stripccomments.c 20b8aabc4694d0d4af5566e42da1f1a03aff057689370326e9269a9ddcffdc37
|
||||
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
|
||||
@@ -2140,8 +2142,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 f47a8d0a207a8442a7f621b070ce9dd1d6013ce26bcf68165d20bb379bd478a0 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301
|
||||
R 0b642bb03e1bb73ba395a556a197e4e8
|
||||
P a420a4f7ff76b0e9cf5f6d515ccfa31e526d58f4001a4015a367e2aa3c82091f 15b618e92a2708cc83256947736de8c494a9985a77e38bd5efc7e51e72cba344
|
||||
R 7bce1ab3b73fc9ca8b3b94f2470fc2d6
|
||||
U drh
|
||||
Z 3c39b7a06f36c21b3f7d5adacbf5ab4c
|
||||
Z 13124fe09b7592eb801d594e79ae949a
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@@ -1 +1 @@
|
||||
a420a4f7ff76b0e9cf5f6d515ccfa31e526d58f4001a4015a367e2aa3c82091f
|
||||
b089bf46374b374d02d7654c65eb3e75d1777638b398061e47644af0dab48c9b
|
@@ -1249,11 +1249,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
while( pNC2
|
||||
&& sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
|
||||
){
|
||||
pExpr->op2++;
|
||||
pExpr->op2 += (1 + pNC2->nNestedSelect);
|
||||
pNC2 = pNC2->pNext;
|
||||
}
|
||||
assert( pDef!=0 || IN_RENAME_OBJECT );
|
||||
if( pNC2 && pDef ){
|
||||
pExpr->op2 += pNC2->nNestedSelect;
|
||||
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
|
||||
assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
|
||||
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
|
||||
@@ -1812,6 +1813,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
|
||||
/* Recursively resolve names in all subqueries in the FROM clause
|
||||
*/
|
||||
if( pOuterNC ) pOuterNC->nNestedSelect++;
|
||||
for(i=0; i<p->pSrc->nSrc; i++){
|
||||
SrcItem *pItem = &p->pSrc->a[i];
|
||||
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
|
||||
@@ -1836,6 +1838,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){
|
||||
pOuterNC->nNestedSelect--;
|
||||
}
|
||||
|
||||
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
|
||||
** resolve the result-set expression list.
|
||||
|
@@ -8472,7 +8472,7 @@ int sqlite3Select(
|
||||
updateAccumulator(pParse, regAcc, pAggInfo, eDist);
|
||||
if( eDist!=WHERE_DISTINCT_NOOP ){
|
||||
struct AggInfo_func *pF = pAggInfo->aFunc;
|
||||
if( pF ){
|
||||
if( ALWAYS(pF) ){
|
||||
fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr);
|
||||
}
|
||||
}
|
||||
|
@@ -704,9 +704,8 @@ static void console_prepare_utf8(void){
|
||||
conState.outCodePage = GetConsoleOutputCP();
|
||||
if( in_console ){
|
||||
SetConsoleCP(CP_UTF8);
|
||||
DWORD newConsoleMode = conState.consoleMode
|
||||
| ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
||||
SetConsoleMode(conState.hConsole, newConsoleMode);
|
||||
SetConsoleMode(conState.hConsole, conState.consoleMode
|
||||
| ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
||||
conState.infsMode = _setmode(_fileno(stdin), _O_U16TEXT);
|
||||
console_utf8_in = 1;
|
||||
}
|
||||
|
@@ -3397,6 +3397,7 @@ struct NameContext {
|
||||
int nRef; /* Number of names resolved by this context */
|
||||
int nNcErr; /* Number of errors encountered while resolving names */
|
||||
int ncFlags; /* Zero or more NC_* flags defined below */
|
||||
u32 nNestedSelect; /* Number of nested selects using this NC */
|
||||
Select *pWinSelect; /* SELECT statement for any window functions */
|
||||
};
|
||||
|
||||
|
@@ -152,7 +152,15 @@ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
|
||||
int rc = SQLITE_OK;
|
||||
Vdbe *p = (Vdbe*)pStmt;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;
|
||||
sqlite3_mutex *mutex;
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( pStmt==0 ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
#if SQLITE_THREADSAFE
|
||||
mutex = p->db->mutex;
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
for(i=0; i<p->nVar; i++){
|
||||
|
@@ -358,6 +358,60 @@ do_execsql_test 6.2.2 {
|
||||
FROM t2 GROUP BY 'constant_string';
|
||||
} {{}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE invoice (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
amount DOUBLE PRECISION DEFAULT NULL,
|
||||
name VARCHAR(100) DEFAULT NULL
|
||||
);
|
||||
|
||||
INSERT INTO invoice (amount, name) VALUES
|
||||
(4.0, 'Michael'), (15.0, 'Bara'), (4.0, 'Michael'), (6.0, 'John');
|
||||
}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT sum(amount), name
|
||||
from invoice
|
||||
group by name
|
||||
having (select v > 6 from (select sum(amount) v) t)
|
||||
} {
|
||||
15.0 Bara
|
||||
8.0 Michael
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
SELECT (select 1 from (select sum(amount))) FROM invoice
|
||||
} {1}
|
||||
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE t1(x INT);
|
||||
INSERT INTO t1 VALUES(100);
|
||||
INSERT INTO t1 VALUES(20);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
SELECT (SELECT y FROM (SELECT sum(x) AS y) AS t2 ) FROM t1;
|
||||
} {123}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
SELECT (
|
||||
SELECT y FROM (
|
||||
SELECT z AS y FROM (SELECT sum(x) AS z) AS t2
|
||||
)
|
||||
) FROM t1;
|
||||
} {123}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
SELECT (
|
||||
SELECT a FROM (
|
||||
SELECT y AS a FROM (
|
||||
SELECT z AS y FROM (SELECT sum(x) AS z) AS t2
|
||||
)
|
||||
)
|
||||
) FROM t1;
|
||||
} {123}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -425,7 +425,7 @@ for {set ii 0} {$ii < [llength $argv]} {incr ii} {
|
||||
if {$isLast} { usage }
|
||||
} elseif {($n>2 && [string match "$a*" --zipvfs]) || $a=="-z"} {
|
||||
incr ii
|
||||
set TRG(zipvfs) [lindex $argv $ii]
|
||||
set TRG(zipvfs) [file normalize [lindex $argv $ii]]
|
||||
if {$isLast} { usage }
|
||||
} else {
|
||||
usage
|
||||
|
@@ -1881,7 +1881,7 @@ do_catchsql_test 57.3 {
|
||||
SELECT max(y) OVER( ORDER BY (SELECT x FROM (SELECT sum(y) AS x FROM t1)))
|
||||
)
|
||||
FROM t3;
|
||||
} {1 {misuse of aggregate: sum()}}
|
||||
} {0 5}
|
||||
|
||||
# 2020-06-06 ticket 1f6f353b684fc708
|
||||
reset_db
|
||||
|
@@ -7,6 +7,13 @@
|
||||
# definition used in src/ctime.c, run this script from
|
||||
# the checkout root. It generates src/ctime.c .
|
||||
#
|
||||
# Results are normally written into src/ctime.c. But if an argument is
|
||||
# provided, results are written there instead. Examples:
|
||||
#
|
||||
# tclsh tool/mkctimec.tcl ;# <-- results to src/ctime.c
|
||||
#
|
||||
# tclsh tool/mkctimec.tcl /dev/tty ;# <-- results to the terminal
|
||||
#
|
||||
|
||||
|
||||
set ::headWarning {/* DO NOT EDIT!
|
||||
@@ -429,10 +436,15 @@ foreach v $value2_options {
|
||||
}]
|
||||
}
|
||||
|
||||
set ctime_c "src/ctime.c"
|
||||
if {$argc>0} {
|
||||
set destfile [lindex $argv 0]
|
||||
} else {
|
||||
set destfile "[file dir [file dir [file normal $argv0]]]/src/ctime.c"
|
||||
puts "Overwriting $destfile..."
|
||||
}
|
||||
|
||||
if {[catch {set cfd [open $ctime_c w]}]!=0} {
|
||||
puts stderr "File '$ctime_c' unwritable."
|
||||
if {[catch {set cfd [open $destfile w]}]!=0} {
|
||||
puts stderr "File '$destfile' unwritable."
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,14 @@
|
||||
# Then add the extra "case PragTyp_XXXXX:" and subsequent code for the
|
||||
# new pragma in ../src/pragma.c.
|
||||
#
|
||||
# The results are normally written into the ../src/pragma.h file. However,
|
||||
# if an alternative output file name is provided as an argument, then
|
||||
# results are written into the alternative. For example:
|
||||
#
|
||||
# tclsh tool/mkpragmatab.tcl ;# <--- Results to src/pragma.h
|
||||
#
|
||||
# tclsh tool/mkpragmatab.tcl /dev/tty ;# <-- results to terminal
|
||||
#
|
||||
|
||||
# Flag meanings:
|
||||
set flagMeaning(NeedSchema) {Force schema load before running}
|
||||
@@ -402,8 +410,12 @@ set pragma_def {
|
||||
|
||||
# Open the output file
|
||||
#
|
||||
set destfile "[file dir [file dir [file normal $argv0]]]/src/pragma.h"
|
||||
puts "Overwriting $destfile with new pragma table..."
|
||||
if {$argc>0} {
|
||||
set destfile [lindex $argv 0]
|
||||
} else {
|
||||
set destfile "[file dir [file dir [file normal $argv0]]]/src/pragma.h"
|
||||
puts "Overwriting $destfile with new pragma table..."
|
||||
}
|
||||
set fd [open $destfile wb]
|
||||
puts $fd {/* DO NOT EDIT!
|
||||
** This file is automatically generated by the script at
|
||||
|
@@ -27,12 +27,15 @@ proc readfile {filename} {
|
||||
# Find the root of the tree.
|
||||
#
|
||||
set ROOT [file dir [file dir [file normalize $argv0]]]
|
||||
cd $ROOT
|
||||
|
||||
# Name of the TCL interpreter
|
||||
#
|
||||
set TCLSH [info nameofexe]
|
||||
|
||||
# Number of errors seen.
|
||||
#
|
||||
set NERR 0
|
||||
|
||||
######################### autoconf/tea/configure.ac ###########################
|
||||
|
||||
set confac [readfile $ROOT/autoconf/tea/configure.ac]
|
||||
@@ -42,38 +45,46 @@ append pattern [string trim $vers]
|
||||
append pattern {])}
|
||||
if {[string first $pattern $confac]<=0} {
|
||||
puts "ERROR: ./autoconf/tea/configure.ac does not agree with ./VERSION"
|
||||
exit 1
|
||||
puts "...... Fix: manually edit ./autoconf/tea/configure.ac to"
|
||||
incr NERR
|
||||
}
|
||||
|
||||
######################### autoconf/Makefile.msc ###############################
|
||||
|
||||
set f1 [readfile $ROOT/autoconf/Makefile.msc]
|
||||
exec mv $ROOT/autoconf/Makefile.msc $ROOT/autoconf/Makefile.msc.tmp
|
||||
exec $TCLSH $ROOT/tool/mkmsvcmin.tcl
|
||||
set f2 [readfile $ROOT/autoconf/Makefile.msc]
|
||||
exec mv $ROOT/autoconf/Makefile.msc.tmp $ROOT/autoconf/Makefile.msc
|
||||
exec $TCLSH $ROOT/tool/mkmsvcmin.tcl $ROOT/Makefile.msc tmp1.txt
|
||||
set f2 [readfile tmp1.txt]
|
||||
file delete tmp1.txt
|
||||
if {$f1 != $f2} {
|
||||
puts "ERROR: ./autoconf/Makefile.msc does not agree with ./Makefile.msc"
|
||||
puts "...... Fix: tclsh tool/mkmsvcmin.tcl"
|
||||
incr NERR
|
||||
}
|
||||
|
||||
######################### src/pragma.h ########################################
|
||||
|
||||
set f1 [readfile $ROOT/src/pragma.h]
|
||||
exec mv $ROOT/src/pragma.h $ROOT/src/pragma.h.tmp
|
||||
exec $TCLSH $ROOT/tool/mkpragmatab.tcl
|
||||
set f2 [readfile $ROOT/src/pragma.h]
|
||||
exec mv $ROOT/src/pragma.h.tmp $ROOT/src/pragma.h
|
||||
exec $TCLSH $ROOT/tool/mkpragmatab.tcl tmp2.txt
|
||||
set f2 [readfile tmp2.txt]
|
||||
file delete tmp2.txt
|
||||
if {$f1 != $f2} {
|
||||
puts "ERROR: ./src/pragma.h does not agree with ./tool/mkpragmatab.tcl"
|
||||
puts "...... Fix: tclsh tool/mkpragmatab.tcl"
|
||||
incr NERR
|
||||
}
|
||||
|
||||
######################### src/ctime.c ########################################
|
||||
|
||||
set f1 [readfile $ROOT/src/ctime.c]
|
||||
exec mv $ROOT/src/ctime.c $ROOT/src/ctime.c.tmp
|
||||
exec $TCLSH $ROOT/tool/mkctimec.tcl
|
||||
set f2 [readfile $ROOT/src/ctime.c]
|
||||
exec mv $ROOT/src/ctime.c.tmp $ROOT/src/ctime.c
|
||||
exec $TCLSH $ROOT/tool/mkctimec.tcl tmp3.txt
|
||||
set f2 [readfile tmp3.txt]
|
||||
file delete tmp3.txt
|
||||
if {$f1 != $f2} {
|
||||
puts "ERROR: ./src/ctime.c does not agree with ./tool/mkctimec.tcl"
|
||||
puts "..... Fix: tclsh tool/mkctimec.tcl"
|
||||
incr NERR
|
||||
}
|
||||
|
||||
# If any errors are seen, exit 1 so that the build will fail.
|
||||
#
|
||||
if {$NERR>0} {exit 1}
|
||||
|
Reference in New Issue
Block a user