More Java docs about making use of the aggregate context. Change the JNI mapping to set the sqlite3_context::aggregateContext member directly, instead of via a superflous setter, because that way is faster.
FossilOrigin-Name: 7af0cb998f7161296d5e5e50a42e9db26ec13c145c61194a999a1a0104818d45
This commit is contained in:
parent
75d3b1b5a2
commit
09c2640fe3
@ -275,7 +275,7 @@ struct NphCacheLine {
|
||||
jmethodID midSet /* setNativePointer() */;
|
||||
jmethodID midGet /* getNativePointer() */;
|
||||
jmethodID midCtor /* constructor */;
|
||||
jmethodID midSetAgg /* sqlite3_context::setAggregateContext() */;
|
||||
jfieldID fidSetAgg /* sqlite3_context::aggregateContext */;
|
||||
};
|
||||
|
||||
typedef struct JNIEnvCacheLine JNIEnvCacheLine;
|
||||
@ -745,11 +745,14 @@ static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassNam
|
||||
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->setAggregateContext(). The memory is only used as a key for
|
||||
mapping, client-side, results of aggregate result sets across
|
||||
xStep() and xFinal() methods.
|
||||
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.
|
||||
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
|
||||
@ -759,35 +762,34 @@ static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassNam
|
||||
static int udf_setAggregateContext(JNIEnv * env, jobject jCx,
|
||||
sqlite3_context * pCx,
|
||||
int isFinal){
|
||||
jmethodID setter;
|
||||
jfieldID member;
|
||||
void * pAgg;
|
||||
int rc = 0;
|
||||
struct NphCacheLine * const cacheLine =
|
||||
S3Global_nph_cache(env, ClassNames.sqlite3_context);
|
||||
if(cacheLine && cacheLine->klazz && cacheLine->midSetAgg){
|
||||
setter = cacheLine->midSetAgg;
|
||||
assert(setter);
|
||||
if(cacheLine && cacheLine->klazz && cacheLine->fidSetAgg){
|
||||
member = cacheLine->fidSetAgg;
|
||||
assert(member);
|
||||
}else{
|
||||
jclass const klazz =
|
||||
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, jCx);
|
||||
setter = (*env)->GetMethodID(env, klazz, "setAggregateContext", "(J)V");
|
||||
member = (*env)->GetFieldID(env, klazz, "aggregateContext", "J");
|
||||
if( !member ){
|
||||
IFTHREW{ EXCEPTION_REPORT; EXCEPTION_CLEAR; }
|
||||
return s3jni_db_error(sqlite3_context_db_handle(pCx),
|
||||
SQLITE_ERROR,
|
||||
"Internal error: cannot find "
|
||||
"sqlite3_context::aggregateContext field.");
|
||||
}
|
||||
if(cacheLine){
|
||||
assert(cacheLine->klazz);
|
||||
assert(!cacheLine->midSetAgg);
|
||||
cacheLine->midSetAgg = setter;
|
||||
assert(!cacheLine->fidSetAgg);
|
||||
cacheLine->fidSetAgg = member;
|
||||
}
|
||||
}
|
||||
pAgg = sqlite3_aggregate_context(pCx, isFinal ? 0 : 4);
|
||||
if( pAgg || isFinal ){
|
||||
(*env)->CallVoidMethod(env, jCx, setter, (jlong)pAgg);
|
||||
IFTHREW {
|
||||
EXCEPTION_REPORT;
|
||||
EXCEPTION_CLEAR/*arguable, but so is propagation*/;
|
||||
rc = s3jni_db_error(sqlite3_context_db_handle(pCx),
|
||||
SQLITE_ERROR,
|
||||
"sqlite3_context::setAggregateContext() "
|
||||
"unexpectedly threw.");
|
||||
}
|
||||
(*env)->SetLongField(env, jCx, member, (jlong)pAgg);
|
||||
}else{
|
||||
assert(!pAgg);
|
||||
rc = SQLITE_NOMEM;
|
||||
|
@ -35,11 +35,17 @@ public abstract class SQLFunction {
|
||||
such mappings.
|
||||
|
||||
This class works by mapping
|
||||
sqlite3_context::getAggregateContext() to a single piece of state
|
||||
which persists across a "matching set" of the UDF's callbacks.
|
||||
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.
|
||||
|
||||
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.
|
||||
*/
|
||||
public static final class ContextMap<T> {
|
||||
private java.util.Map<Long,ValueHolder<T>> map
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
|
@ -656,7 +656,6 @@ public class Tester1 {
|
||||
}
|
||||
|
||||
private static void testBusy(){
|
||||
outln("testBusy()...");
|
||||
final String dbName = "_busy-handler.db";
|
||||
final sqlite3 db1 = new sqlite3();
|
||||
final sqlite3 db2 = new sqlite3();
|
||||
|
@ -15,39 +15,56 @@ package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
sqlite3_context instances are used in conjunction with user-defined
|
||||
SQL functions (a.k.a. UDFs). They are opaque pointers.
|
||||
|
||||
The getAggregateContext() method corresponds to C's
|
||||
sqlite3_aggregate_context(), with a slightly different interface in
|
||||
order to account for cross-language differences. It serves the same
|
||||
purposes in a slightly different way: it provides a key which is
|
||||
stable across invocations of UDF xStep() and xFinal() pairs, to
|
||||
which a UDF may map state across such calls (e.g. a numeric result
|
||||
which is being accumulated).
|
||||
SQL functions (a.k.a. UDFs).
|
||||
*/
|
||||
public class sqlite3_context extends NativePointerHolder<sqlite3_context> {
|
||||
public sqlite3_context() {
|
||||
super();
|
||||
}
|
||||
private long aggcx = 0;
|
||||
|
||||
/**
|
||||
If this object is being used in the context of an aggregate or
|
||||
window UDF, the UDF binding layer will set a unique context value
|
||||
here, else this will return 0. That value will be the same across
|
||||
matching calls to the UDF callbacks. This value can be used as a
|
||||
key to map state which needs to persist across such calls, noting
|
||||
that such state should be cleaned up via xFinal().
|
||||
*/
|
||||
public long getAggregateContext(){
|
||||
return aggcx;
|
||||
}
|
||||
|
||||
/**
|
||||
For use only by the JNI layer. It's permitted to call this even
|
||||
For use only by the JNI layer. It's permitted to set this even
|
||||
though it's private.
|
||||
*/
|
||||
private void setAggregateContext(long n){
|
||||
aggcx = n;
|
||||
private long aggregateContext = 0;
|
||||
|
||||
/**
|
||||
getAggregateContext() corresponds to C's
|
||||
sqlite3_aggregate_context(), with a slightly different interface
|
||||
to account for cross-language differences. It serves the same
|
||||
purposes in a slightly different way: it provides a key which is
|
||||
stable across invocations of "matching sets" of a UDF's callbacks,
|
||||
such that all calls into those callbacks can determine which "set"
|
||||
of those calls they belong to.
|
||||
|
||||
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
|
||||
for each set of UDF callbacks from a single invocation of the
|
||||
UDF, otherwise it returns 0. The returned value is only only
|
||||
valid within the context of execution of a single SQL statement,
|
||||
and may be re-used by future invocations of the UDF in different
|
||||
SQL statements.
|
||||
|
||||
Consider this SQL, where MYFUNC is a user-defined aggregate function:
|
||||
|
||||
SELECT MYFUNC(A), MYFUNC(B) FROM T;
|
||||
|
||||
The xStep() and xFinal() methods of the callback need to be able
|
||||
to differentiate between those two invocations in order to
|
||||
perform their work properly. The value returned by
|
||||
getAggregateContext() will be distinct for each of those
|
||||
invocations of MYFUNC() and is intended to be used as a lookup
|
||||
key for mapping callback invocations to whatever client-defined
|
||||
state is needed by the UDF.
|
||||
|
||||
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,
|
||||
the UDF's xFinal() will be called without any other x...() members
|
||||
having been called. In that one case, no aggregate context key will
|
||||
have been generated. xFinal() implementations need to be prepared to
|
||||
accept that condition as legal.
|
||||
*/
|
||||
public long getAggregateContext(){
|
||||
return aggregateContext;
|
||||
}
|
||||
}
|
||||
|
18
manifest
18
manifest
@ -1,5 +1,5 @@
|
||||
C More\sdocs\sand\scleanups\srelated\sto\sthe\saggregate\sUDF\sstate.\sCorrect\sthe\sOOM\scheck\sto\sbehave\sproperly\sif\sxFinal()\sis\scalled\swithout\sa\smatching\sxStep(),\sxValue(),\sor\sxInverse().
|
||||
D 2023-07-28T01:51:14.668
|
||||
C More\sJava\sdocs\sabout\smaking\suse\sof\sthe\saggregate\scontext.\sChange\sthe\sJNI\smapping\sto\sset\sthe\ssqlite3_context::aggregateContext\smember\sdirectly,\sinstead\sof\svia\sa\ssuperflous\ssetter,\sbecause\sthat\sway\sis\sfaster.
|
||||
D 2023-07-28T09:25:05.029
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -232,20 +232,20 @@ F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
|
||||
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
|
||||
F ext/jni/GNUmakefile 56a014dbff9516774d895ec1ae9df0ed442765b556f79a0fc0b5bc438217200d
|
||||
F ext/jni/README.md 042762dbf047667783a5bd0aec303535140f302debfbd259c612edf856661623
|
||||
F ext/jni/src/c/sqlite3-jni.c 9464d7f186c52cecd4c6ac91d3da35f29fd98923a048befc8d2d872edd639a41
|
||||
F ext/jni/src/c/sqlite3-jni.c 9d0d58f3633bd8f467f893f45548873ed2c5451c673b0782b3cc6bfa92327b10
|
||||
F ext/jni/src/c/sqlite3-jni.h c9bb150a38dce09cc2794d5aac8fa097288d9946fbb15250fd0a23c31957f506
|
||||
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
|
||||
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
|
||||
F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 70dc7bc41f80352ff3d4331e2e24f45fcd23353b3641e2f68a81bd8262215861
|
||||
F ext/jni/src/org/sqlite/jni/OutputPointer.java 08a752b58a33696c5eaf0eb9361a0966b188dec40f4a3613eb133123951f6c5f
|
||||
F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5a1d7b2607eb2ef596fcf4492a49d1b3a5bdea3af9918e11716831ffd2f02284
|
||||
F ext/jni/src/org/sqlite/jni/SQLFunction.java b176c46828a52084dd3a39e5084d0b0ce12dcaf2abe719a58f4d1d92733e1136
|
||||
F ext/jni/src/org/sqlite/jni/SQLFunction.java 268291ee7be1406b13a3b220df2eac59b9337473d5eb9fa40bd528eefb57252c
|
||||
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 3582b30c0fb1cb39e25b9069fe8c9e2fe4f2659f4d38437b610e46143e163610
|
||||
F ext/jni/src/org/sqlite/jni/Tester1.java 2334d1dd0efc22179654c586065c77d904830d736059b4049f9cd9e6832565bd
|
||||
F ext/jni/src/org/sqlite/jni/Tester1.java 7d8742eb6d6aba429171b2ba6136f4f17569a280676d846cbe319fa95a97ae4d
|
||||
F ext/jni/src/org/sqlite/jni/Tracer.java c2fe1eba4a76581b93b375a7b95ab1919e5ae60accfb06d6beb067b033e9bae1
|
||||
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
|
||||
F ext/jni/src/org/sqlite/jni/sqlite3.java c7d0500c7269882243aafb41425928d094b2fcbdbc2fd1caffc276871cd3fae3
|
||||
F ext/jni/src/org/sqlite/jni/sqlite3_context.java 4e7eebc8a5c85ecfbae3aa2c4ddb7f1ca861c218d3829d31afe16f6b11104213
|
||||
F ext/jni/src/org/sqlite/jni/sqlite3_context.java 841ac0384ec23e7d24ad9a928f8728b98bd3c4c3814d401200c6531786b9c241
|
||||
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 3193693440071998a66870544d1d2314f144bea397ce4c3f83ff225d587067a0
|
||||
F ext/jni/src/org/sqlite/jni/sqlite3_value.java f9d8c0766b1d1b290564cb35db8d37be54c42adc8df22ee77b8d39e3e93398cd
|
||||
F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9
|
||||
@ -2067,8 +2067,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 6b56e4d62b4945e52978d00aa8e2984faa731c92a7e002e81524fcfcf8ba0cce
|
||||
R 0fe909577d9a504bc5127b45fc11fe45
|
||||
P ff53f1ccdc1780f2d9bd5f59804a76dbdf4f6b70696d3a7dbdbd96d1f8f6fa5c
|
||||
R ce373f0682789ebfcc8ec45219320dda
|
||||
U stephan
|
||||
Z 8f663d2013372069850b9c169f30fdc3
|
||||
Z 02ac0e260dba2cb9492d0ae9287f8a5f
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
ff53f1ccdc1780f2d9bd5f59804a76dbdf4f6b70696d3a7dbdbd96d1f8f6fa5c
|
||||
7af0cb998f7161296d5e5e50a42e9db26ec13c145c61194a999a1a0104818d45
|
Loading…
Reference in New Issue
Block a user