From: stephan Date: Fri, 28 Jul 2023 09:25:05 +0000 (+0000) Subject: More Java docs about making use of the aggregate context. Change the JNI mapping... X-Git-Tag: version-3.43.0~47^2~142 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=09c2640fe3153f1855268596479ba80f4c851d53;p=thirdparty%2Fsqlite.git 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 --- diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 6303d87553..42c46b755b 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -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; diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index eaa83df9a9..396602b959 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -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 { - private java.util.Map> map + private final java.util.Map> map = new java.util.HashMap<>(); /** diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index edf0eeaafa..77495dad84 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -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(); diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_context.java b/ext/jni/src/org/sqlite/jni/sqlite3_context.java index bf2224dd5e..3272465299 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_context.java +++ b/ext/jni/src/org/sqlite/jni/sqlite3_context.java @@ -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 { 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; } } diff --git a/manifest b/manifest index 6237512571..324f25d9f9 100644 --- a/manifest +++ b/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. diff --git a/manifest.uuid b/manifest.uuid index 28aa247cb8..b0a799140d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ff53f1ccdc1780f2d9bd5f59804a76dbdf4f6b70696d3a7dbdbd96d1f8f6fa5c \ No newline at end of file +7af0cb998f7161296d5e5e50a42e9db26ec13c145c61194a999a1a0104818d45 \ No newline at end of file