]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
More Java docs about making use of the aggregate context. Change the JNI mapping...
authorstephan <stephan@noemail.net>
Fri, 28 Jul 2023 09:25:05 +0000 (09:25 +0000)
committerstephan <stephan@noemail.net>
Fri, 28 Jul 2023 09:25:05 +0000 (09:25 +0000)
FossilOrigin-Name: 7af0cb998f7161296d5e5e50a42e9db26ec13c145c61194a999a1a0104818d45

ext/jni/src/c/sqlite3-jni.c
ext/jni/src/org/sqlite/jni/SQLFunction.java
ext/jni/src/org/sqlite/jni/Tester1.java
ext/jni/src/org/sqlite/jni/sqlite3_context.java
manifest
manifest.uuid

index 6303d87553ee071e1eeaf302ea74b557dfb920c9..42c46b755b61ef4550e87c9e7bdc0b167099be70 100644 (file)
@@ -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;
index eaa83df9a96335986b8b6714b617d8d1f5bf32c7..396602b95908cb816bf6be358473cee2b0ce96e9 100644 (file)
@@ -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<>();
 
     /**
index edf0eeaafa3c8a4590de7d33bdc0cefcd7a9f6a9..77495dad8411b5de00d0322ab49a1f8ca995ddc2 100644 (file)
@@ -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();
index bf2224dd5e9287fbf68b359bce060ceecfba5baf..32724652997f1a8630b743f6f67f12b1b63c8f35 100644 (file)
@@ -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().
+     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;
   }
 }
index 62375125713cbbe7d8882773fb17bf3560fb4fcf..324f25d9f92abfccc6b2be750cbf9ddf9027f189 100644 (file)
--- 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.
index 28aa247cb8506a8f7653596c495800b14c91496e..b0a799140d1006c385777ca5d29441cdd0a28488 100644 (file)
@@ -1 +1 @@
-ff53f1ccdc1780f2d9bd5f59804a76dbdf4f6b70696d3a7dbdbd96d1f8f6fa5c
\ No newline at end of file
+7af0cb998f7161296d5e5e50a42e9db26ec13c145c61194a999a1a0104818d45
\ No newline at end of file