]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
JNI: add aggregate function support to the wrapper1 API.
authorstephan <stephan@noemail.net>
Mon, 16 Oct 2023 16:04:23 +0000 (16:04 +0000)
committerstephan <stephan@noemail.net>
Mon, 16 Oct 2023 16:04:23 +0000 (16:04 +0000)
FossilOrigin-Name: 15b28b340a5c5efdbfe3fbed16ee0b699561edaeebb77446addf2374bdf9357e

ext/jni/GNUmakefile
ext/jni/src/org/sqlite/jni/capi/Tester1.java
ext/jni/src/org/sqlite/jni/capi/ValueHolder.java [new file with mode: 0644]
ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java [new file with mode: 0644]
ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java
ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java
ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java
ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java [new file with mode: 0644]
manifest
manifest.uuid

index e7f083a75643ec22663cecc999bddd89d730c2ff..19a5080471ffb1255c16fb27f94f533b63d2c3d8 100644 (file)
@@ -106,6 +106,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
   TableColumnMetadata.java \
   TraceV2Callback.java \
   UpdateHookCallback.java \
+  ValueHolder.java \
   WindowFunction.java \
   XDestroyCallback.java \
   sqlite3.java \
@@ -113,9 +114,12 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
   sqlite3_stmt.java \
   sqlite3_value.java \
 ) $(patsubst %,$(dir.src.jni)/wrapper1/%,\
+  AggregateFunction.java \
+  ScalarFunction.java \
   SqlFunction.java \
   Sqlite.java \
   SqliteException.java \
+  ValueHolder.java \
 )
 
 JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
index 5c982ea5ed6f12717c7ec4911d9dc6f8e9c7ed11..6fb28e65b965c43de7cec7f8a29ea78ad5296cdb 100644 (file)
@@ -38,17 +38,6 @@ import java.util.concurrent.Future;
 @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
 @interface SingleThreadOnly{}
 
-/**
-   A helper class which simply holds a single value. Its current use
-   is for communicating values out of anonymous classes, as doing so
-   requires a "final" reference.
-*/
-class ValueHolder<T> {
-  public T value;
-  public ValueHolder(){}
-  public ValueHolder(T v){value = v;}
-}
-
 public class Tester1 implements Runnable {
   //! True when running in multi-threaded mode.
   private static boolean mtMode = false;
diff --git a/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java b/ext/jni/src/org/sqlite/jni/capi/ValueHolder.java
new file mode 100644 (file)
index 0000000..b3f03ac
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+** 2023-10-16
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains a set of tests for the sqlite3 JNI bindings.
+*/
+package org.sqlite.jni.capi;
+
+/**
+   A helper class which simply holds a single value. Its primary use
+   is for communicating values out of anonymous classes, as doing so
+   requires a "final" reference.
+*/
+public class ValueHolder<T> {
+  public T value;
+  public ValueHolder(){}
+  public ValueHolder(T v){value = v;}
+}
diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java
new file mode 100644 (file)
index 0000000..173d775
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+** 2023-10-16
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file is part of the wrapper1 interface for sqlite3.
+*/
+package org.sqlite.jni.wrapper1;
+import org.sqlite.jni.capi.CApi;
+import org.sqlite.jni.annotation.*;
+import org.sqlite.jni.capi.sqlite3_context;
+import org.sqlite.jni.capi.sqlite3_value;
+
+/**
+   EXPERIMENTAL/INCOMPLETE/UNTESTED
+
+   A SqlFunction implementation for aggregate functions. The T type
+   represents the type of data accumulated by this aggregate while it
+   works. e.g. a SUM()-like UDF might use Integer or Long and a
+   CONCAT()-like UDF might use a StringBuilder or a List<String>.
+*/
+public abstract class AggregateFunction<T> implements SqlFunction  {
+
+  /**
+     As for the xStep() argument of the C API's
+     sqlite3_create_function().  If this function throws, the
+     exception is reported via sqlite3_result_error().
+  */
+  public abstract void xStep(SqlFunction.Arguments args);
+
+  /**
+     As for the xFinal() argument of the C API's
+     sqlite3_create_function(). If this function throws, it is
+     translated into sqlite3_result_error().
+
+     Note that the passed-in object will not actually contain any
+     arguments for xFinal() but will contain the context object needed
+     for setting the call's result or error state.
+  */
+  public abstract void xFinal(SqlFunction.Arguments args);
+
+  /**
+     Optionally override to be notified when the UDF is finalized by
+     SQLite.
+  */
+  public void xDestroy() {}
+
+  /** Per-invocation state for the UDF. */
+  private final SqlFunction.PerContextState<T> map =
+    new SqlFunction.PerContextState<>();
+
+  /**
+     To be called from the implementation's xStep() method, as well
+     as the xValue() and xInverse() methods of the {@link WindowFunction}
+     subclass, to fetch the current per-call UDF state. On the
+     first call to this method for any given sqlite3_context
+     argument, the context is set to the given initial value. On all other
+     calls, the 2nd argument is ignored.
+
+     @see SQLFunction.PerContextState#getAggregateState
+  */
+  protected final ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
+    return map.getAggregateState(args, initialValue);
+  }
+
+  /**
+     To be called from the implementation's xFinal() method to fetch
+     the final state of the UDF and remove its mapping.
+
+     see SQLFunction.PerContextState#takeAggregateState
+  */
+  protected final T takeAggregateState(SqlFunction.Arguments args){
+    return map.takeAggregateState(args);
+  }
+
+}
index 6851bf83793f0f6674ce0fe05e791705f55596a6..d6acda5aa5e3e75fbb3b984925511583cf5eb391 100644 (file)
 */
 package org.sqlite.jni.wrapper1;
 import org.sqlite.jni.capi.CApi;
-import org.sqlite.jni.annotation.*;
 import org.sqlite.jni.capi.sqlite3_context;
 import org.sqlite.jni.capi.sqlite3_value;
 
 /**
-   EXPERIMENTAL/INCOMPLETE/UNTESTED
+   Base marker interface for SQLite's three types of User-Defined SQL
+   Functions (UDFs): Scalar, Aggregate, and Window functions.
 */
 public interface SqlFunction  {
 
   /**
-     EXPERIMENTAL/INCOMPLETE/UNTESTED. An attempt at hiding UDF-side
-     uses of the sqlite3_context and sqlite3_value classes from a
-     high-level wrapper.  This level of indirection requires more than
-     twice as much Java code (in this API, not client-side) as using
-     the lower-level API. Client-side it's roughly the same amount of
-     code.
+     The Arguments type is an abstraction on top of the lower-level
+     UDF function argument types. It provides _most_ of the functionality
+     of the lower-level interface, insofar as possible without "leaking"
+     those types into this API.
   */
   public final static class Arguments implements Iterable<SqlFunction.Arguments.Arg>{
     private final sqlite3_context cx;
@@ -37,29 +35,34 @@ public interface SqlFunction  {
 
     /**
        Must be passed the context and arguments for the UDF call this
-       object is wrapping.
+       object is wrapping. Intended to be used by internal proxy
+       classes which "convert" the lower-level interface into this
+       package's higher-level interface, e.g. ScalarAdapter and
+       AggregateAdapter.
+
+       Passing null for the args is equivalent to passing a length-0
+       array.
     */
-    Arguments(@NotNull sqlite3_context cx, @NotNull sqlite3_value args[]){
+    Arguments(sqlite3_context cx, sqlite3_value args[]){
       this.cx = cx;
-      this.args = args;
-      this.length = args.length;
+      this.args = args==null ? new sqlite3_value[0] : args;;
+      this.length = this.args.length;
     }
 
     /**
        Wrapper for a single SqlFunction argument. Primarily intended
-       for eventual use with the Arguments class's Iterable interface.
+       for use with the Arguments class's Iterable interface.
     */
     public final static class Arg {
       private final Arguments a;
       private final int ndx;
       /* Only for use by the Arguments class. */
-      private Arg(@NotNull Arguments a, int ndx){
+      private Arg(Arguments a, int ndx){
         this.a = a;
         this.ndx = ndx;
       }
       /** Returns this argument's index in its parent argument list. */
       public int getIndex(){return ndx;}
-
       public int getInt(){return a.getInt(ndx);}
       public long getInt64(){return a.getInt64(ndx);}
       public double getDouble(){return a.getDouble(ndx);}
@@ -75,10 +78,9 @@ public interface SqlFunction  {
       public void setAuxData(Object o){a.setAuxData(ndx, o);}
     }
 
-    //! Untested!
     @Override
     public java.util.Iterator<SqlFunction.Arguments.Arg> iterator(){
-      Arg[] proxies = new Arg[args.length];
+      final Arg[] proxies = new Arg[args.length];
       for( int i = 0; i < args.length; ++i ){
         proxies[i] = new Arg(this, i);
       }
@@ -98,6 +100,8 @@ public interface SqlFunction  {
       return args[ndx];
     }
 
+    sqlite3_context getContext(){return cx;}
+
     public int getArgCount(){ return args.length; }
 
     public int getInt(int arg){return CApi.sqlite3_value_int(valueAt(arg));}
@@ -159,6 +163,73 @@ public interface SqlFunction  {
     }
   }
 
+  /**
+     PerContextState assists aggregate and window functions in
+     managing their accumulator state across calls to the UDF's
+     callbacks.
+
+     <p>T must be of a type which can be legally stored as a value in
+     java.util.HashMap<KeyType,T>.
+
+     <p>If a given aggregate or window function is called multiple times
+     in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
+     then the clients need some way of knowing which call is which so
+     that they can map their state between their various UDF callbacks
+     and reset it via xFinal(). This class takes care of such
+     mappings.
+
+     <p>This class works by mapping
+     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.
+
+     <p>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. The provided {@link AggregateFunction} and {@link
+     WindowFunction} classes use this.
+  */
+  public static final class PerContextState<T> {
+    private final java.util.Map<Long,ValueHolder<T>> map
+      = new java.util.HashMap<>();
+
+    /**
+       Should be called from a UDF's xStep(), xValue(), and xInverse()
+       methods, passing it that method's first argument and an initial
+       value for the persistent state. If there is currently no
+       mapping for the given context within the map, one is created
+       using the given initial value, else the existing one is used
+       and the 2nd argument is ignored.  It returns a ValueHolder<T>
+       which can be used to modify that state directly without
+       requiring that the client update the underlying map's entry.
+
+       <p>The caller is obligated to eventually call
+       takeAggregateState() to clear the mapping.
+    */
+    public ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
+      final Long key = args.getContext().getAggregateContext(true);
+      ValueHolder<T> rc = null==key ? null : map.get(key);
+      if( null==rc ){
+        map.put(key, rc = new ValueHolder<>(initialValue));
+      }
+      return rc;
+    }
+
+    /**
+       Should be called from a UDF's xFinal() method and passed that
+       method's first argument. This function removes the value
+       associated with with the arguments' aggregate context from the
+       map and returns it, returning null if no other UDF method has
+       been called to set up such a mapping. The latter condition will
+       be the case if a UDF is used in a statement which has no result
+       rows.
+    */
+    public T takeAggregateState(SqlFunction.Arguments args){
+      final ValueHolder<T> h = map.remove(args.getContext().getAggregateContext(false));
+      return null==h ? null : h.value;
+    }
+  }
+
   /**
      Internal-use adapter for wrapping this package's ScalarFunction
      for use with the org.sqlite.jni.capi.ScalarFunction interface.
@@ -169,11 +240,57 @@ public interface SqlFunction  {
       this.impl = impl;
     }
     /**
-       Proxies this.f.xFunc(), adapting the call arguments to that
-       function's signature.
+       Proxies this.impl.xFunc(), adapting the call arguments to that
+       function's signature. If the proxy throws, it's translated to
+       sqlite_result_error() with the exception's message.
     */
     public void xFunc(sqlite3_context cx, sqlite3_value[] args){
-      impl.xFunc( new SqlFunction.Arguments(cx, args) );
+      try{
+        impl.xFunc( new SqlFunction.Arguments(cx, args) );
+      }catch(Exception e){
+        CApi.sqlite3_result_error(cx, e);
+      }
+    }
+
+    public void xDestroy(){
+      impl.xDestroy();
+    }
+  }
+
+  /**
+     Internal-use adapter for wrapping this package's AggregateFunction
+     for use with the org.sqlite.jni.capi.AggregateFunction interface.
+  */
+  static final class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction {
+    final AggregateFunction impl;
+    AggregateAdapter(AggregateFunction impl){
+      this.impl = impl;
+    }
+
+    /**
+       Proxies this.impl.xStep(), adapting the call arguments to that
+       function's signature. If the proxied function throws, it is
+       translated to sqlite_result_error() with the exception's
+       message.
+    */
+    public void xStep(sqlite3_context cx, sqlite3_value[] args){
+      try{
+        impl.xStep( new SqlFunction.Arguments(cx, args) );
+      }catch(Exception e){
+        CApi.sqlite3_result_error(cx, e);
+      }
+    }
+
+    /**
+       As for the xFinal() argument of the C API's sqlite3_create_function().
+       If the proxied function throws, it is translated into a sqlite3_result_error().
+    */
+    public void xFinal(sqlite3_context cx){
+      try{
+        impl.xFinal( new SqlFunction.Arguments(cx, null) );
+      }catch(Exception e){
+        CApi.sqlite3_result_error(cx, e);
+      }
     }
 
     public void xDestroy(){
index 7321d7bb36a27b1f3a0411870901d6b8df503f34..bcf97b23946abff5ce7095c44bfac2fac015aed7 100644 (file)
@@ -195,7 +195,6 @@ public final class Sqlite implements AutoCloseable  {
     return prepare(sql, 0);
   }
 
-
   public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){
     int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
                                            new SqlFunction.ScalarAdapter(f));
@@ -206,4 +205,14 @@ public final class Sqlite implements AutoCloseable  {
     this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
   }
 
+  public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f ){
+    int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
+                                           new SqlFunction.AggregateAdapter(f));
+    if( 0!=rc ) throw new SqliteException(db);
+  }
+
+  public void createFunction(String name, int nArg, AggregateFunction f){
+    this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
+  }
+
 }
index 07a43b63dd01feda329a25fea584f55abd3ba651..f5fd5f84e60e6b01680a539dc89ea3daabf9633c 100644 (file)
@@ -38,17 +38,6 @@ import org.sqlite.jni.capi.*;
 @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
 @interface SingleThreadOnly{}
 
-/**
-   A helper class which simply holds a single value. Its current use
-   is for communicating values out of anonymous classes, as doing so
-   requires a "final" reference.
-*/
-class ValueHolder<T> {
-  public T value;
-  public ValueHolder(){}
-  public ValueHolder(T v){value = v;}
-}
-
 public class Tester2 implements Runnable {
   //! True when running in multi-threaded mode.
   private static boolean mtMode = false;
@@ -279,6 +268,36 @@ public class Tester2 implements Runnable {
     affirm( 1 == xDestroyCalled.value );
   }
 
+  void testUdfAggregate(){
+    final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0);
+    final ValueHolder<Integer> vh = new ValueHolder<>(0);
+    try (Sqlite db = openDb()) {
+      execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)");
+      final AggregateFunction f = new AggregateFunction<Integer>(){
+          public void xStep(SqlFunction.Arguments args){
+            final ValueHolder<Integer> agg = this.getAggregateState(args, 0);
+            for( SqlFunction.Arguments.Arg arg : args ){
+              agg.value += arg.getInt();
+            }
+          }
+          public void xFinal(SqlFunction.Arguments args){
+            final Integer v = this.takeAggregateState(args);
+            if( null==v ) args.resultNull();
+            else args.resultInt(v);
+            vh.value = v;
+          }
+          public void xDestroy(){
+            ++xDestroyCalled.value;
+          }
+        };
+      db.createFunction("myagg", -1, f);
+      execSql(db, "select myagg(a) from t");
+      affirm( 6 == vh.value );
+      affirm( 0 == xDestroyCalled.value );
+    }
+    affirm( 1 == xDestroyCalled.value );
+  }
+
   private void runTests(boolean fromThread) throws Exception {
     List<java.lang.reflect.Method> mlist = testMethods;
     affirm( null!=mlist );
diff --git a/ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java b/ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java
new file mode 100644 (file)
index 0000000..009936a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+** 2023-10-16
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains a set of tests for the sqlite3 JNI bindings.
+*/
+package org.sqlite.jni.wrapper1;
+
+/**
+   A helper class which simply holds a single value. Its primary use
+   is for communicating values out of anonymous classes, as doing so
+   requires a "final" reference.
+*/
+public class ValueHolder<T> {
+  public T value;
+  public ValueHolder(){}
+  public ValueHolder(T v){value = v;}
+}
index 1fb9230659f8b4e7a67196caef77803d07daadb4..ed6c5f7f8a9245f9210c750e9cd6526eaf5f6da5 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C JNI:\sadd\sscalar\sUDF\ssupport\sto\sthe\swrapper1\sAPI.
-D 2023-10-16T14:31:13.824
+C JNI:\sadd\saggregate\sfunction\ssupport\sto\sthe\swrapper1\sAPI.
+D 2023-10-16T16:04:23.203
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -235,7 +235,7 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
 F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
-F ext/jni/GNUmakefile 069399d471af948a4293e79135907a8d58daa09e59b4cc1b9cc1a5124c87f589
+F ext/jni/GNUmakefile 5c3ac326bf3853486ebe0d70819abc790cc65c412182ce4ebd5012b008d9b059
 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4
 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
 F ext/jni/src/c/sqlite3-jni.c 8d32ca0598a11370a9e92a6d111f38934c225056b42b13512175acf6e37eed4c
@@ -266,9 +266,10 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java fef556adbc3624292423083a648bd
 F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1
 F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615
 F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f
-F ext/jni/src/org/sqlite/jni/capi/Tester1.java 8aacea90b0eed6e4e801cfba2515a66b5d602e124f1ba68fe3d2f0aa98f0f443
+F ext/jni/src/org/sqlite/jni/capi/Tester1.java ca195521b6bda3e0cd00e76bb71ec8060d1fab76a2f13b1af9feea40789f44bb
 F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723
 F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4
+F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 9f9e151f1da017b706c0ee5f40f4c86b54e773d6ae4339723e0cc85a456251ab
 F ext/jni/src/org/sqlite/jni/capi/WindowFunction.java caf4396f91b2567904cf94bc538a069fd62260d975bd037d15a02a890ed1ef9e
 F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba909437090caf68200f06717b8a7d6aa96fa3e8133117d
 F ext/jni/src/org/sqlite/jni/capi/package-info.java 08ff986a65d2be9162442c82d28a65ce431d826f188520717c2ecb1484d0a50e
@@ -289,10 +290,12 @@ F ext/jni/src/org/sqlite/jni/fts5/fts5_api.java a8e88c3783d21cec51b0748568a96653
 F ext/jni/src/org/sqlite/jni/fts5/fts5_extension_function.java 9e2b954d210d572552b28aca523b272fae14bd41e318921b22f65b728d5bf978
 F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java 92bdaa3893bd684533004d64ade23d329843f809cd0d0f4f1a2856da6e6b4d90
 F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e
-F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 8b422ec8a2e922c1c21db549e68e0eb93078d2c4d341354043975e111a43b10d
-F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 7826de9bea3102d8a2ecaef3cc84480d8d6f6bc617c531d2078b419913c866fd
+F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java 5ad99bd74c85f56bbef324d9ec29b4048f4620547c9a80093d8586c3557f9f9a
+F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 004394eeb944baa56e36cd7ae69ba6d4a52b52db3c49439db16e98270b861421
+F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java a9ddc6a9e8c113168cc67592ae24c0e56d30dd06226eeab012f2761a0889d7bb
 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 1386f7b753134fc12253ce2fbbc448ba8c970567fac01a3356cb672e14408d73
-F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 3dccdb259bd1d737c6d104bdf488fb489063b40a113c03b311284e0287d0d5b7
+F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java c24b510ebe801c30533cc62efdf69a4a5e2da9ec4b49f8d403f2060693f060a0
+F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af
 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745
 F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70
 F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0
@@ -2129,8 +2132,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 43b10a5cf9cb8be53d62914f340d533e60a70bf4caa8b9b91c0f867fa0f70493
-R b476ec63940a46ee7b1e2c0e07bbcf7b
+P a850535766d2243d9475e1523c753615875a2da9c9d82a41a9fb61b141c6334a
+R 41c1c0a2430694da022548afb899150c
 U stephan
-Z a39099e216c3c59927f5c5a18a7e93ef
+Z b40ce65d6c09198a38c3804feb9178c7
 # Remove this line to create a well-formed Fossil manifest.
index 0ec7f3375d42053fe61028327768eab1647b8990..0973dce9e0c5a024c5044376dcba1a11f555a0da 100644 (file)
@@ -1 +1 @@
-a850535766d2243d9475e1523c753615875a2da9c9d82a41a9fb61b141c6334a
\ No newline at end of file
+15b28b340a5c5efdbfe3fbed16ee0b699561edaeebb77446addf2374bdf9357e
\ No newline at end of file