mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Do not pre-allocate sqlite3_aggregate_context() for Java UDFs, as it unduly complicates UDF initialization.
FossilOrigin-Name: e8308f0c6ec2d8999c8a2502fb130cb3501ba326f23f71f2cd8d452debae79b5
This commit is contained in:
@@ -288,7 +288,7 @@ jar: $(package.jar)
|
|||||||
|
|
||||||
dir.doc := $(dir.jni)/doc
|
dir.doc := $(dir.jni)/doc
|
||||||
doc: $(JAVA_FILES.main)
|
doc: $(JAVA_FILES.main)
|
||||||
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) org.sqlite.jni
|
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet org.sqlite.jni
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Clean up...
|
# Clean up...
|
||||||
|
@@ -1164,49 +1164,6 @@ static int S3JniAutoExtension_init(JNIEnv *const env,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** Requires that jCx be a Java-side sqlite3_context wrapper for pCx.
|
|
||||||
** This function calls sqlite3_aggregate_context() to allocate a tiny
|
|
||||||
** sliver of memory, the address of which is set in
|
|
||||||
** jCx->aggregateContext. The memory is only used as a key for
|
|
||||||
** mapping client-side results of aggregate result sets across
|
|
||||||
** calls to the UDF's callbacks.
|
|
||||||
**
|
|
||||||
** isFinal must be 1 for xFinal() calls and 0 for all others, the
|
|
||||||
** difference being that the xFinal() invocation will not allocate
|
|
||||||
** new memory if it was not already, resulting in a value of 0
|
|
||||||
** for jCx->aggregateContext.
|
|
||||||
**
|
|
||||||
** Returns 0 on success. Returns SQLITE_NOMEM on allocation error,
|
|
||||||
** noting that it will not allocate when isFinal is true. It returns
|
|
||||||
** SQLITE_ERROR if there's a serious internal error in dealing with
|
|
||||||
** the JNI state.
|
|
||||||
*/
|
|
||||||
static int udf_setAggregateContext(JNIEnv * env, jobject jCx,
|
|
||||||
sqlite3_context * pCx,
|
|
||||||
int isFinal){
|
|
||||||
void * pAgg;
|
|
||||||
int rc = 0;
|
|
||||||
S3JniNphClass * const pNC =
|
|
||||||
S3JniGlobal_nph_cache(env, &S3NphRefs.sqlite3_context);
|
|
||||||
if( !pNC->fidAggCtx ){
|
|
||||||
S3JniMutex_Nph_enter;
|
|
||||||
if( !pNC->fidAggCtx ){
|
|
||||||
pNC->fidAggCtx = (*env)->GetFieldID(env, pNC->klazz, "aggregateContext", "J");
|
|
||||||
EXCEPTION_IS_FATAL("Cannot get sqlite3_contex.aggregateContext member.");
|
|
||||||
}
|
|
||||||
S3JniMutex_Nph_leave;
|
|
||||||
}
|
|
||||||
pAgg = sqlite3_aggregate_context(pCx, isFinal ? 0 : sizeof(void*));
|
|
||||||
if( pAgg || isFinal ){
|
|
||||||
(*env)->SetLongField(env, jCx, pNC->fidAggCtx, (jlong)pAgg);
|
|
||||||
}else{
|
|
||||||
assert(!pAgg);
|
|
||||||
rc = SQLITE_NOMEM;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Common init for OutputPointer_set_Int32() and friends. pRef must be
|
** Common init for OutputPointer_set_Int32() and friends. pRef must be
|
||||||
** a pointer from S3NphRefs. jOut must be an instance of that
|
** a pointer from S3NphRefs. jOut must be an instance of that
|
||||||
@@ -1628,6 +1585,7 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr,
|
|||||||
(*env)->ExceptionDescribe( env );
|
(*env)->ExceptionDescribe( env );
|
||||||
S3JniExceptionClear;
|
S3JniExceptionClear;
|
||||||
}
|
}
|
||||||
|
UNREF_L(ex);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1645,10 +1603,6 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc,
|
|||||||
int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv);
|
int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv);
|
||||||
|
|
||||||
//MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType));
|
//MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType));
|
||||||
if( rc ) return rc;
|
|
||||||
if( UDF_SCALAR != s->type ){
|
|
||||||
rc = udf_setAggregateContext(env, args.jcx, pCx, 0);
|
|
||||||
}
|
|
||||||
if( 0 == rc ){
|
if( 0 == rc ){
|
||||||
(*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv);
|
(*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv);
|
||||||
S3JniIfThrew{
|
S3JniIfThrew{
|
||||||
@@ -1678,15 +1632,10 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s,
|
|||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
//MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType));
|
//MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType));
|
||||||
if( UDF_SCALAR != s->type ){
|
(*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx);
|
||||||
rc = udf_setAggregateContext(env, jcx, cx, isFinal);
|
S3JniIfThrew{
|
||||||
}
|
rc = udf_report_exception(env, isFinal, cx, s->zFuncName,
|
||||||
if( 0 == rc ){
|
zFuncType);
|
||||||
(*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx);
|
|
||||||
S3JniIfThrew{
|
|
||||||
rc = udf_report_exception(env, isFinal, cx, s->zFuncName,
|
|
||||||
zFuncType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
UNREF_L(jcx);
|
UNREF_L(jcx);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -1834,6 +1783,20 @@ WRAP_INT_SVALUE(1value_1type, sqlite3_value_type)
|
|||||||
#undef WRAP_MUTF8_VOID
|
#undef WRAP_MUTF8_VOID
|
||||||
#undef WRAP_STR_STMT_INT
|
#undef WRAP_STR_STMT_INT
|
||||||
|
|
||||||
|
|
||||||
|
S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)(
|
||||||
|
JniArgsEnvClass, jobject jCx, jboolean initialize
|
||||||
|
){
|
||||||
|
sqlite3_context * const pCx = PtrGet_sqlite3_context(jCx);
|
||||||
|
void * const p = pCx
|
||||||
|
? sqlite3_aggregate_context(pCx, (int)(initialize
|
||||||
|
? (int)sizeof(void*)
|
||||||
|
: 0))
|
||||||
|
: 0;
|
||||||
|
return (jlong)p / sizeof(void*);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Central auto-extension handler. */
|
/* Central auto-extension handler. */
|
||||||
static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
|
static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
|
||||||
const struct sqlite3_api_routines *ignored){
|
const struct sqlite3_api_routines *ignored){
|
||||||
|
@@ -771,6 +771,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_init
|
|||||||
JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv
|
JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv
|
||||||
(JNIEnv *, jclass);
|
(JNIEnv *, jclass);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: org_sqlite_jni_SQLite3Jni
|
||||||
|
* Method: sqlite3_aggregate_context
|
||||||
|
* Signature: (Lorg/sqlite/jni/sqlite3_context;Z)J
|
||||||
|
*/
|
||||||
|
JNIEXPORT jlong JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1aggregate_1context
|
||||||
|
(JNIEnv *, jclass, jobject, jboolean);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: org_sqlite_jni_SQLite3Jni
|
* Class: org_sqlite_jni_SQLite3Jni
|
||||||
* Method: sqlite3_auto_extension
|
* Method: sqlite3_auto_extension
|
||||||
|
@@ -55,6 +55,9 @@ public abstract class SQLFunction {
|
|||||||
Client UDFs are free to perform such mappings using custom
|
Client UDFs are free to perform such mappings using custom
|
||||||
approaches. The provided Aggregate<T> and Window<T> classes
|
approaches. The provided Aggregate<T> and Window<T> classes
|
||||||
use this.
|
use this.
|
||||||
|
|
||||||
|
<p>T must be of a type which can be legally stored as a value in
|
||||||
|
java.util.HashMap<KeyType,T>.
|
||||||
*/
|
*/
|
||||||
public static final class PerContextState<T> {
|
public static final class PerContextState<T> {
|
||||||
private final java.util.Map<Long,ValueHolder<T>> map
|
private final java.util.Map<Long,ValueHolder<T>> map
|
||||||
@@ -64,20 +67,20 @@ public abstract class SQLFunction {
|
|||||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||||
methods, passing it that method's first argument and an initial
|
methods, passing it that method's first argument and an initial
|
||||||
value for the persistent state. If there is currently no
|
value for the persistent state. If there is currently no
|
||||||
mapping for cx.getAggregateContext() within the map, one is
|
mapping for the given context within the map, one is created
|
||||||
created using the given initial value, else the existing one is
|
using the given initial value, else the existing one is used
|
||||||
used and the 2nd argument is ignored. It returns a
|
and the 2nd argument is ignored. It returns a ValueHolder<T>
|
||||||
ValueHolder<T> which can be used to modify that state directly
|
which can be used to modify that state directly without
|
||||||
without requiring that the client update the underlying map's
|
requiring that the client update the underlying map's entry.
|
||||||
entry.
|
|
||||||
|
|
||||||
<p>T must be of a type which can be legally stored as a value in
|
<p>The caller is obligated to eventually call
|
||||||
java.util.HashMap<KeyType,T>.
|
takeAggregateState() to clear the mapping.
|
||||||
*/
|
*/
|
||||||
public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||||
ValueHolder<T> rc = map.get(cx.getAggregateContext());
|
final Long key = cx.getAggregateContext(true);
|
||||||
if(null == rc){
|
ValueHolder<T> rc = null==key ? null : map.get(key);
|
||||||
map.put(cx.getAggregateContext(), rc = new ValueHolder<>(initialValue));
|
if( null==rc ){
|
||||||
|
map.put(key, rc = new ValueHolder<>(initialValue));
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -92,7 +95,7 @@ public abstract class SQLFunction {
|
|||||||
rows.
|
rows.
|
||||||
*/
|
*/
|
||||||
public T takeAggregateState(sqlite3_context cx){
|
public T takeAggregateState(sqlite3_context cx){
|
||||||
final ValueHolder<T> h = map.remove(cx.getAggregateContext());
|
final ValueHolder<T> h = map.remove(cx.getAggregateContext(false));
|
||||||
return null==h ? null : h.value;
|
return null==h ? null : h.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -126,19 +126,19 @@ public final class SQLite3Jni {
|
|||||||
This will clean up any cached per-JNIEnv info. Calling into the
|
This will clean up any cached per-JNIEnv info. Calling into the
|
||||||
library will re-initialize the cache on demand.
|
library will re-initialize the cache on demand.
|
||||||
|
|
||||||
This process does not close any databases or finalize
|
<p>This process does not close any databases or finalize
|
||||||
any prepared statements because their ownership does not depend on
|
any prepared statements because their ownership does not depend on
|
||||||
a given thread. For proper library behavior, and to
|
a given thread. For proper library behavior, and to
|
||||||
avoid C-side leaks, be sure to finalize all statements and close
|
avoid C-side leaks, be sure to finalize all statements and close
|
||||||
all databases before calling this function.
|
all databases before calling this function.
|
||||||
|
|
||||||
Calling this from the main application thread is not strictly
|
<p>Calling this from the main application thread is not strictly
|
||||||
required but is "polite." Additional threads must call this
|
required but is "polite." Additional threads must call this
|
||||||
before ending or they will leak cache entries in the C heap,
|
before ending or they will leak cache entries in the C heap,
|
||||||
which in turn may keep numerous Java-side global references
|
which in turn may keep numerous Java-side global references
|
||||||
active.
|
active.
|
||||||
|
|
||||||
This routine returns false without side effects if the current
|
<p>This routine returns false without side effects if the current
|
||||||
JNIEnv is not cached, else returns true, but this information is
|
JNIEnv is not cached, else returns true, but this information is
|
||||||
primarily for testing of the JNI bindings and is not information
|
primarily for testing of the JNI bindings and is not information
|
||||||
which client-level code should use to make any informed
|
which client-level code should use to make any informed
|
||||||
@@ -151,22 +151,42 @@ public final class SQLite3Jni {
|
|||||||
// alphabetized. The SQLITE_... values. on the other hand, are
|
// alphabetized. The SQLITE_... values. on the other hand, are
|
||||||
// grouped by category.
|
// grouped by category.
|
||||||
|
|
||||||
|
/**
|
||||||
|
Functions exactly like the native form except that (A) the
|
||||||
|
returned value is only intended for use as a lookup key in a
|
||||||
|
higher-level data structure and (B) the 2nd argument is a boolean
|
||||||
|
instead of an int. If passed true, it will attempt to allocate
|
||||||
|
enough memory to use as a UDF-call-local context key. If passed
|
||||||
|
false it will not allocate any memory.
|
||||||
|
|
||||||
|
<p>It is only valid for the life of the current UDF method call
|
||||||
|
and must not be retained for later use. The return value 0
|
||||||
|
indicates an allocation error unless initialize is false, in
|
||||||
|
which case it means that the given context was never passed to
|
||||||
|
this function with a true second argument so never had to
|
||||||
|
allocate.
|
||||||
|
|
||||||
|
<p>For the JNI wrapping, the value of sz is provided for API
|
||||||
|
consistency but it is ignored unless it's 0. Results are
|
||||||
|
undefined if the value is negative.
|
||||||
|
*/
|
||||||
|
public static native long sqlite3_aggregate_context(sqlite3_context cx, boolean initialize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Functions almost as documented for the C API, with these
|
Functions almost as documented for the C API, with these
|
||||||
exceptions:
|
exceptions:
|
||||||
|
|
||||||
- The callback interface is is shorter because of cross-language
|
<p>- The callback interface is is shorter because of
|
||||||
differences. Specifically, 3rd argument to the C auto-extension
|
cross-language differences. Specifically, 3rd argument to the C
|
||||||
callback interface is unnecessary here.
|
auto-extension callback interface is unnecessary here.
|
||||||
|
|
||||||
|
|
||||||
The C API docs do not specifically say so, if the list of
|
<p>The C API docs do not specifically say so, but if the list of
|
||||||
auto-extensions is manipulated from an auto-extension, it is
|
auto-extensions is manipulated from an auto-extension, it is
|
||||||
undefined which, if any, auto-extensions will subsequently
|
undefined which, if any, auto-extensions will subsequently
|
||||||
execute for the current database.
|
execute for the current database.
|
||||||
|
|
||||||
See the AutoExtension class docs for more information.
|
<p>See the AutoExtension class docs for more information.
|
||||||
*/
|
*/
|
||||||
public static native int sqlite3_auto_extension(@NotNull AutoExtension callback);
|
public static native int sqlite3_auto_extension(@NotNull AutoExtension callback);
|
||||||
|
|
||||||
|
@@ -723,7 +723,9 @@ public class Tester1 implements Runnable {
|
|||||||
SQLFunction func = new SQLFunction.Aggregate<Integer>(){
|
SQLFunction func = new SQLFunction.Aggregate<Integer>(){
|
||||||
@Override
|
@Override
|
||||||
public void xStep(sqlite3_context cx, sqlite3_value[] args){
|
public void xStep(sqlite3_context cx, sqlite3_value[] args){
|
||||||
this.getAggregateState(cx, 0).value += sqlite3_value_int(args[0]);
|
final ValueHolder<Integer> agg = this.getAggregateState(cx, 0);
|
||||||
|
agg.value += sqlite3_value_int(args[0]);
|
||||||
|
affirm( agg == this.getAggregateState(cx, 0) );
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void xFinal(sqlite3_context cx){
|
public void xFinal(sqlite3_context cx){
|
||||||
@@ -740,15 +742,19 @@ public class Tester1 implements Runnable {
|
|||||||
int rc = sqlite3_create_function(db, "myfunc", 1, SQLITE_UTF8, func);
|
int rc = sqlite3_create_function(db, "myfunc", 1, SQLITE_UTF8, func);
|
||||||
affirm(0 == rc);
|
affirm(0 == rc);
|
||||||
sqlite3_stmt stmt = prepare(db, "select myfunc(a), myfunc(a+10) from t");
|
sqlite3_stmt stmt = prepare(db, "select myfunc(a), myfunc(a+10) from t");
|
||||||
|
affirm( null != stmt );
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if( SQLITE_ROW == sqlite3_step(stmt) ){
|
if( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||||
final int v = sqlite3_column_int(stmt, 0);
|
int v = sqlite3_column_int(stmt, 0);
|
||||||
affirm( 6 == v );
|
affirm( 6 == v );
|
||||||
|
int v2 = sqlite3_column_int(stmt, 1);
|
||||||
|
affirm( 30+v == v2 );
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
|
affirm( 1==n );
|
||||||
affirm(!xFinalNull.value);
|
affirm(!xFinalNull.value);
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
// Ensure that the accumulator is reset...
|
// Ensure that the accumulator is reset on subsequent calls...
|
||||||
n = 0;
|
n = 0;
|
||||||
if( SQLITE_ROW == sqlite3_step(stmt) ){
|
if( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||||
final int v = sqlite3_column_int(stmt, 0);
|
final int v = sqlite3_column_int(stmt, 0);
|
||||||
@@ -767,9 +773,9 @@ public class Tester1 implements Runnable {
|
|||||||
affirm( 6 == c0 );
|
affirm( 6 == c0 );
|
||||||
affirm( 12 == c1 );
|
affirm( 12 == c1 );
|
||||||
}
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
affirm( 1 == n );
|
affirm( 1 == n );
|
||||||
affirm(!xFinalNull.value);
|
affirm(!xFinalNull.value);
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
execSql(db, "SELECT myfunc(1) WHERE 0");
|
execSql(db, "SELECT myfunc(1) WHERE 0");
|
||||||
affirm(xFinalNull.value);
|
affirm(xFinalNull.value);
|
||||||
|
@@ -18,10 +18,7 @@ package org.sqlite.jni;
|
|||||||
SQL functions (a.k.a. UDFs).
|
SQL functions (a.k.a. UDFs).
|
||||||
*/
|
*/
|
||||||
public final class sqlite3_context extends NativePointerHolder<sqlite3_context> {
|
public final class sqlite3_context extends NativePointerHolder<sqlite3_context> {
|
||||||
/**
|
private Long aggregateContext = null;
|
||||||
Only set by the JNI layer.
|
|
||||||
*/
|
|
||||||
private long aggregateContext = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
getAggregateContext() corresponds to C's
|
getAggregateContext() corresponds to C's
|
||||||
@@ -32,19 +29,29 @@ public final class sqlite3_context extends NativePointerHolder<sqlite3_context>
|
|||||||
such that all calls into those callbacks can determine which "set"
|
such that all calls into those callbacks can determine which "set"
|
||||||
of those calls they belong to.
|
of those calls they belong to.
|
||||||
|
|
||||||
If this object is being used in the context of an aggregate or
|
<p>If the argument is true and the aggregate context has not yet
|
||||||
|
been set up, it will be initialized fetched on demand, else it
|
||||||
|
won't. The intent is that xStep(), xValue(), and xInverse()
|
||||||
|
methods pass true and xFinal() methods pass false.
|
||||||
|
|
||||||
|
<p>This function treats numeric 0 as null, always returning null instead
|
||||||
|
of 0.
|
||||||
|
|
||||||
|
<p>If this object is being used in the context of an aggregate or
|
||||||
window UDF, this function returns a non-0 value which is distinct
|
window UDF, this function returns a non-0 value which is distinct
|
||||||
for each set of UDF callbacks from a single invocation of the
|
for each set of UDF callbacks from a single invocation of the
|
||||||
UDF, otherwise it returns 0. The returned value is only only
|
UDF, otherwise it returns 0. The returned value is only only
|
||||||
valid within the context of execution of a single SQL statement,
|
valid within the context of execution of a single SQL statement,
|
||||||
and may be re-used by future invocations of the UDF in different
|
and must not be re-used by future invocations of the UDF in
|
||||||
SQL statements.
|
different SQL statements.
|
||||||
|
|
||||||
Consider this SQL, where MYFUNC is a user-defined aggregate function:
|
<p>Consider this SQL, where MYFUNC is a user-defined aggregate function:
|
||||||
|
|
||||||
|
{code
|
||||||
SELECT MYFUNC(A), MYFUNC(B) FROM T;
|
SELECT MYFUNC(A), MYFUNC(B) FROM T;
|
||||||
|
}
|
||||||
|
|
||||||
The xStep() and xFinal() methods of the callback need to be able
|
<p>The xStep() and xFinal() methods of the callback need to be able
|
||||||
to differentiate between those two invocations in order to
|
to differentiate between those two invocations in order to
|
||||||
perform their work properly. The value returned by
|
perform their work properly. The value returned by
|
||||||
getAggregateContext() will be distinct for each of those
|
getAggregateContext() will be distinct for each of those
|
||||||
@@ -52,14 +59,18 @@ public final class sqlite3_context extends NativePointerHolder<sqlite3_context>
|
|||||||
key for mapping callback invocations to whatever client-defined
|
key for mapping callback invocations to whatever client-defined
|
||||||
state is needed by the UDF.
|
state is needed by the UDF.
|
||||||
|
|
||||||
There is one case where this will return 0 in the context of an
|
<p>There is one case where this will return 0 in the context of an
|
||||||
aggregate or window function: if the result set has no rows,
|
aggregate or window function: if the result set has no rows,
|
||||||
the UDF's xFinal() will be called without any other x...() members
|
the UDF's xFinal() will be called without any other x...() members
|
||||||
having been called. In that one case, no aggregate context key will
|
having been called. In that one case, no aggregate context key will
|
||||||
have been generated. xFinal() implementations need to be prepared to
|
have been generated. xFinal() implementations need to be prepared to
|
||||||
accept that condition as legal.
|
accept that condition as legal.
|
||||||
*/
|
*/
|
||||||
public long getAggregateContext(){
|
public synchronized Long getAggregateContext(boolean initIfNeeded){
|
||||||
return aggregateContext;
|
if( aggregateContext==null ){
|
||||||
|
aggregateContext = SQLite3Jni.sqlite3_aggregate_context(this, initIfNeeded);
|
||||||
|
if( !initIfNeeded && null==aggregateContext ) aggregateContext = 0L;
|
||||||
|
}
|
||||||
|
return (null==aggregateContext || 0!=aggregateContext) ? aggregateContext : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
manifest
26
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Add\sdoc/testrunner.md,\sfor\sdocumenting\sthe\stestrunner.tcl\sscript.
|
C Do\snot\spre-allocate\ssqlite3_aggregate_context()\sfor\sJava\sUDFs,\sas\sit\sunduly\scomplicates\sUDF\sinitialization.
|
||||||
D 2023-08-24T19:08:50.024
|
D 2023-08-24T21:31:56.676
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@@ -233,11 +233,11 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
|
|||||||
F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
|
F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
|
||||||
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
|
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
|
||||||
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
|
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
|
||||||
F ext/jni/GNUmakefile 6b3c0fd8d055c129735702d0b288589d25544dd404a00d46d9eb43770fe7f78f
|
F ext/jni/GNUmakefile 6aeafa0ebcf0f0d834c814ae8b450b54135ea11a2a7868f90b6286ec1bf6020f
|
||||||
F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35
|
F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35
|
||||||
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
|
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
|
||||||
F ext/jni/src/c/sqlite3-jni.c 8db2fcc05dd7749f9f4175e2654344feccac6abfd19fd9db0d116c6350e3b625
|
F ext/jni/src/c/sqlite3-jni.c 0d98ab3b117893904a06f0f8a350d68d4e911939b6aee4f0eb1ef707502ac23c
|
||||||
F ext/jni/src/c/sqlite3-jni.h 2b81cfb83933cb18e5f690487f4556591d3329538809c847d00190aa4d69aa1d
|
F ext/jni/src/c/sqlite3-jni.h 7e9f36434b919cd8b6aa66c61e3910e9f112e252f52d1ac8a9811c52710aefcb
|
||||||
F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892
|
F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892
|
||||||
F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6
|
F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6
|
||||||
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
|
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
|
||||||
@@ -256,10 +256,10 @@ F ext/jni/src/org/sqlite/jni/PreUpdateHook.java dec00a706b58c67989f0ff56c4f0a703
|
|||||||
F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495380677e87daa29a1c57a0e2c06b0a131dc
|
F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495380677e87daa29a1c57a0e2c06b0a131dc
|
||||||
F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86
|
F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86
|
||||||
F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564
|
F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564
|
||||||
F ext/jni/src/org/sqlite/jni/SQLFunction.java 5851698d96ee29171d68930ad758d0f5a253f7575f1feb890d82b2557a8d3ef5
|
F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb
|
||||||
F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798
|
F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798
|
||||||
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java d96f10a097c1d614b44353e85a65368d9aca565d5ae57fae0104811594fbdfba
|
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 866b320e6cb089ebe96c14763575df9e2e0d5e52a0ec87a52119ff17ec59ffbf
|
||||||
F ext/jni/src/org/sqlite/jni/Tester1.java 76f308ad9bf0bd74374561c30c65564ed24583a465264b751d9e2333980149f1
|
F ext/jni/src/org/sqlite/jni/Tester1.java 239eb0133547d4a23317a6dd5bc456172cfb1f2547fd15ba8408871c2776a721
|
||||||
F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
|
F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
|
||||||
F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d
|
F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d
|
||||||
F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
|
F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
|
||||||
@@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd9603
|
|||||||
F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b
|
F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b
|
||||||
F ext/jni/src/org/sqlite/jni/package-info.java 1a547913d681411d65c5fe0bca840f049abe5612740154a125545ea9e2481747
|
F ext/jni/src/org/sqlite/jni/package-info.java 1a547913d681411d65c5fe0bca840f049abe5612740154a125545ea9e2481747
|
||||||
F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc
|
F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc
|
||||||
F ext/jni/src/org/sqlite/jni/sqlite3_context.java fe7797a696978f057528a57b7a11e7797ed41fd7afcf100c5ebb67055d9f706f
|
F ext/jni/src/org/sqlite/jni/sqlite3_context.java 42df6769ab9c8de40d24f39f49152cdaa6e0c06d1468660a95dfee9ecaeb9a63
|
||||||
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc
|
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc
|
||||||
F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a
|
F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a
|
||||||
F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bc3d6797a2f6cb7d443a0b72af84e5a45e0416b96af52e432d28e123db1970c3
|
F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bc3d6797a2f6cb7d443a0b72af84e5a45e0416b96af52e432d28e123db1970c3
|
||||||
@@ -2096,8 +2096,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 62b404d62fd62f4d220838b59c9f38a71afa2d4a8c3af0a5c9495fa7020972cf
|
P 9c69a28401c7273823f2c2b291fd417febeb278afb9ce085a4b944505ca13d23
|
||||||
R a33be9d18a7bfec02bf745dca74a3ae4
|
R 1c78bd3d04007fccae2264dff957535e
|
||||||
U dan
|
U stephan
|
||||||
Z b231c489ac6e506e7affd58c17184d19
|
Z 6a74bde3490bcf9bf06f9dd5ecb09697
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@@ -1 +1 @@
|
|||||||
9c69a28401c7273823f2c2b291fd417febeb278afb9ce085a4b944505ca13d23
|
e8308f0c6ec2d8999c8a2502fb130cb3501ba326f23f71f2cd8d452debae79b5
|
Reference in New Issue
Block a user