jmethodID midSet /* setNativePointer() */;
jmethodID midGet /* getNativePointer() */;
jmethodID midCtor /* constructor */;
- jmethodID midSetAgg /* sqlite3_context::setAggregateContext() */;
+ jfieldID fidSetAgg /* sqlite3_context::aggregateContext */;
};
typedef struct JNIEnvCacheLine JNIEnvCacheLine;
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
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;
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<>();
/**
}
private static void testBusy(){
- outln("testBusy()...");
final String dbName = "_busy-handler.db";
final sqlite3 db1 = new sqlite3();
final sqlite3 db2 = new sqlite3();
/**
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().
+ For use only by the JNI layer. It's permitted to set this even
+ though it's private.
*/
- public long getAggregateContext(){
- return aggcx;
- }
+ private long aggregateContext = 0;
/**
- For use only by the JNI layer. It's permitted to call this even
- though it's private.
+ 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.
*/
- private void setAggregateContext(long n){
- aggcx = n;
+ public long getAggregateContext(){
+ return aggregateContext;
}
}
-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
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
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.
-ff53f1ccdc1780f2d9bd5f59804a76dbdf4f6b70696d3a7dbdbd96d1f8f6fa5c
\ No newline at end of file
+7af0cb998f7161296d5e5e50a42e9db26ec13c145c61194a999a1a0104818d45
\ No newline at end of file