1
0
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:
drh
2023-11-03 11:35:33 +00:00
37 changed files with 1475 additions and 455 deletions

View File

@@ -1 +1 @@
3.44.0
3.45.0

View File

@@ -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
View File

@@ -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\\"

View File

@@ -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'`

View File

@@ -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

View File

@@ -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;
/* 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 ) break;
zNext = zIn;
if( zIn<zEof ){
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
if( iCode==0 ) return SQLITE_OK;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
WRITE_UTF8(zOut, iCode);
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);
/* 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;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
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);
}else{
break;
}
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
if( rc!=SQLITE_OK ) break;
zIn = zNext;
/* Update the aStart[] array */
aStart[0] = aStart[1];
aStart[1] = aStart[2];
aStart[2] = iNext;
}
return rc;
@@ -1380,8 +1421,10 @@ int sqlite3Fts5TokenizerPattern(
){
if( xCreate==fts5TriCreate ){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
if( p->iFoldParam==0 ){
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
}
}
return FTS5_PATTERN_NONE;
}

View 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

View File

@@ -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)/%,\

View File

@@ -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,7 +3533,14 @@ S3JniApi(sqlite3_errmsg(),jstring,1errmsg)(
S3JniApi(sqlite3_errstr(),jstring,1errstr)(
JniArgsEnvClass, jint rcCode
){
jstring const rv = (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode))
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 );
@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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();
}
}
}

View File

@@ -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 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 int step(){
return checkRc(sqlite3_step(affirmOpen()));
}
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 byte[] columnBlob(int ndx){
return CApi.sqlite3_column_blob( checkColIndex(ndx), ndx );
}
public Stmt prepare(String sql){
return prepare(sql, 0);
public byte[] columnText(int ndx){
return CApi.sqlite3_column_text( checkColIndex(ndx), ndx );
}
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 String columnText16(int ndx){
return CApi.sqlite3_column_text16( checkColIndex(ndx), ndx );
}
public void createFunction(String name, int nArg, ScalarFunction f){
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
public int columnBytes(int ndx){
return CApi.sqlite3_column_bytes( checkColIndex(ndx), ndx );
}
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 int columnBytes16(int ndx){
return CApi.sqlite3_column_bytes16( checkColIndex(ndx), ndx );
}
public void createFunction(String name, int nArg, AggregateFunction f){
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
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 */
}

View File

@@ -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);
}
/**

View File

@@ -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();
@@ -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,18 +229,49 @@ 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() );
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 {

View 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);
}

View File

@@ -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

View File

@@ -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*"

View File

@@ -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(){

View File

@@ -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:

View File

@@ -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.

View File

@@ -1 +1 @@
a420a4f7ff76b0e9cf5f6d515ccfa31e526d58f4001a4015a367e2aa3c82091f
b089bf46374b374d02d7654c65eb3e75d1777638b398061e47644af0dab48c9b

View File

@@ -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.

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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 */
};

View File

@@ -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++){

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
#
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

View File

@@ -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}